mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-12 09:06:02 +02:00
* Refactored singleinstance so it is independent of any specific implementation
git-svn-id: trunk@32770 -
This commit is contained in:
parent
2451f7596b
commit
a2985a5572
@ -40,7 +40,7 @@ begin
|
|||||||
WriteLn('Sending response to client.');
|
WriteLn('Sending response to client.');
|
||||||
xStringStream := TStringStream.Create('my response');
|
xStringStream := TStringStream.Create('my response');
|
||||||
try
|
try
|
||||||
Sender.ServerPostCustomResponse(MsgID, MsgType_Response, xStringStream);
|
(Sender as TAdvancedSingleInstance).ServerPostCustomResponse(MsgID, MsgType_Response, xStringStream);
|
||||||
finally
|
finally
|
||||||
xStringStream.Free;
|
xStringStream.Free;
|
||||||
end;
|
end;
|
||||||
@ -66,9 +66,9 @@ var
|
|||||||
begin
|
begin
|
||||||
xApp := TMyCustomApplication.Create(nil);
|
xApp := TMyCustomApplication.Create(nil);
|
||||||
try
|
try
|
||||||
xApp.SingleInstance.Enabled := True;
|
xApp.SingleInstanceEnabled := True;
|
||||||
xApp.SingleInstance.OnServerReceivedParams := @xApp.ServerReceivedParams;
|
xApp.SingleInstance.OnServerReceivedParams := @xApp.ServerReceivedParams;
|
||||||
xApp.SingleInstance.OnServerReceivedCustomRequest := @xApp.ServerReceivedCustomRequest;
|
(xApp.SingleInstance as TAdvancedSingleInstance).OnServerReceivedCustomRequest := @xApp.ServerReceivedCustomRequest;
|
||||||
xApp.Initialize;
|
xApp.Initialize;
|
||||||
Writeln(xApp.SingleInstance.StartResult);
|
Writeln(xApp.SingleInstance.StartResult);
|
||||||
xApp.Run;
|
xApp.Run;
|
||||||
@ -79,15 +79,15 @@ begin
|
|||||||
begin
|
begin
|
||||||
xStream := TStringStream.Create('hello');
|
xStream := TStringStream.Create('hello');
|
||||||
try
|
try
|
||||||
xApp.SingleInstance.ClientSendCustomRequest(MsgType_Request_No_Response, xStream);
|
(xApp.SingleInstance as TAdvancedSingleInstance).ClientSendCustomRequest(MsgType_Request_No_Response, xStream);
|
||||||
finally
|
finally
|
||||||
xStream.Free;
|
xStream.Free;
|
||||||
end;
|
end;
|
||||||
xStream := TStringStream.Create('I want a response');
|
xStream := TStringStream.Create('I want a response');
|
||||||
try
|
try
|
||||||
xApp.SingleInstance.ClientSendCustomRequest(MsgType_Request_With_Response, xStream);
|
(xApp.SingleInstance as TAdvancedSingleInstance).ClientSendCustomRequest(MsgType_Request_With_Response, xStream);
|
||||||
xStream.Size := 0;
|
xStream.Size := 0;
|
||||||
if xApp.SingleInstance.ClientPeekCustomResponse(xStream, xMsgType) then
|
if (xApp.SingleInstance as TAdvancedSingleInstance).ClientPeekCustomResponse(xStream, xMsgType) then
|
||||||
WriteLn('Response: ', xStream.DataString)
|
WriteLn('Response: ', xStream.DataString)
|
||||||
else
|
else
|
||||||
WriteLn('Error: no response');
|
WriteLn('Error: no response');
|
||||||
|
@ -30,7 +30,7 @@ uses
|
|||||||
{$IFDEF UNIX}
|
{$IFDEF UNIX}
|
||||||
baseunix,
|
baseunix,
|
||||||
{$endif}
|
{$endif}
|
||||||
sysutils, Classes;
|
sysutils, Classes, singleinstance;
|
||||||
|
|
||||||
const
|
const
|
||||||
HEADER_VERSION = 2;
|
HEADER_VERSION = 2;
|
||||||
@ -168,6 +168,43 @@ type
|
|||||||
|
|
||||||
EICPException = class(Exception);
|
EICPException = class(Exception);
|
||||||
|
|
||||||
|
TSingleInstanceReceivedCustomMessage = procedure(Sender: TBaseSingleInstance; MsgID: Integer; MsgType: Integer; MsgData: TStream) of object;
|
||||||
|
|
||||||
|
TAdvancedSingleInstance = class(TBaseSingleInstance)
|
||||||
|
private
|
||||||
|
FGlobal: Boolean;
|
||||||
|
FID: string;
|
||||||
|
FServer: TIPCServer;
|
||||||
|
FClient: TIPCClient;
|
||||||
|
FOnServerReceivedCustomRequest: TSingleInstanceReceivedCustomMessage;
|
||||||
|
function GetIsClient: Boolean; override;
|
||||||
|
function GetIsServer: Boolean; override;
|
||||||
|
function GetStartResult: TSingleInstanceStart; override;
|
||||||
|
procedure SetGlobal(const aGlobal: Boolean);
|
||||||
|
procedure SetID(const aID: string);
|
||||||
|
protected
|
||||||
|
procedure DoServerReceivedCustomRequest(const aMsgID: Integer; const aMsgType: Integer; const aStream: TStream);
|
||||||
|
public
|
||||||
|
constructor Create(aOwner: TComponent); override;
|
||||||
|
public
|
||||||
|
function Start: TSingleInstanceStart; override;
|
||||||
|
procedure Stop; override;
|
||||||
|
|
||||||
|
procedure ServerCheckMessages; override;
|
||||||
|
procedure ClientPostParams; override;
|
||||||
|
public
|
||||||
|
function ClientPostCustomRequest(const aMsgType: Integer; const aStream: TStream): Integer;
|
||||||
|
function ClientSendCustomRequest(const aMsgType: Integer; const aStream: TStream): Boolean; overload;
|
||||||
|
function ClientSendCustomRequest(const aMsgType: Integer; const aStream: TStream; out outRequestID: Integer): Boolean; overload;
|
||||||
|
procedure ServerPostCustomResponse(const aRequestID: Integer; const aMsgType: Integer; const aStream: TStream);
|
||||||
|
function ClientPeekCustomResponse(const aStream: TStream; out outMsgType: Integer): Boolean;
|
||||||
|
public
|
||||||
|
property ID: string read FID write SetID;
|
||||||
|
property Global: Boolean read FGlobal write SetGlobal;
|
||||||
|
|
||||||
|
property OnServerReceivedCustomRequest: TSingleInstanceReceivedCustomMessage read FOnServerReceivedCustomRequest write FOnServerReceivedCustomRequest;
|
||||||
|
end;
|
||||||
|
|
||||||
resourcestring
|
resourcestring
|
||||||
SErrInvalidServerID = 'Invalid server ID "%s". Please use only alphanumerical characters and underlines.';
|
SErrInvalidServerID = 'Invalid server ID "%s". Please use only alphanumerical characters and underlines.';
|
||||||
SErrSetGlobalActive = 'You cannot change the global property when the server is active.';
|
SErrSetGlobalActive = 'You cannot change the global property when the server is active.';
|
||||||
@ -772,8 +809,284 @@ begin
|
|||||||
FActive := False;
|
FActive := False;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
{ TAdvancedSingleInstance }
|
||||||
|
|
||||||
|
constructor TAdvancedSingleInstance.Create(aOwner: TComponent);
|
||||||
|
var
|
||||||
|
xID: RawByteString;
|
||||||
|
I: Integer;
|
||||||
|
begin
|
||||||
|
inherited Create(aOwner);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
function TAdvancedSingleInstance.ClientPeekCustomResponse(
|
||||||
|
const aStream: TStream; out outMsgType: Integer): Boolean;
|
||||||
|
begin
|
||||||
|
if not Assigned(FClient) then
|
||||||
|
raise ESingleInstance.Create(SErrSingleInstanceNotClient);
|
||||||
|
|
||||||
|
Result := FClient.PeekResponse(aStream, outMsgType, TimeOutMessages);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAdvancedSingleInstance.ClientPostCustomRequest(
|
||||||
|
const aMsgType: Integer; const aStream: TStream): Integer;
|
||||||
|
begin
|
||||||
|
if not Assigned(FClient) then
|
||||||
|
raise ESingleInstance.Create(SErrSingleInstanceNotClient);
|
||||||
|
|
||||||
|
Result := FClient.PostRequest(aMsgType, aStream);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TAdvancedSingleInstance.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 TAdvancedSingleInstance.ClientSendCustomRequest(
|
||||||
|
const aMsgType: Integer; const aStream: TStream): Boolean;
|
||||||
|
begin
|
||||||
|
if not Assigned(FClient) then
|
||||||
|
raise ESingleInstance.Create(SErrSingleInstanceNotClient);
|
||||||
|
|
||||||
|
Result := FClient.SendRequest(aMsgType, aStream, TimeOutMessages);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAdvancedSingleInstance.ClientSendCustomRequest(
|
||||||
|
const aMsgType: Integer; const aStream: TStream; out
|
||||||
|
outRequestID: Integer): Boolean;
|
||||||
|
begin
|
||||||
|
if not Assigned(FClient) then
|
||||||
|
raise ESingleInstance.Create(SErrSingleInstanceNotClient);
|
||||||
|
|
||||||
|
Result := FClient.SendRequest(aMsgType, aStream, TimeOutMessages, outRequestID);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TAdvancedSingleInstance.DoServerReceivedCustomRequest(
|
||||||
|
const aMsgID: Integer; const aMsgType: Integer; const aStream: TStream);
|
||||||
|
begin
|
||||||
|
if Assigned(FOnServerReceivedCustomRequest) then
|
||||||
|
FOnServerReceivedCustomRequest(Self, aMsgID, aMsgType, aStream);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAdvancedSingleInstance.GetIsClient: Boolean;
|
||||||
|
begin
|
||||||
|
Result := Assigned(FClient);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAdvancedSingleInstance.GetIsServer: Boolean;
|
||||||
|
begin
|
||||||
|
Result := Assigned(FServer);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAdvancedSingleInstance.GetStartResult: TSingleInstanceStart;
|
||||||
|
begin
|
||||||
|
if not(Assigned(FServer) or Assigned(FClient)) then
|
||||||
|
raise ESingleInstance.Create(SErrSingleInstanceStartResultNotAvailable);
|
||||||
|
|
||||||
|
Result := inherited GetStartResult;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TAdvancedSingleInstance.ServerCheckMessages;
|
||||||
|
var
|
||||||
|
xMsgID: Integer;
|
||||||
|
xMsgType: Integer;
|
||||||
|
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 TAdvancedSingleInstance.ServerPostCustomResponse(
|
||||||
|
const aRequestID: Integer; const aMsgType: Integer;
|
||||||
|
const aStream: TStream);
|
||||||
|
begin
|
||||||
|
if not Assigned(FServer) then
|
||||||
|
raise ESingleInstance.Create(SErrSingleInstanceNotServer);
|
||||||
|
|
||||||
|
FServer.PostResponse(aRequestID, aMsgType, aStream);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TAdvancedSingleInstance.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 TAdvancedSingleInstance.SetID(const aID: string);
|
||||||
|
begin
|
||||||
|
if FID = aID then Exit;
|
||||||
|
if Assigned(FServer) or Assigned(FClient) then
|
||||||
|
raise ESingleInstance.Create(SErrSetSingleInstanceIDStarted);
|
||||||
|
FID := aID;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TAdvancedSingleInstance.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(TimeOutWaitForInstances);
|
||||||
|
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(GetProcessID)) and $3F));//limit to $3F (63)
|
||||||
|
bServerStarted := FServer.StartServer(False) and (FServer.GetPendingRequestCount > 0);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
{$ENDIF}
|
||||||
|
var
|
||||||
|
xStream: TStream;
|
||||||
|
xMsgType: Integer;
|
||||||
|
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, TimeOutMessages) then
|
||||||
|
Result := siClient
|
||||||
|
else
|
||||||
|
Result := siNotResponding;
|
||||||
|
finally
|
||||||
|
xStream.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
SetStartResult(Result);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TAdvancedSingleInstance.Stop;
|
||||||
|
begin
|
||||||
|
FreeAndNil(FServer);
|
||||||
|
FreeAndNil(FClient);
|
||||||
|
end;
|
||||||
|
|
||||||
initialization
|
initialization
|
||||||
InitCriticalSection(CreateUniqueRequestCritSec);
|
InitCriticalSection(CreateUniqueRequestCritSec);
|
||||||
|
DefaultSingleInstanceClass:=TAdvancedSingleInstance;
|
||||||
|
|
||||||
finalization
|
finalization
|
||||||
DoneCriticalsection(CreateUniqueRequestCritSec);
|
DoneCriticalsection(CreateUniqueRequestCritSec);
|
||||||
|
@ -25,16 +25,15 @@ Type
|
|||||||
TExceptionEvent = Procedure (Sender : TObject; E : Exception) Of Object;
|
TExceptionEvent = Procedure (Sender : TObject; E : Exception) Of Object;
|
||||||
TEventLogTypes = Set of TEventType;
|
TEventLogTypes = Set of TEventType;
|
||||||
|
|
||||||
TCustomApplication = Class;
|
|
||||||
TCustomSingleInstance = Class;
|
|
||||||
|
|
||||||
{ TCustomApplication }
|
{ TCustomApplication }
|
||||||
|
|
||||||
TCustomApplication = Class(TComponent)
|
TCustomApplication = Class(TComponent)
|
||||||
Private
|
Private
|
||||||
FEventLogFilter: TEventLogTypes;
|
FEventLogFilter: TEventLogTypes;
|
||||||
FOnException: TExceptionEvent;
|
FOnException: TExceptionEvent;
|
||||||
FSingleInstance: TCustomSingleInstance;
|
FSingleInstance: TBaseSingleInstance;
|
||||||
|
FSingleInstanceClass: TBaseSingleInstanceClass; // set before FSingleInstance is created
|
||||||
|
FSingleInstanceEnabled: Boolean; // set before Initialize is called
|
||||||
FTerminated : Boolean;
|
FTerminated : Boolean;
|
||||||
FHelpFile,
|
FHelpFile,
|
||||||
FTitle : String;
|
FTitle : String;
|
||||||
@ -44,6 +43,9 @@ Type
|
|||||||
function GetEnvironmentVar(VarName : String): String;
|
function GetEnvironmentVar(VarName : String): String;
|
||||||
function GetExeName: string;
|
function GetExeName: string;
|
||||||
Function GetLocation : String;
|
Function GetLocation : String;
|
||||||
|
function GetSingleInstance: TBaseSingleInstance;
|
||||||
|
procedure SetSingleInstanceClass(
|
||||||
|
const ASingleInstanceClass: TBaseSingleInstanceClass);
|
||||||
function GetTitle: string;
|
function GetTitle: string;
|
||||||
Protected
|
Protected
|
||||||
function GetOptionAtIndex(AIndex: Integer; IsLong: Boolean): String;
|
function GetOptionAtIndex(AIndex: Integer; IsLong: Boolean): String;
|
||||||
@ -96,15 +98,9 @@ Type
|
|||||||
Property CaseSensitiveOptions : Boolean Read FCaseSensitiveOptions Write FCaseSensitiveOptions;
|
Property CaseSensitiveOptions : Boolean Read FCaseSensitiveOptions Write FCaseSensitiveOptions;
|
||||||
Property StopOnException : Boolean Read FStopOnException Write FStopOnException;
|
Property StopOnException : Boolean Read FStopOnException Write FStopOnException;
|
||||||
Property EventLogFilter : TEventLogTypes Read FEventLogFilter Write FEventLogFilter;
|
Property EventLogFilter : TEventLogTypes Read FEventLogFilter Write FEventLogFilter;
|
||||||
Property SingleInstance: TCustomSingleInstance read FSingleInstance;
|
Property SingleInstance: TBaseSingleInstance read GetSingleInstance;
|
||||||
end;
|
Property SingleInstanceClass: TBaseSingleInstanceClass read FSingleInstanceClass write SetSingleInstanceClass;
|
||||||
|
Property SingleInstanceEnabled: Boolean read FSingleInstanceEnabled write FSingleInstanceEnabled;
|
||||||
TCustomSingleInstance = class(TBaseSingleInstance)
|
|
||||||
private
|
|
||||||
FEnabled: Boolean;
|
|
||||||
public
|
|
||||||
//you must set Enabled before CustomApplication.Initialize
|
|
||||||
property Enabled: Boolean read FEnabled write FEnabled;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
var CustomApplication : TCustomApplication = nil;
|
var CustomApplication : TCustomApplication = nil;
|
||||||
@ -235,6 +231,17 @@ begin
|
|||||||
Result:=ParamStr(Index);
|
Result:=ParamStr(Index);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TCustomApplication.GetSingleInstance: TBaseSingleInstance;
|
||||||
|
begin
|
||||||
|
if FSingleInstance = nil then
|
||||||
|
begin
|
||||||
|
if FSingleInstanceClass=Nil then
|
||||||
|
Raise ESingleInstance.Create('No single instance provider class set! Include a single-instance class unit such as advsingleinstance');
|
||||||
|
FSingleInstance := FSingleInstanceClass.Create(Self);
|
||||||
|
end;
|
||||||
|
Result := FSingleInstance;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TCustomApplication.SetTitle(const AValue: string);
|
procedure TCustomApplication.SetTitle(const AValue: string);
|
||||||
begin
|
begin
|
||||||
FTitle:=AValue;
|
FTitle:=AValue;
|
||||||
@ -247,8 +254,9 @@ end;
|
|||||||
|
|
||||||
procedure TCustomApplication.DoRun;
|
procedure TCustomApplication.DoRun;
|
||||||
begin
|
begin
|
||||||
if FSingleInstance.IsServer then
|
if Assigned(FSingleInstance) then
|
||||||
FSingleInstance.ServerCheckMessages;
|
if FSingleInstance.IsServer then
|
||||||
|
FSingleInstance.ServerCheckMessages;
|
||||||
|
|
||||||
// Override in descendent classes.
|
// Override in descendent classes.
|
||||||
end;
|
end;
|
||||||
@ -283,7 +291,7 @@ begin
|
|||||||
FOptionChar:='-';
|
FOptionChar:='-';
|
||||||
FCaseSensitiveOptions:=True;
|
FCaseSensitiveOptions:=True;
|
||||||
FStopOnException:=False;
|
FStopOnException:=False;
|
||||||
FSingleInstance := TCustomSingleInstance.Create(Self);
|
FSingleInstanceClass := DefaultSingleInstanceClass;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TCustomApplication.Destroy;
|
destructor TCustomApplication.Destroy;
|
||||||
@ -310,12 +318,12 @@ end;
|
|||||||
procedure TCustomApplication.Initialize;
|
procedure TCustomApplication.Initialize;
|
||||||
begin
|
begin
|
||||||
FTerminated:=False;
|
FTerminated:=False;
|
||||||
if FSingleInstance.Enabled then
|
if FSingleInstanceEnabled then
|
||||||
begin
|
begin
|
||||||
case FSingleInstance.Start of
|
case SingleInstance.Start of
|
||||||
siClient:
|
siClient:
|
||||||
begin
|
begin
|
||||||
FSingleInstance.ClientPostParams;
|
SingleInstance.ClientPostParams;
|
||||||
FTerminated:=True;
|
FTerminated:=True;
|
||||||
end;
|
end;
|
||||||
siNotResponding:
|
siNotResponding:
|
||||||
@ -336,6 +344,13 @@ begin
|
|||||||
Until FTerminated;
|
Until FTerminated;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TCustomApplication.SetSingleInstanceClass(
|
||||||
|
const ASingleInstanceClass: TBaseSingleInstanceClass);
|
||||||
|
begin
|
||||||
|
Assert((FSingleInstance = nil) and (ASingleInstanceClass <> nil));
|
||||||
|
FSingleInstanceClass := ASingleInstanceClass;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TCustomApplication.ShowException(E: Exception);
|
procedure TCustomApplication.ShowException(E: Exception);
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
@ -19,7 +19,7 @@ unit singleinstance;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
SysUtils, Classes, advancedipc;
|
SysUtils, Classes;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
@ -29,187 +29,58 @@ type
|
|||||||
//siClient: There is another instance running. This instance is used as client.
|
//siClient: There is another instance running. This instance is used as client.
|
||||||
//siNotResponding: There is another instance running but it doesn't respond.
|
//siNotResponding: There is another instance running but it doesn't respond.
|
||||||
TSingleInstanceStart = (siServer, siClient, siNotResponding);
|
TSingleInstanceStart = (siServer, siClient, siNotResponding);
|
||||||
TSingleInstanceParams = procedure(Sender: TBaseSingleInstance; Params: TStringList) of object;
|
TSingleInstanceParamsEvent = procedure(Sender: TBaseSingleInstance; Params: TStringList) of object;
|
||||||
TSingleInstanceReceivedCustomMessage = procedure(Sender: TBaseSingleInstance; MsgID: Integer; MsgType: TMessageType; MsgData: TStream) of object;
|
|
||||||
TBaseSingleInstance = class(TComponent)
|
TBaseSingleInstance = class(TComponent)
|
||||||
private
|
private
|
||||||
FGlobal: Boolean;
|
|
||||||
FID: string;
|
|
||||||
FServer: TIPCServer;
|
|
||||||
FClient: TIPCClient;
|
|
||||||
FStartResult: TSingleInstanceStart;
|
FStartResult: TSingleInstanceStart;
|
||||||
FTimeOutMessages: Integer;
|
FTimeOutMessages: Integer;
|
||||||
FTimeOutWaitForInstances: Integer;
|
FTimeOutWaitForInstances: Integer;
|
||||||
FOnServerReceivedCustomRequest: TSingleInstanceReceivedCustomMessage;
|
FOnServerReceivedParams: TSingleInstanceParamsEvent;
|
||||||
FOnServerReceivedParams: TSingleInstanceParams;
|
Protected
|
||||||
function GetIsClient: Boolean;
|
function GetIsClient: Boolean; virtual; abstract;
|
||||||
function GetIsServer: Boolean;
|
function GetIsServer: Boolean; virtual; abstract;
|
||||||
function GetStartResult: TSingleInstanceStart;
|
function GetStartResult: TSingleInstanceStart; virtual;
|
||||||
procedure SetGlobal(const aGlobal: Boolean);
|
|
||||||
procedure SetID(const aID: string);
|
|
||||||
procedure DoServerReceivedParams(const aParamsDelimitedText: string);
|
procedure DoServerReceivedParams(const aParamsDelimitedText: string);
|
||||||
procedure DoServerReceivedCustomRequest(const aMsgID: Integer; const aMsgType: TMessageType; const aStream: TStream);
|
Procedure SetStartResult(AValue : TSingleInstanceStart);
|
||||||
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
|
public
|
||||||
constructor Create(aOwner: TComponent); override;
|
constructor Create(aOwner: TComponent); override;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
public
|
public
|
||||||
function ClientPostCustomRequest(const aMsgType: TMessageType; const aStream: TStream): Integer;
|
//call Start when you want to start single instance checking
|
||||||
function ClientSendCustomRequest(const aMsgType: TMessageType; const aStream: TStream): Boolean; overload;
|
function Start: TSingleInstanceStart; virtual; abstract;
|
||||||
function ClientSendCustomRequest(const aMsgType: TMessageType; const aStream: TStream; out outRequestID: Integer): Boolean; overload;
|
//stop single instance server or client
|
||||||
procedure ServerPostCustomResponse(const aRequestID: Integer; const aMsgType: TMessageType; const aStream: TStream);
|
procedure Stop; virtual; abstract;
|
||||||
function ClientPeekCustomResponse(const aStream: TStream; out outMsgType: TMessageType): Boolean;
|
|
||||||
|
//check and handle pending messages on server
|
||||||
|
procedure ServerCheckMessages; virtual; abstract;
|
||||||
|
//post cmd parameters from client to server
|
||||||
|
procedure ClientPostParams; virtual; abstract;
|
||||||
public
|
public
|
||||||
property ID: string read FID write SetID;
|
|
||||||
property Global: Boolean read FGlobal write SetGlobal;
|
|
||||||
property TimeOutMessages: Integer read FTimeOutMessages write FTimeOutMessages;
|
property TimeOutMessages: Integer read FTimeOutMessages write FTimeOutMessages;
|
||||||
property TimeOutWaitForInstances: Integer read FTimeOutWaitForInstances write FTimeOutWaitForInstances;
|
property TimeOutWaitForInstances: Integer read FTimeOutWaitForInstances write FTimeOutWaitForInstances;
|
||||||
property OnServerReceivedParams: TSingleInstanceParams read FOnServerReceivedParams write FOnServerReceivedParams;
|
property OnServerReceivedParams: TSingleInstanceParamsEvent read FOnServerReceivedParams write FOnServerReceivedParams;
|
||||||
property OnServerReceivedCustomRequest: TSingleInstanceReceivedCustomMessage read FOnServerReceivedCustomRequest write FOnServerReceivedCustomRequest;
|
|
||||||
public
|
public
|
||||||
property StartResult: TSingleInstanceStart read GetStartResult;
|
property StartResult: TSingleInstanceStart read GetStartResult;
|
||||||
property IsServer: Boolean read GetIsServer;
|
property IsServer: Boolean read GetIsServer;
|
||||||
property IsClient: Boolean read GetIsClient;
|
property IsClient: Boolean read GetIsClient;
|
||||||
end;
|
end;
|
||||||
|
TBaseSingleInstanceClass = class of TBaseSingleInstance;
|
||||||
TSingleInstance = class(TBaseSingleInstance)
|
|
||||||
public
|
|
||||||
function Start: TSingleInstanceStart;
|
|
||||||
procedure Stop;
|
|
||||||
|
|
||||||
procedure ServerCheckMessages;
|
|
||||||
procedure ClientPostParams;
|
|
||||||
end;
|
|
||||||
|
|
||||||
ESingleInstance = class(Exception);
|
ESingleInstance = class(Exception);
|
||||||
|
|
||||||
|
Var
|
||||||
|
DefaultSingleInstanceClass : TBaseSingleInstanceClass = Nil;
|
||||||
|
|
||||||
implementation
|
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 }
|
{ 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);
|
constructor TBaseSingleInstance.Create(aOwner: TComponent);
|
||||||
var
|
|
||||||
xID: RawByteString;
|
|
||||||
I: Integer;
|
|
||||||
begin
|
begin
|
||||||
inherited Create(aOwner);
|
inherited Create(aOwner);
|
||||||
|
|
||||||
FTimeOutMessages := 1000;
|
FTimeOutMessages := 1000;
|
||||||
FTimeOutWaitForInstances := 100;
|
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;
|
end;
|
||||||
|
|
||||||
destructor TBaseSingleInstance.Destroy;
|
destructor TBaseSingleInstance.Destroy;
|
||||||
@ -219,13 +90,6 @@ begin
|
|||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
end;
|
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(
|
procedure TBaseSingleInstance.DoServerReceivedParams(
|
||||||
const aParamsDelimitedText: string);
|
const aParamsDelimitedText: string);
|
||||||
var
|
var
|
||||||
@ -243,177 +107,16 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TBaseSingleInstance.GetIsClient: Boolean;
|
|
||||||
begin
|
|
||||||
Result := Assigned(FClient);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TBaseSingleInstance.GetIsServer: Boolean;
|
|
||||||
begin
|
|
||||||
Result := Assigned(FServer);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TBaseSingleInstance.GetStartResult: TSingleInstanceStart;
|
function TBaseSingleInstance.GetStartResult: TSingleInstanceStart;
|
||||||
begin
|
begin
|
||||||
if not(Assigned(FServer) or Assigned(FClient)) then
|
|
||||||
raise ESingleInstance.Create(SErrSingleInstanceStartResultNotAvailable);
|
|
||||||
|
|
||||||
Result := FStartResult;
|
Result := FStartResult;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TBaseSingleInstance.ServerCheckMessages;
|
Procedure TBaseSingleInstance.SetStartResult(AValue : TSingleInstanceStart);
|
||||||
var
|
|
||||||
xMsgID: Integer;
|
|
||||||
xMsgType: TMessageType;
|
|
||||||
xStream: TStream;
|
|
||||||
xStringStream: TStringStream;
|
|
||||||
begin
|
begin
|
||||||
if not Assigned(FServer) then
|
FStartResult:=AValue;
|
||||||
raise ESingleInstance.Create(SErrSingleInstanceNotServer);
|
end;
|
||||||
|
|
||||||
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.
|
end.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user