mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-17 06:19:28 +02:00
* Single instance functionality by Ondrej Pokorny
git-svn-id: trunk@32287 -
This commit is contained in:
parent
9de7830c8c
commit
f7cab410c5
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -1966,6 +1966,7 @@ packages/fcl-base/examples/showver.pp svneol=native#text/plain
|
||||
packages/fcl-base/examples/showver.rc -text
|
||||
packages/fcl-base/examples/showver.res -text
|
||||
packages/fcl-base/examples/simple.xml -text
|
||||
packages/fcl-base/examples/sitest.pp svneol=native#text/plain
|
||||
packages/fcl-base/examples/sockcli.pp svneol=native#text/plain
|
||||
packages/fcl-base/examples/socksvr.pp svneol=native#text/plain
|
||||
packages/fcl-base/examples/sstream.pp svneol=native#text/plain
|
||||
@ -2045,6 +2046,7 @@ packages/fcl-base/src/pooledmm.pp svneol=native#text/plain
|
||||
packages/fcl-base/src/rtfdata.inc svneol=native#text/plain
|
||||
packages/fcl-base/src/rtfpars.pp svneol=native#text/plain
|
||||
packages/fcl-base/src/rttiutils.pp svneol=native#text/plain
|
||||
packages/fcl-base/src/singleinstance.pp svneol=native#text/plain
|
||||
packages/fcl-base/src/streamcoll.pp svneol=native#text/plain
|
||||
packages/fcl-base/src/streamex.pp svneol=native#text/plain
|
||||
packages/fcl-base/src/streamio.pp svneol=native#text/plain
|
||||
|
104
packages/fcl-base/examples/sitest.pp
Normal file
104
packages/fcl-base/examples/sitest.pp
Normal file
@ -0,0 +1,104 @@
|
||||
program SITest;
|
||||
|
||||
{$mode objfpc}
|
||||
{$h+}
|
||||
|
||||
uses
|
||||
Classes,
|
||||
CustApp, advancedipc, singleinstance;
|
||||
|
||||
type
|
||||
TMyCustomApplication = class(TCustomApplication)
|
||||
private
|
||||
procedure ServerReceivedParams(Sender: TBaseSingleInstance; aParams: TStringList);
|
||||
procedure ServerReceivedCustomRequest(Sender: TBaseSingleInstance; {%H-}MsgID: Integer; aMsgType: TMessageType; MsgData: TStream);
|
||||
end;
|
||||
|
||||
const
|
||||
MsgType_Request_No_Response = 1;
|
||||
MsgType_Request_With_Response = 2;
|
||||
MsgType_Response = 3;
|
||||
|
||||
{ TMyCustomApplication }
|
||||
|
||||
procedure TMyCustomApplication.ServerReceivedCustomRequest(
|
||||
Sender: TBaseSingleInstance; MsgID: Integer; aMsgType: TMessageType;
|
||||
MsgData: TStream);
|
||||
var
|
||||
xData: string;
|
||||
xStringStream: TStringStream;
|
||||
begin
|
||||
MsgData.Position := 0;
|
||||
SetLength(xData, MsgData.Size div SizeOf(Char));
|
||||
if MsgData.Size > 0 then
|
||||
MsgData.ReadBuffer(xData[1], MsgData.Size);
|
||||
|
||||
WriteLn('Request: ', xData);
|
||||
|
||||
if aMsgType = MsgType_Request_With_Response then
|
||||
begin
|
||||
WriteLn('Sending response to client.');
|
||||
xStringStream := TStringStream.Create('my response');
|
||||
try
|
||||
Sender.ServerPostCustomResponse(MsgID, MsgType_Response, xStringStream);
|
||||
finally
|
||||
xStringStream.Free;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMyCustomApplication.ServerReceivedParams(Sender: TBaseSingleInstance;
|
||||
aParams: TStringList);
|
||||
var
|
||||
I: Integer;
|
||||
begin
|
||||
Writeln('-----');
|
||||
Writeln('Params:');
|
||||
for I := 0 to aParams.Count-1 do
|
||||
Writeln(aParams[I]);
|
||||
Writeln('-----');
|
||||
end;
|
||||
|
||||
var
|
||||
xApp: TMyCustomApplication;
|
||||
xStream: TStringStream;
|
||||
xMsgType: TMessageType;
|
||||
begin
|
||||
xApp := TMyCustomApplication.Create(nil);
|
||||
try
|
||||
xApp.SingleInstance.Enabled := True;
|
||||
xApp.SingleInstance.OnServerReceivedParams := @xApp.ServerReceivedParams;
|
||||
xApp.SingleInstance.OnServerReceivedCustomRequest := @xApp.ServerReceivedCustomRequest;
|
||||
xApp.Initialize;
|
||||
Writeln(xApp.SingleInstance.StartResult);
|
||||
xApp.Run;
|
||||
|
||||
case xApp.SingleInstance.StartResult of
|
||||
siNotResponding: ReadLn;
|
||||
siClient:
|
||||
begin
|
||||
xStream := TStringStream.Create('hello');
|
||||
try
|
||||
xApp.SingleInstance.ClientSendCustomRequest(MsgType_Request_No_Response, xStream);
|
||||
finally
|
||||
xStream.Free;
|
||||
end;
|
||||
xStream := TStringStream.Create('I want a response');
|
||||
try
|
||||
xApp.SingleInstance.ClientSendCustomRequest(MsgType_Request_With_Response, xStream);
|
||||
xStream.Size := 0;
|
||||
if xApp.SingleInstance.ClientPeekCustomResponse(xStream, xMsgType) then
|
||||
WriteLn('Response: ', xStream.DataString)
|
||||
else
|
||||
WriteLn('Error: no response');
|
||||
finally
|
||||
xStream.Free;
|
||||
end;
|
||||
ReadLn;
|
||||
end;
|
||||
end;
|
||||
finally
|
||||
xApp.Free;
|
||||
end;
|
||||
end.
|
||||
|
@ -52,8 +52,12 @@ begin
|
||||
T.ResourceStrings:=true;
|
||||
T:=P.Targets.AddUnit('contnrs.pp');
|
||||
T.ResourceStrings:=true;
|
||||
T:=P.Targets.AddUnit('custapp.pp');
|
||||
T:=P.Targets.AddUnit('singleinstance.pp');
|
||||
T.ResourceStrings:=true;
|
||||
T:=P.Targets.AddUnit('custapp.pp');
|
||||
T.ResourceStrings:=true;
|
||||
with T.Dependencies do
|
||||
AddUnit('singleinstance');
|
||||
T:=P.Targets.AddUnit('eventlog.pp');
|
||||
T.ResourceStrings:=true;
|
||||
with T.Dependencies do
|
||||
|
@ -18,18 +18,22 @@ unit CustApp;
|
||||
|
||||
Interface
|
||||
|
||||
uses SysUtils,Classes;
|
||||
uses SysUtils,Classes,singleinstance;
|
||||
|
||||
Type
|
||||
TExceptionEvent = Procedure (Sender : TObject; E : Exception) Of Object;
|
||||
TEventLogTypes = Set of TEventType;
|
||||
|
||||
TCustomApplication = Class;
|
||||
TCustomSingleInstance = Class;
|
||||
|
||||
{ TCustomApplication }
|
||||
|
||||
TCustomApplication = Class(TComponent)
|
||||
Private
|
||||
FEventLogFilter: TEventLogTypes;
|
||||
FOnException: TExceptionEvent;
|
||||
FSingleInstance: TCustomSingleInstance;
|
||||
FTerminated : Boolean;
|
||||
FHelpFile,
|
||||
FTitle : String;
|
||||
@ -86,6 +90,15 @@ Type
|
||||
Property CaseSensitiveOptions : Boolean Read FCaseSensitiveOptions Write FCaseSensitiveOptions;
|
||||
Property StopOnException : Boolean Read FStopOnException Write FStopOnException;
|
||||
Property EventLogFilter : TEventLogTypes Read FEventLogFilter Write FEventLogFilter;
|
||||
Property SingleInstance: TCustomSingleInstance read FSingleInstance;
|
||||
end;
|
||||
|
||||
TCustomSingleInstance = class(TBaseSingleInstance)
|
||||
private
|
||||
FEnabled: Boolean;
|
||||
public
|
||||
//you must set Enabled before CustomApplication.Initialize
|
||||
property Enabled: Boolean read FEnabled write FEnabled;
|
||||
end;
|
||||
|
||||
var CustomApplication : TCustomApplication = nil;
|
||||
@ -228,7 +241,10 @@ end;
|
||||
|
||||
procedure TCustomApplication.DoRun;
|
||||
begin
|
||||
// Do nothing. Override in descendent classes.
|
||||
if FSingleInstance.IsServer then
|
||||
FSingleInstance.ServerCheckMessages;
|
||||
|
||||
// Override in descendent classes.
|
||||
end;
|
||||
|
||||
procedure TCustomApplication.DoLog(EventType: TEventType; const Msg: String);
|
||||
@ -250,6 +266,7 @@ begin
|
||||
FOptionChar:='-';
|
||||
FCaseSensitiveOptions:=True;
|
||||
FStopOnException:=False;
|
||||
FSingleInstance := TCustomSingleInstance.Create(Self);
|
||||
end;
|
||||
|
||||
destructor TCustomApplication.Destroy;
|
||||
@ -276,6 +293,18 @@ end;
|
||||
procedure TCustomApplication.Initialize;
|
||||
begin
|
||||
FTerminated:=False;
|
||||
if FSingleInstance.Enabled then
|
||||
begin
|
||||
case FSingleInstance.Start of
|
||||
siClient:
|
||||
begin
|
||||
FSingleInstance.ClientPostParams;
|
||||
FTerminated:=True;
|
||||
end;
|
||||
siNotResponding:
|
||||
FTerminated:=True;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TCustomApplication.Run;
|
||||
@ -442,11 +471,11 @@ Var
|
||||
end;
|
||||
|
||||
Procedure AddToResult(Const Msg : string);
|
||||
|
||||
|
||||
begin
|
||||
If (Result<>'') then
|
||||
Result:=Result+sLineBreak;
|
||||
Result:=Result+Msg;
|
||||
Result:=Result+Msg;
|
||||
end;
|
||||
|
||||
begin
|
||||
|
419
packages/fcl-base/src/singleinstance.pp
Normal file
419
packages/fcl-base/src/singleinstance.pp
Normal file
@ -0,0 +1,419 @@
|
||||
unit singleinstance;
|
||||
|
||||
{
|
||||
This file is part of the Free Component Library (FCL)
|
||||
Copyright (c) 2015 by Ondrej Pokorny
|
||||
|
||||
See the file COPYING.FPC, included in this distribution,
|
||||
for details about the copyright.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
**********************************************************************}
|
||||
|
||||
{$mode objfpc}
|
||||
{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
SysUtils, Classes, advancedipc;
|
||||
|
||||
type
|
||||
|
||||
TBaseSingleInstance = class;
|
||||
|
||||
//siServer: No other instance is running. The server is started.
|
||||
//siClient: There is another instance running. This instance is used as client.
|
||||
//siNotResponding: There is another instance running but it doesn't respond.
|
||||
TSingleInstanceStart = (siServer, siClient, siNotResponding);
|
||||
TSingleInstanceParams = procedure(Sender: TBaseSingleInstance; Params: TStringList) of object;
|
||||
TSingleInstanceReceivedCustomMessage = procedure(Sender: TBaseSingleInstance; MsgID: Integer; MsgType: TMessageType; MsgData: TStream) of object;
|
||||
TBaseSingleInstance = class(TComponent)
|
||||
private
|
||||
FGlobal: Boolean;
|
||||
FID: string;
|
||||
FServer: TIPCServer;
|
||||
FClient: TIPCClient;
|
||||
FStartResult: TSingleInstanceStart;
|
||||
FTimeOutMessages: Integer;
|
||||
FTimeOutWaitForInstances: Integer;
|
||||
FOnServerReceivedCustomRequest: TSingleInstanceReceivedCustomMessage;
|
||||
FOnServerReceivedParams: TSingleInstanceParams;
|
||||
function GetIsClient: Boolean;
|
||||
function GetIsServer: Boolean;
|
||||
function GetStartResult: TSingleInstanceStart;
|
||||
procedure SetGlobal(const aGlobal: Boolean);
|
||||
procedure SetID(const aID: string);
|
||||
procedure DoServerReceivedParams(const aParamsDelimitedText: string);
|
||||
procedure DoServerReceivedCustomRequest(const aMsgID: Integer; const aMsgType: TMessageType; const aStream: TStream);
|
||||
protected
|
||||
//call Start when you want to start single instance checking
|
||||
function Start: TSingleInstanceStart;
|
||||
//stop single instance server or client
|
||||
procedure Stop;
|
||||
|
||||
procedure ServerCheckMessages;
|
||||
procedure ClientPostParams;
|
||||
public
|
||||
constructor Create(aOwner: TComponent); override;
|
||||
destructor Destroy; override;
|
||||
public
|
||||
function ClientPostCustomRequest(const aMsgType: TMessageType; const aStream: TStream): Integer;
|
||||
function ClientSendCustomRequest(const aMsgType: TMessageType; const aStream: TStream): Boolean; overload;
|
||||
function ClientSendCustomRequest(const aMsgType: TMessageType; const aStream: TStream; out outRequestID: Integer): Boolean; overload;
|
||||
procedure ServerPostCustomResponse(const aRequestID: Integer; const aMsgType: TMessageType; const aStream: TStream);
|
||||
function ClientPeekCustomResponse(const aStream: TStream; out outMsgType: TMessageType): Boolean;
|
||||
public
|
||||
property ID: string read FID write SetID;
|
||||
property Global: Boolean read FGlobal write SetGlobal;
|
||||
property TimeOutMessages: Integer read FTimeOutMessages write FTimeOutMessages;
|
||||
property TimeOutWaitForInstances: Integer read FTimeOutWaitForInstances write FTimeOutWaitForInstances;
|
||||
property OnServerReceivedParams: TSingleInstanceParams read FOnServerReceivedParams write FOnServerReceivedParams;
|
||||
property OnServerReceivedCustomRequest: TSingleInstanceReceivedCustomMessage read FOnServerReceivedCustomRequest write FOnServerReceivedCustomRequest;
|
||||
public
|
||||
property StartResult: TSingleInstanceStart read GetStartResult;
|
||||
property IsServer: Boolean read GetIsServer;
|
||||
property IsClient: Boolean read GetIsClient;
|
||||
end;
|
||||
|
||||
TSingleInstance = class(TBaseSingleInstance)
|
||||
public
|
||||
function Start: TSingleInstanceStart;
|
||||
procedure Stop;
|
||||
|
||||
procedure ServerCheckMessages;
|
||||
procedure ClientPostParams;
|
||||
end;
|
||||
|
||||
ESingleInstance = class(Exception);
|
||||
|
||||
implementation
|
||||
|
||||
Resourcestring
|
||||
SErrSetSingleInstanceIDStarted = 'You cannot change the single instance ID when it''s been started.';
|
||||
SErrSetSingleInstanceGlobalStarted = 'You cannot change the single instance global property when it''s been started.';
|
||||
SErrStartSingleInstanceStarted = 'You cannot start single instance when it''s been already started.';
|
||||
SErrSingleInstanceStartResultNotAvailable = 'Single instance hasn''t been started yet.';
|
||||
SErrSingleInstanceNotClient = 'Current instance is not a client.';
|
||||
SErrSingleInstanceNotServer = 'Current instance is not a server.';
|
||||
|
||||
Const
|
||||
MSGTYPE_CHECK = -1;
|
||||
MSGTYPE_CHECKRESPONSE = -2;
|
||||
MSGTYPE_PARAMS = -3;
|
||||
MSGTYPE_WAITFORINSTANCES = -4;
|
||||
|
||||
{ TSingleInstance }
|
||||
|
||||
procedure TSingleInstance.ClientPostParams;
|
||||
begin
|
||||
inherited ClientPostParams;
|
||||
end;
|
||||
|
||||
procedure TSingleInstance.ServerCheckMessages;
|
||||
begin
|
||||
inherited ServerCheckMessages;
|
||||
end;
|
||||
|
||||
function TSingleInstance.Start: TSingleInstanceStart;
|
||||
begin
|
||||
Result := inherited Start;
|
||||
end;
|
||||
|
||||
procedure TSingleInstance.Stop;
|
||||
begin
|
||||
inherited Stop;
|
||||
end;
|
||||
|
||||
{ TBaseSingleInstance }
|
||||
|
||||
function TBaseSingleInstance.ClientPeekCustomResponse(const aStream: TStream; out
|
||||
outMsgType: TMessageType): Boolean;
|
||||
begin
|
||||
if not Assigned(FClient) then
|
||||
raise ESingleInstance.Create(SErrSingleInstanceNotClient);
|
||||
|
||||
Result := FClient.PeekResponse(aStream, outMsgType, FTimeOutMessages);
|
||||
end;
|
||||
|
||||
function TBaseSingleInstance.ClientPostCustomRequest(const aMsgType: TMessageType;
|
||||
const aStream: TStream): Integer;
|
||||
begin
|
||||
if not Assigned(FClient) then
|
||||
raise ESingleInstance.Create(SErrSingleInstanceNotClient);
|
||||
|
||||
Result := FClient.PostRequest(aMsgType, aStream);
|
||||
end;
|
||||
|
||||
procedure TBaseSingleInstance.ClientPostParams;
|
||||
var
|
||||
xSL: TStringList;
|
||||
xStringStream: TStringStream;
|
||||
I: Integer;
|
||||
begin
|
||||
if not Assigned(FClient) then
|
||||
raise ESingleInstance.Create(SErrSingleInstanceNotClient);
|
||||
|
||||
xSL := TStringList.Create;
|
||||
try
|
||||
for I := 0 to ParamCount do
|
||||
xSL.Add(ParamStr(I));
|
||||
|
||||
xStringStream := TStringStream.Create(xSL.DelimitedText);
|
||||
try
|
||||
xStringStream.Position := 0;
|
||||
FClient.PostRequest(MSGTYPE_PARAMS, xStringStream);
|
||||
finally
|
||||
xStringStream.Free;
|
||||
end;
|
||||
finally
|
||||
xSL.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TBaseSingleInstance.ClientSendCustomRequest(
|
||||
const aMsgType: TMessageType; const aStream: TStream): Boolean;
|
||||
begin
|
||||
if not Assigned(FClient) then
|
||||
raise ESingleInstance.Create(SErrSingleInstanceNotClient);
|
||||
|
||||
Result := FClient.SendRequest(aMsgType, aStream, FTimeOutMessages);
|
||||
end;
|
||||
|
||||
function TBaseSingleInstance.ClientSendCustomRequest(const aMsgType: TMessageType;
|
||||
const aStream: TStream; out outRequestID: Integer): Boolean;
|
||||
begin
|
||||
if not Assigned(FClient) then
|
||||
raise ESingleInstance.Create(SErrSingleInstanceNotClient);
|
||||
|
||||
Result := FClient.SendRequest(aMsgType, aStream, FTimeOutMessages, outRequestID);
|
||||
end;
|
||||
|
||||
constructor TBaseSingleInstance.Create(aOwner: TComponent);
|
||||
var
|
||||
xID: RawByteString;
|
||||
I: Integer;
|
||||
begin
|
||||
inherited Create(aOwner);
|
||||
|
||||
FTimeOutMessages := 1000;
|
||||
FTimeOutWaitForInstances := 100;
|
||||
|
||||
xID := 'SI_'+ExtractFileName(ParamStr(0));
|
||||
for I := 1 to Length(xID) do
|
||||
case xID[I] of
|
||||
'a'..'z', 'A'..'Z', '0'..'9', '_': begin end;
|
||||
else
|
||||
xID[I] := '_';
|
||||
end;
|
||||
ID := xID;
|
||||
end;
|
||||
|
||||
destructor TBaseSingleInstance.Destroy;
|
||||
begin
|
||||
Stop;
|
||||
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TBaseSingleInstance.DoServerReceivedCustomRequest(
|
||||
const aMsgID: Integer; const aMsgType: TMessageType; const aStream: TStream);
|
||||
begin
|
||||
if Assigned(FOnServerReceivedCustomRequest) then
|
||||
FOnServerReceivedCustomRequest(Self, aMsgID, aMsgType, aStream);
|
||||
end;
|
||||
|
||||
procedure TBaseSingleInstance.DoServerReceivedParams(
|
||||
const aParamsDelimitedText: string);
|
||||
var
|
||||
xSL: TStringList;
|
||||
begin
|
||||
if not Assigned(FOnServerReceivedParams) then
|
||||
Exit;
|
||||
|
||||
xSL := TStringList.Create;
|
||||
try
|
||||
xSL.DelimitedText := aParamsDelimitedText;
|
||||
FOnServerReceivedParams(Self, xSL);
|
||||
finally
|
||||
xSL.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TBaseSingleInstance.GetIsClient: Boolean;
|
||||
begin
|
||||
Result := Assigned(FClient);
|
||||
end;
|
||||
|
||||
function TBaseSingleInstance.GetIsServer: Boolean;
|
||||
begin
|
||||
Result := Assigned(FServer);
|
||||
end;
|
||||
|
||||
function TBaseSingleInstance.GetStartResult: TSingleInstanceStart;
|
||||
begin
|
||||
if not(Assigned(FServer) or Assigned(FClient)) then
|
||||
raise ESingleInstance.Create(SErrSingleInstanceStartResultNotAvailable);
|
||||
|
||||
Result := FStartResult;
|
||||
end;
|
||||
|
||||
procedure TBaseSingleInstance.ServerCheckMessages;
|
||||
var
|
||||
xMsgID: Integer;
|
||||
xMsgType: TMessageType;
|
||||
xStream: TStream;
|
||||
xStringStream: TStringStream;
|
||||
begin
|
||||
if not Assigned(FServer) then
|
||||
raise ESingleInstance.Create(SErrSingleInstanceNotServer);
|
||||
|
||||
if not FServer.PeekRequest(xMsgID, xMsgType) then
|
||||
Exit;
|
||||
|
||||
case xMsgType of
|
||||
MSGTYPE_CHECK:
|
||||
begin
|
||||
FServer.DeleteRequest(xMsgID);
|
||||
FServer.PostResponse(xMsgID, MSGTYPE_CHECKRESPONSE, nil);
|
||||
end;
|
||||
MSGTYPE_PARAMS:
|
||||
begin
|
||||
xStringStream := TStringStream.Create('');
|
||||
try
|
||||
FServer.ReadRequest(xMsgID, xStringStream);
|
||||
DoServerReceivedParams(xStringStream.DataString);
|
||||
finally
|
||||
xStringStream.Free;
|
||||
end;
|
||||
end;
|
||||
MSGTYPE_WAITFORINSTANCES:
|
||||
FServer.DeleteRequest(xMsgID);
|
||||
else
|
||||
xStream := TMemoryStream.Create;
|
||||
try
|
||||
FServer.ReadRequest(xMsgID, xStream);
|
||||
DoServerReceivedCustomRequest(xMsgID, xMsgType, xStream);
|
||||
finally
|
||||
xStream.Free;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TBaseSingleInstance.ServerPostCustomResponse(
|
||||
const aRequestID: Integer; const aMsgType: TMessageType;
|
||||
const aStream: TStream);
|
||||
begin
|
||||
if not Assigned(FServer) then
|
||||
raise ESingleInstance.Create(SErrSingleInstanceNotServer);
|
||||
|
||||
FServer.PostResponse(aRequestID, aMsgType, aStream);
|
||||
end;
|
||||
|
||||
procedure TBaseSingleInstance.SetGlobal(const aGlobal: Boolean);
|
||||
begin
|
||||
if FGlobal = aGlobal then Exit;
|
||||
if Assigned(FServer) or Assigned(FClient) then
|
||||
raise ESingleInstance.Create(SErrSetSingleInstanceGlobalStarted);
|
||||
FGlobal := aGlobal;
|
||||
end;
|
||||
|
||||
procedure TBaseSingleInstance.SetID(const aID: string);
|
||||
begin
|
||||
if FID = aID then Exit;
|
||||
if Assigned(FServer) or Assigned(FClient) then
|
||||
raise ESingleInstance.Create(SErrSetSingleInstanceIDStarted);
|
||||
FID := aID;
|
||||
end;
|
||||
|
||||
procedure TBaseSingleInstance.Stop;
|
||||
begin
|
||||
FreeAndNil(FServer);
|
||||
FreeAndNil(FClient);
|
||||
end;
|
||||
|
||||
function TBaseSingleInstance.Start: TSingleInstanceStart;
|
||||
{$IFNDEF MSWINDOWS}
|
||||
procedure UnixWorkaround(var bServerStarted: Boolean);
|
||||
var
|
||||
xWaitRequestID, xLastCount, xNewCount: Integer;
|
||||
xClient: TIPCClient;
|
||||
begin
|
||||
//file locking workaround for UNIX systems -> the server can be started twice if 2 processes are started in parallel
|
||||
//wait some time to see other clients
|
||||
FServer.StopServer(False);
|
||||
xClient := TIPCClient.Create(Self);
|
||||
try
|
||||
xClient.ServerID := FID;
|
||||
xClient.Global := FGlobal;
|
||||
xWaitRequestID := xClient.PostRequest(MSGTYPE_WAITFORINSTANCES, nil);
|
||||
xLastCount := -1;
|
||||
xNewCount := FServer.GetPendingRequestCount;
|
||||
while xLastCount <> xNewCount do
|
||||
begin
|
||||
xLastCount := xNewCount;
|
||||
Sleep(FTimeOutWaitForInstances);
|
||||
xNewCount := FServer.GetPendingRequestCount;
|
||||
end;
|
||||
finally
|
||||
FreeAndNil(xClient);
|
||||
end;
|
||||
|
||||
//find highest client that will be the server
|
||||
if FServer.FindHighestPendingRequestId = xWaitRequestID then
|
||||
begin
|
||||
bServerStarted := FServer.StartServer(False);
|
||||
end else
|
||||
begin
|
||||
//something went wrong, there are not-deleted waiting requests
|
||||
//use random sleep as workaround and try to restart the server
|
||||
Randomize;
|
||||
Sleep(Random(($3F+PtrInt(GetCurrentThreadId)) and $3F));//limit to $3F (63)
|
||||
bServerStarted := FServer.StartServer(False) and (FServer.GetPendingRequestCount > 0);
|
||||
end;
|
||||
end;
|
||||
{$ENDIF}
|
||||
var
|
||||
xStream: TStream;
|
||||
xMsgType: TMessageType;
|
||||
xServerStarted: Boolean;
|
||||
begin
|
||||
if Assigned(FServer) or Assigned(FClient) then
|
||||
raise ESingleInstance.Create(SErrStartSingleInstanceStarted);
|
||||
|
||||
FServer := TIPCServer.Create(Self);
|
||||
FServer.ServerID := FID;
|
||||
FServer.Global := FGlobal;
|
||||
xServerStarted := FServer.StartServer(False);
|
||||
if xServerStarted then
|
||||
begin//this is single instance -> be server
|
||||
Result := siServer;
|
||||
{$IFNDEF MSWINDOWS}
|
||||
UnixWorkaround(xServerStarted);
|
||||
{$ENDIF}
|
||||
end;
|
||||
if not xServerStarted then
|
||||
begin//instance found -> be client
|
||||
FreeAndNil(FServer);
|
||||
FClient := TIPCClient.Create(Self);
|
||||
FClient.ServerID := FID;
|
||||
FClient.Global := FGlobal;
|
||||
FClient.PostRequest(MSGTYPE_CHECK, nil);
|
||||
xStream := TMemoryStream.Create;
|
||||
try
|
||||
if FClient.PeekResponse(xStream, xMsgType, FTimeOutMessages) then
|
||||
Result := siClient
|
||||
else
|
||||
Result := siNotResponding;
|
||||
finally
|
||||
xStream.Free;
|
||||
end;
|
||||
end;
|
||||
FStartResult := Result;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
Loading…
Reference in New Issue
Block a user