I would use the power of Firebird Events with delphi application with TIBEvents component.
The problem is the firewall, not every time have the correct role and when I try to register the events the application stops responding and I must wait...
How can I do?
I also try to call register function in a separated thread but with the same result.
function RegisterEvents(data : Pointer) : Integer;
begin
with Form1 do begin
DBOspitiEvent.Registered := true;
end;
end; //<-- AFTER THIS, APPLICATION IS BLOCKED (for a while)
procedure TForm1.Button2Click(Sender: TObject);
var
ThreadId : Cardinal;
ThreadHandle : Integer;
begin
ThreadHandle := BeginThread(nil,0,#RegisterEvents,nil,0,ThreadId);
if ThreadHandle = 0
then ShowMessage('Error');
end;
For events, a client needs to establish a separate connection, and by default Firebird uses a random port for this. In combination with firewalls, this leads to problems because the port is - for example - not allowed.
You can configure Firebird to use a fixed port, by editing firebird.conf and setting the RemoteAuxPort to a fixed value (eg 3051), and restarting Firebird. You can then configure your firewall to allow this port.
See also How to configure events with firewall?
We had many problems with TIBEvent components since XP SP3 too.
Here is what works perfectly fine for us since than, even with newest Win10 updates:
We use UIB for receiving events. TUIBEvents component. (You may keep IBX for everything else... but UIB is better and faster and FB3.0 compatible.)
Do not execute any long-lasting code inside OnEvent procedure, but rather set multithread-safe variables to mark, what event you received (last time)
Deal with those variable inside each window locally. (For example: running a timer that checks and compares last-event-time with local last-refresh-time.)
If you are running SQL queries in a separate Thread, always create new database + transaction component, DO NOT USE the one from the main thread!
Open 3050 and 3051 TCP ports on Firewall!
We also add "fbserver.exe" file to Firewall exception and to Defender exception.
Set fixed ports in "firebird.conf" file: RemoteServicePort=3050 and RemoteAuxPort=3051
You may create a single FirstInstallScrip.bat file to do all these firewall changes and copies a pre-edited .conf file to FB's directory, overwriting the original one.
And YES, you can create these simple text files easily from Delphi and run it from there. Or notify the user if not ran yet. (You can read the original config file and compare those settings.)
#ECHO program in
#NETSH advfirewall firewall add rule name="FirebirdSQL szerver" program="%programfiles%\Firebird\Firebird_2_5\bin\fbserver.exe" profile=public,private,domain dir=in action=allow edge=yes description="FirebirdSQL Database engine"
#ECHO program out
#NETSH advfirewall firewall add rule name="FirebirdSQL szerver" program="%programfiles%\Firebird\Firebird_2_5\bin\fbserver.exe" profile=public,private,domain dir=out action=allow description="FirebirdSQL Database engine"
#ECHO ports in
#NETSH advfirewall firewall add rule name="FirebirdSQL portok" localport=3050-3051 protocol=tcp profile=public,private,domain dir=in action=allow edge=yes description="FirebirdSQL Database engine ports"
#ECHO ports out
#NETSH advfirewall firewall add rule name="FirebirdSQL portok" localport=3050-3051 protocol=tcp profile=public,private,domain dir=out action=allow description="FirebirdSQL Database engine ports"
#pause
As #Victoria suggested: use better business logic!
You may store your data (red from DB) in PC's memory and show it from there.
Create triggers inside your FB database self-updating the last modification.
Do not delete rows, just mark them as "deleted".
check only "what changed" SELECT * from "Customers" c where c.MODIFIED > '2019...' and compare with data already downloaded before, to reduce SQL load.
use separate, short-time transaction to write data into DB.
Related
I use windows 7, 64-bit SP1, and TP Async V4.07 and have the following problem:
I have two Com ports, Com11, and Com18.
I add the following components to my form:
I open a Com port on ApdComPort2 (Com18) and it works without a problem. The ApdDataPacket2 detects the packet terminator and the result displayed is what is expected. Both Com11 and Com18 work fine.
Now if I open another Com port (Com11) with ApdComPort1 I get an Access violation:
The code that generates the error is this in the AdPacket module:
procedure TApdDataPacketManager.EnablePackets;
var
i : integer;
begin
for i := 0 to pred(PacketList.Count) do
with TApdDataPacket(PacketList[i]) do
if Enabled then
Enable;
end;
It is the PacketList.Count that seems to be the problem when it iterates through the list but I can’t catch why:
Note that ApdComPort2 works without problem with both Com11 and Com18.
If I remove the Apd2 components then Apd1 works as expected. The problems surface when I try to use two (or more) Apd components at the same time.
Does anybody have a suggestion or can recommend a component that works with more than one serial port simultaneously?
Some notes about the Turbo Power Async Professional components:
When using the Async components it is very important on how you add the components to the form. If you don’t does it in the right order and in the correct way it will not work if you use more than one serial port. You will actually get an access violation. For example, if you add the components below you have to do it in this way:
Add one ApdComPort to the form, it will become ApdComPort1
Now copy and paste this component to the form, it will become ApdComPort2
Add one ApdDataPacket component to the form, it will become ApdDataPacket1
Now copy and paste this component to the form, it will become ApdDataPacket2
Add one ApdSLController component to the form, it will become ApdSLController1
Now copy and paste this component to the form, it will become ApdSLController2
When doing it, as described above, it works to use two serial ports with ApdDatapacket. Now I don’t get any getting Access violations. I have tested it up to 4 ports and it works as well.
Recently I made some very small, very light app that uses two UDP client and two UDP Server components. Apart from several functions, it has basically nothing else in it.
I was stuck for a while with "Cannot bind socket. Address and port already in use", but somehow solved that with changing the ports, using rsTrue property on Reuse Socket, but somehow, several users still got that error when they try to run the connection from the app.
The server and client has to use same port and same address, as the UDPClient needs to have BoundPort the same as UDPServer's binding port. That's because the responses are being sent to the same port the request came from. This was causing the primary error, which was solved with rsTrue property.
Now I wonder, is there any chance that system overrides this settings by something, I'm not familiar with? Because on 12 computers I have tested so far, it worked fine (on one I had to kill Bonjour service as it was using the exact port that I needed, but other than that...)
What could be causing errors otherwise?
These are settings which are being called on Connect button click:
Server.Binding.IP:=Interface.Text;
Server.Binding.Port:=StrToInt(Port.Text);
Server.DefaultPort:=StrToInt(Port.Text);
Client.Host:= DeviceIP.Text;
Client.Port:= 10023;
Client.BoundIP:= Interface.Text;
Client.BoundPort:= StrToInt(Port.Text);
LocalServer.Binding.IP:= '127.0.0.5';
LocalServer.Binding.Port:= 10023;
LocalServer.DefaultPort:= 10023;
LocalClient.BoundIP:= '127.0.0.1';
LocalClient.BoundPort:= 10024;
LocalClient.Host:= '127.0.0.1';
LocalClient.Port:= 10023;
try Client.Active:=True; finally end;
try Server.Active:=True; finally end;
try LocalClient.Active:=True; finally end;
try LocalServer.Active:=True; finally end;
Where Port is the TEdit field for user to enter port, DeviceIP the device's IP, and Interface the local network interface IP that he will use.
The only hardcoded thing that doesn't really need to be hardcoded is LocalClient.BoundPort:=10024, it's just to make GUI as light as possible. But changing this doesn't help either.
The app is kind of a proxy server between the original App on PC, and the device on network.
The 10023 must be set as is, since the device only listens on that port, and the original app only sends to that port!
Any help would be appreciated.
You do not need to use separate TIdUDPClient and TIdUDPServer on the same port. You can use TIdUDPServer by itself and let it handle both sending and receiving. No need to use a separate TIdUDPClient at all.
Also, you are not using the Binding property correctly. When you read a server's Binding property for the first time, the socket is bound using the settings from the server's Bindings collection. Your assignments to Binding.IP and Binding.Port are then ignored. You need to configure the server's Bindings collection first, then use the server's Binding property only for sending (the server will handle the reading for you).
Try this:
Server.Active := False;
Server.Bindings.Clear;
Server.DefaultPort := StrToInt(Port.Text);
Server.Bindings.Add.IP := Interface.Text;
Server.Active := True;
...
Server.Binding.Send(DeviceIP.Text, 10023, data here);
Replies will arrive in the server's OnUDPRead event.
BTW, since you are writing a proxy, have a look at the TIdMappedPortUDP component.
i need win XP service with TTcpServer.
application was created by "File->New->Other->ServiceApplication"
TTcpServer.localport := 33000
server registered with exename.exe /install
everything looks good, even netstat -a shows that port 33000 - LISTENING
but i can`t access that port from outside of this machine. only local.
and when i make the standard application with same params - all ok.
EDIT1
TTcpServe.OnAccept =
procedure TFlexorXL.tcpServerAccept(Sender: TObject;
ClientSocket: TCustomIpClient);
var str: string;
begin
if ClientSocket.Connect then
begin
str := ClientSocket.Receiveln;
ClientSocket.Sendln('test');
//ClientSocket.Disconnect;
end;
end;
TCP/IP works just fine in a service (I use it all the time), so you are likely just misusing the TTcpServer component (which is possible, because it is a horribly written component).
If the TTcpServer.LocalHost property is blank then the socket will bind to all available local IPv4 addresses, otherwise it will bind only to the particular IPv4 address that you specify (netstat will show you the actual IP that the server is actually bound to). That is the IP that you must have clients connect to. In the case of 0.0.0.0, you can connect to any IP that belongs to the server's machine.
With that said, in order to actually accept clients, you must either:
set the TTcpServer.BlockMode property to bmThreadBlocking. The server will then use an internal worker thread to accept connections, and each client will run in its own worker thread. However, you must perform all of your client-related logic inside of the TTcpServer.OnAccept event, because a client will be disconnected immediately after that event handler exits.
for any other value of BlockMode, you must call TTcpServer.Accept() yourself, such as in a timer or thread. If you call the overloaded version of Accept() that has no parameters, you must perform all of your client-related logic inside of the TTcpServer.OnAccept event, because the client will be disconnected immediately after that event handler exits. If you call the other overloaded version of Accept() that returns a TCustomIpClient object, then you control the lifetime of that object and can use it however you need.
With that said, if you are doing all of that, and still having problems, then you need to provide more information about your actual TTcpServer setup, show some actual TTcpServer code, etc. As it currently stands, you have not provides enough details to diagnose your problem.
I am working with application in delphi. I need to use MIDIYOKE to send output from my application to another application. The second application is Virtual piano keyboard.
I installed all the packages and got MIDI components in the delphi.
I tried using MidiOutputPort1 and MidiInput1 components.
I tried playing one MIDI.The code is as follows:
procedure TForm3.Button1Click(Sender: TObject);
var
outputPort : TMidiOutputPort;
begin
outputPort := TMidiOutputPort.Create (Nil);
try
outputPort.PortId := -1;
outputPort.Active := True;
outputPort.PatchChange(0, 127, 0); // Gunshot
outputPort.NoteOn (1, 20, 127); // Play note at full volume
Sleep (1000);
outputPort.NoteOff (0, 60, 0);
finally
outputPort.Free
end
end;
I wanted to estalish connection between my application and Virtual piano keyboard.How to use MidiOutputPort1 and MidiInput1 for the connection between the two.
If both applications support MIDI sync you can use MIDI syncing. In that case MIDIYOKE is the master and Vpk is the slave. Syncing is handled by the following commands:
mc_MIDI_Timing_Clock = $F8;
mc_MIDI_Start = $FA;
mc_MIDI_Continue = $FB;
mc_MIDI_Stop = $FC;
I used it in the far past, so my knowledge is a bit rusty. What I can gather from my code is that it works as follows: Set the slave in the slave/sync receive/whatever it's called mode. Next send $FA to the channel of your choice. Some (not all) slaves require you to listen to specific channels.
At each clock tick send $F8 first. Next send the messages, preceded by the $FB message (both data bytes zero). When you're ready send $FC.
I think you should put the port number of one of your yoke ports in the portid property.
To know which id to use, you'll have to enumerate the available ports, because the id's can change if you add hardware, or if you change your midi yoke configuration.
Therefore, to remember which ports were chosen by the user, you need to store the device name, and hope that the user doesn't rename its devices :)
Let me know if this helps you enough to be able to continue your work; otherwise i'll dig up some old code that does what you're attempting to do.
I have got a dll that I load in my program which reads and writes its settings to the registry (hkcu). My program changes these settings prior to loading the dll so it uses the settings my program wants it to use which works fine.
Unfortunately I need to run several instances of my program with different settings for the dll. Now the approach I have used so far no longer works reliably because it is possible for one instance of the program to overwrite the settings that another instance just wrote before the dll has a chance to read them.
I haven't got the source of the dll in question and I cannot ask the programmer who wrote it to change it.
One idea I had, was to hook registry access functions and redirect them to a different branch of the registry which is specific to the instance of my program (e.g. use the process id as part of the path). I think this should work but maybe you have got a different / more elegant.
In case it matters: I am using Delphi 2007 for my program, the dll is probably written in C or C++.
As an alternative to API hooking, perhaps you could use RegOverridePredefKey API.
Instead of hooking the registry access for the dll, you can use an inter-process lock mechanism for writing the values to the registry for your own app. The idea being that the lock acquired by instance1 isn't released until its dll "instance" has read the values, so that when instance2 starts it won't acquire the lock until instance1 has finished. You'd need a locking mechanism that works between processes for this to work. For example mutexes.
To create mutexes:
procedure CreateMutexes(const MutexName: string);
//Creates the two mutexes checked for by the installer/uninstaller to see if
//the program is still running.
//One of the mutexes is created in the global name space (which makes it
//possible to access the mutex across user sessions in Windows XP); the other
//is created in the session name space (because versions of Windows NT prior
//to 4.0 TSE don't have a global name space and don't support the 'Global\'
//prefix).
const
SECURITY_DESCRIPTOR_REVISION = 1; // Win32 constant not defined in Delphi 3
var
SecurityDesc: TSecurityDescriptor;
SecurityAttr: TSecurityAttributes;
begin
// By default on Windows NT, created mutexes are accessible only by the user
// running the process. We need our mutexes to be accessible to all users, so
// that the mutex detection can work across user sessions in Windows XP. To
// do this we use a security descriptor with a null DACL.
InitializeSecurityDescriptor(#SecurityDesc, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(#SecurityDesc, True, nil, False);
SecurityAttr.nLength := SizeOf(SecurityAttr);
SecurityAttr.lpSecurityDescriptor := #SecurityDesc;
SecurityAttr.bInheritHandle := False;
CreateMutex(#SecurityAttr, False, PChar(MutexName));
CreateMutex(#SecurityAttr, False, PChar('Global\' + MutexName));
end;
To release a mutex, you'd use the ReleaseMutex API and to acquire a created mutex, you'd use the OpenMutex API.
For CreateMutex see: http://msdn.microsoft.com/en-us/library/ms682411(VS.85).aspx
For OpenMutex see: http://msdn.microsoft.com/en-us/library/ms684315(v=VS.85).aspx
For ReleaseMutex see: http://msdn.microsoft.com/en-us/library/ms685066(v=VS.85).aspx
dirty method: open the dll in a hexeditor and change/alter the registry-path from the original hive to any other you want to use or have your proper settings.