SimpleIPC
Simple IPC is a Free Pascal unit (and Lazarus components) that allows interprocess communication (IPC) between Free Pascal programs.
It allows two executables to communicate.
Advantages of SimpleIPC
You can create communication systems without requiring low level sockets programming of your own. SimpleIPC does the grunt work of IPC for you and makes a nice high level wrapper around lower level IPC mechanisms, so you don't have to program them yourself.
SimpleIPC can be used to communicate between programs to make simple communication systems, plugin systems and much more,
Hint : Multiuser Systems
On multiuser systems, eg, Unix systems, in Global Mode, note that the pipe that IPC creates must have a distinctive name between individual users. Its usual to set the ServerID to the application name so, add the user name to that -
CommsServer := TSimpleIPCServer.Create(Nil);
CommsServer.ServerID:='MyAppName' {$ifdef UNIX} + '-' + GetEnvironmentVariable('USER'){$endif};
CommsServer.OnMessageQueued:=@CommMessageReceived;
CommsServer.Global:=True;
CommsServer.StartServer({$ifdef WINDOWS}False{$else}True{$endif}); // start listening, threaded
Without the user name being added, you risk all instances of the App trying to use the same named pipe and, obviously permissions do not allow that.
Use Cases
The Lazarus help system itself, uses SimpleIPC.
Some fpGUI tools and demos use IPC, for example, to detect if the program is already running (single instance)
Example: Single Instance Application
Assume your application is a text editor and the user can open files using Open With from a file manager.
If the application is already running, instead of launching a second instance, the new instance should send the file name to the running instance and then exit. The running instance receives the file name and opens it.
Below is a minimal example demonstrating this approach.
1. A helper class returning the unique SimpleIPC server name
On Unix systems it is recommended to append the current user name to avoid conflicts on multi-user systems.
unit o_App;
{$mode DELPHI}{$H+}
interface
uses
Classes
, SysUtils
;
type
App = class
public
class function GetSimpleIpcServerName(): string;
end;
implementation
class function App.GetSimpleIpcServerName(): string;
begin
Result := 'SlatePad_Main_Instance';
{$ifdef UNIX}
Result := Result + '-' + GetEnvironmentVariable('USER');
{$endif}
end;
end.
2. In the program (.lpr) file
The program first checks whether another instance is already running. If another instance exists, the file name is sent to that instance and the new instance exits.
program SlatePad;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}
cthreads,
{$ENDIF}
{$IFDEF HASAMIGA}
athreads,
{$ENDIF}
Interfaces,
SimpleIPC,
Forms, SysUtils, f_MainForm, o_App;
{$R *.res}
var
Client: TSimpleIPCClient;
FilePath: string;
begin
if ParamCount > 0 then
FilePath := ExpandFileName(ParamStr(1))
else
FilePath := '';
Client := TSimpleIPCClient.Create(nil);
try
Client.ServerID := App.GetSimpleIpcServerName();
if Client.ServerRunning then
begin
if (FilePath <> '') and FileExists(FilePath) then
begin
Client.Connect;
try
Client.SendStringMessage(FilePath);
finally
Client.Disconnect;
end;
end;
Halt;
Exit;
end;
finally
Client.Free;
end;
RequireDerivedFormResource := True;
Application.Scaled := True;
{$PUSH}{$WARN 5044 OFF}
Application.MainFormOnTaskbar := True;
{$POP}
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
Application.Run;
end.
3. In the main form
The main instance creates a TSimpleIPCServer and periodically checks for incoming messages. When a message arrives, the file name is read and opened.
uses
SimpleIPC;
private
IpcServerTimer: TTimer;
IpcServer: TSimpleIPCServer;
constructor TMainForm.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
IpcServer := TSimpleIPCServer.Create(Self);
IpcServer.ServerID := App.GetSimpleIpcServerName();
IpcServer.MaxQueue := 1;
IpcServer.Global := True;
IpcServer.SynchronizeEvents := True;
IpcServer.Threaded := False;
IpcServer.OnMessage := IpcServer_OnMessage;
IpcServerTimer := TTimer.Create(Self);
IpcServerTimer.Interval := 250;
IpcServerTimer.OnTimer := IpcServerTimer_OnTimer;
IpcServerTimer.Enabled := True;
IpcServer.StartServer;
end;
procedure TMainForm.IpcServerTimer_OnTimer(Sender: TObject);
begin
while IpcServer.PeekMessage(0, True) do
IpcServer.ReadMessage;
end;
procedure TMainForm.IpcServer_OnMessage(Sender: TObject);
var
FilePath: string;
begin
FilePath := Trim(IpcServer.StringMessage);
if (FilePath <> '') and FileExists(FilePath) then
OpenDoc(FilePath);
end;
Send Messages to Other Programming Languages
For IPC between Free Pascal programs and other programs written in any language (C++, Delphi, GoLang, etc.) see SimpleIPC Library which allows you to use SimpleIPC not just in FPC and Lazarus, but in multiple programming languages.