Fixed: D7 compatibility
Fixed: SingleInstance global/local ondersteuning + refactoring
This commit is contained in:
parent
af655e8083
commit
59d62f8d8c
@ -1,5 +1,7 @@
|
|||||||
unit HashesTest;
|
unit HashesTest;
|
||||||
|
|
||||||
|
{$I X2UtCompilerVersion.inc}
|
||||||
|
|
||||||
interface
|
interface
|
||||||
uses
|
uses
|
||||||
TestFramework,
|
TestFramework,
|
||||||
@ -145,6 +147,7 @@ var
|
|||||||
sKey: String;
|
sKey: String;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
{$IFDEF D2006}
|
||||||
FillTestItems();
|
FillTestItems();
|
||||||
FillChar(aPresent, SizeOf(aPresent), #0);
|
FillChar(aPresent, SizeOf(aPresent), #0);
|
||||||
|
|
||||||
@ -161,6 +164,7 @@ begin
|
|||||||
CheckTrue(aPresent[1], 'Key1 was not in the enumeration!');
|
CheckTrue(aPresent[1], 'Key1 was not in the enumeration!');
|
||||||
CheckTrue(aPresent[2], 'Key2 was not in the enumeration!');
|
CheckTrue(aPresent[2], 'Key2 was not in the enumeration!');
|
||||||
CheckTrue(aPresent[3], 'Key3 was not in the enumeration!');
|
CheckTrue(aPresent[3], 'Key3 was not in the enumeration!');
|
||||||
|
{$ENDIF}
|
||||||
|
|
||||||
{ Not supported yet, maybe in the future.
|
{ Not supported yet, maybe in the future.
|
||||||
FillChar(aPresent, SizeOf(aPresent), #0);
|
FillChar(aPresent, SizeOf(aPresent), #0);
|
||||||
@ -245,6 +249,7 @@ var
|
|||||||
pKey: Pointer;
|
pKey: Pointer;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
{$IFDEF D2006}
|
||||||
FillTestItems();
|
FillTestItems();
|
||||||
FillChar(aPresent, SizeOf(aPresent), #0);
|
FillChar(aPresent, SizeOf(aPresent), #0);
|
||||||
|
|
||||||
@ -254,6 +259,7 @@ begin
|
|||||||
CheckTrue(aPresent[0], 'Key1 was not in the enumeration!');
|
CheckTrue(aPresent[0], 'Key1 was not in the enumeration!');
|
||||||
CheckTrue(aPresent[1], 'Key2 was not in the enumeration!');
|
CheckTrue(aPresent[1], 'Key2 was not in the enumeration!');
|
||||||
CheckTrue(aPresent[2], 'Key3 was not in the enumeration!');
|
CheckTrue(aPresent[2], 'Key3 was not in the enumeration!');
|
||||||
|
{$ENDIF}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure THashesPOTest.testIterate;
|
procedure THashesPOTest.testIterate;
|
||||||
|
@ -32,12 +32,12 @@
|
|||||||
-M
|
-M
|
||||||
-$M16384,1048576
|
-$M16384,1048576
|
||||||
-K$00400000
|
-K$00400000
|
||||||
-LE"C:\Documents and Settings\PsychoMark\My Documents\Borland Studio Projects\Bpl"
|
-LE"c:\program files\borland\delphi7\Projects\Bpl"
|
||||||
-LN"C:\Documents and Settings\PsychoMark\My Documents\Borland Studio Projects\Bpl"
|
-LN"c:\program files\borland\delphi7\Projects\Bpl"
|
||||||
-U"..\"
|
-U".."
|
||||||
-O"..\"
|
-O".."
|
||||||
-I"..\"
|
-I".."
|
||||||
-R"..\"
|
-R".."
|
||||||
-w-SYMBOL_PLATFORM
|
-w-SYMBOL_PLATFORM
|
||||||
-w-UNIT_PLATFORM
|
-w-UNIT_PLATFORM
|
||||||
-w-UNSAFE_TYPE
|
-w-UNSAFE_TYPE
|
||||||
|
@ -94,7 +94,7 @@ OutputDir=
|
|||||||
UnitOutputDir=
|
UnitOutputDir=
|
||||||
PackageDLLOutputDir=
|
PackageDLLOutputDir=
|
||||||
PackageDCPOutputDir=
|
PackageDCPOutputDir=
|
||||||
SearchPath=
|
SearchPath=..
|
||||||
Packages=vcl;rtl;dbrtl;vcldb;vclx;dss;dsnapcrba;dsnapcon;inetdb;webdsnap;websnap;dbxcds;Irc;parsdpk;hotspotter
|
Packages=vcl;rtl;dbrtl;vcldb;vclx;dss;dsnapcrba;dsnapcon;inetdb;webdsnap;websnap;dbxcds;Irc;parsdpk;hotspotter
|
||||||
Conditionals=
|
Conditionals=
|
||||||
DebugSourceDirs=
|
DebugSourceDirs=
|
||||||
@ -105,6 +105,10 @@ HostApplication=
|
|||||||
Launcher=
|
Launcher=
|
||||||
UseLauncher=0
|
UseLauncher=0
|
||||||
DebugCWD=
|
DebugCWD=
|
||||||
|
[Language]
|
||||||
|
ActiveLang=
|
||||||
|
ProjectLang=
|
||||||
|
RootDir=
|
||||||
[Version Info]
|
[Version Info]
|
||||||
IncludeVerInfo=0
|
IncludeVerInfo=0
|
||||||
AutoIncBuild=0
|
AutoIncBuild=0
|
||||||
@ -130,6 +134,11 @@ OriginalFilename=
|
|||||||
ProductName=
|
ProductName=
|
||||||
ProductVersion=1.0.0.0
|
ProductVersion=1.0.0.0
|
||||||
Comments=
|
Comments=
|
||||||
|
[Excluded Packages]
|
||||||
|
C:\Program Files\Borland\Indy\D7\dclIndy70.bpl=Internet Direct (Indy) for D7 Property and Component Editors
|
||||||
[HistoryLists\hlUnitAliases]
|
[HistoryLists\hlUnitAliases]
|
||||||
Count=1
|
Count=1
|
||||||
Item0=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
|
Item0=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
|
||||||
|
[HistoryLists\hlSearchPath]
|
||||||
|
Count=1
|
||||||
|
Item0=..
|
||||||
|
@ -6,7 +6,8 @@ uses
|
|||||||
Variants,
|
Variants,
|
||||||
BitsTest in 'Units\BitsTest.pas',
|
BitsTest in 'Units\BitsTest.pas',
|
||||||
HashesTest in 'Units\HashesTest.pas',
|
HashesTest in 'Units\HashesTest.pas',
|
||||||
PersistTest in 'Units\PersistTest.pas';
|
PersistTest in 'Units\PersistTest.pas',
|
||||||
|
X2UtSingleInstance in '..\X2UtSingleInstance.pas';
|
||||||
|
|
||||||
//SettingsTest in 'Units\SettingsTest.pas',
|
//SettingsTest in 'Units\SettingsTest.pas',
|
||||||
//IniParserTest in 'Units\IniParserTest.pas';
|
//IniParserTest in 'Units\IniParserTest.pas';
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
}
|
}
|
||||||
unit X2UtHashes;
|
unit X2UtHashes;
|
||||||
|
|
||||||
|
{$I X2UtCompilerVersion.inc}
|
||||||
|
|
||||||
interface
|
interface
|
||||||
uses
|
uses
|
||||||
Classes,
|
Classes,
|
||||||
@ -43,7 +45,10 @@ type
|
|||||||
// Forward declarations
|
// Forward declarations
|
||||||
TX2CustomHash = class;
|
TX2CustomHash = class;
|
||||||
|
|
||||||
|
|
||||||
|
{$IFDEF D2005}
|
||||||
{$REGION 'Internal hash structures'}
|
{$REGION 'Internal hash structures'}
|
||||||
|
{$ENDIF}
|
||||||
{
|
{
|
||||||
:$ Internal representation of a hash item.
|
:$ Internal representation of a hash item.
|
||||||
}
|
}
|
||||||
@ -92,9 +97,11 @@ type
|
|||||||
|
|
||||||
property Current: PX2HashValue read GetCurrent;
|
property Current: PX2HashValue read GetCurrent;
|
||||||
end;
|
end;
|
||||||
|
{$IFDEF D2005}
|
||||||
{$ENDREGION}
|
{$ENDREGION}
|
||||||
|
|
||||||
{$REGION 'Internal value managers'}
|
{$REGION 'Internal value managers'}
|
||||||
|
{$ENDIF}
|
||||||
{
|
{
|
||||||
:$ Base value manager.
|
:$ Base value manager.
|
||||||
}
|
}
|
||||||
@ -161,9 +168,11 @@ type
|
|||||||
function Compare(const AData: Pointer; const AValue: Pointer;
|
function Compare(const AData: Pointer; const AValue: Pointer;
|
||||||
const ASize: Cardinal): Boolean; override;
|
const ASize: Cardinal): Boolean; override;
|
||||||
end;
|
end;
|
||||||
|
{$IFDEF D2005}
|
||||||
{$ENDREGION}
|
{$ENDREGION}
|
||||||
|
|
||||||
{$REGION 'Delphi 2006 enumerator support'}
|
{$REGION 'Delphi 2006 enumerator support'}
|
||||||
|
{$ENDIF}
|
||||||
{
|
{
|
||||||
:$ Base enumerator class.
|
:$ Base enumerator class.
|
||||||
}
|
}
|
||||||
@ -224,9 +233,11 @@ type
|
|||||||
public
|
public
|
||||||
property Current: String read GetCurrent;
|
property Current: String read GetCurrent;
|
||||||
end;
|
end;
|
||||||
|
{$IFDEF D2005}
|
||||||
{$ENDREGION}
|
{$ENDREGION}
|
||||||
|
|
||||||
{$REGION 'Abstract hash implementation'}
|
{$REGION 'Abstract hash implementation'}
|
||||||
|
{$ENDIF}
|
||||||
{
|
{
|
||||||
:$ Hash implementation.
|
:$ Hash implementation.
|
||||||
}
|
}
|
||||||
@ -277,9 +288,11 @@ type
|
|||||||
|
|
||||||
property Count: Integer read FCount;
|
property Count: Integer read FCount;
|
||||||
end;
|
end;
|
||||||
|
{$IFDEF D2005}
|
||||||
{$ENDREGION}
|
{$ENDREGION}
|
||||||
|
|
||||||
{$REGION 'Base hash classes'}
|
{$REGION 'Base hash classes'}
|
||||||
|
{$ENDIF}
|
||||||
{
|
{
|
||||||
:$ Base hash implementation for pointer keys.
|
:$ Base hash implementation for pointer keys.
|
||||||
}
|
}
|
||||||
@ -355,9 +368,11 @@ type
|
|||||||
|
|
||||||
property CurrentKey: String read GetCurrentKey;
|
property CurrentKey: String read GetCurrentKey;
|
||||||
end;
|
end;
|
||||||
|
{$IFDEF D2005}
|
||||||
{$ENDREGION}
|
{$ENDREGION}
|
||||||
|
|
||||||
{$REGION 'Concrete hash classes'}
|
{$REGION 'Concrete hash classes'}
|
||||||
|
{$ENDIF}
|
||||||
{
|
{
|
||||||
:$ Pointer-to-Pointer hash.
|
:$ Pointer-to-Pointer hash.
|
||||||
}
|
}
|
||||||
@ -617,7 +632,9 @@ type
|
|||||||
property CurrentValue: String read GetCurrentValue;
|
property CurrentValue: String read GetCurrentValue;
|
||||||
property Values[Key: String]: String read GetValue write SetValue; default;
|
property Values[Key: String]: String read GetValue write SetValue; default;
|
||||||
end;
|
end;
|
||||||
|
{$IFDEF D2005}
|
||||||
{$ENDREGION}
|
{$ENDREGION}
|
||||||
|
{$ENDIF}
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
const
|
const
|
||||||
@ -672,7 +689,9 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{$IFDEF D2005}
|
||||||
{$REGION 'Internal hash structures'}
|
{$REGION 'Internal hash structures'}
|
||||||
|
{$ENDIF}
|
||||||
{========================================
|
{========================================
|
||||||
TX2HashCursor
|
TX2HashCursor
|
||||||
========================================}
|
========================================}
|
||||||
@ -774,10 +793,12 @@ begin
|
|||||||
break;
|
break;
|
||||||
until False;
|
until False;
|
||||||
end;
|
end;
|
||||||
|
{$IFDEF D2005}
|
||||||
{$ENDREGION}
|
{$ENDREGION}
|
||||||
|
|
||||||
|
|
||||||
{$REGION 'Internal value managers'}
|
{$REGION 'Internal value managers'}
|
||||||
|
{$ENDIF}
|
||||||
{========================================
|
{========================================
|
||||||
TX2CustomHashManager
|
TX2CustomHashManager
|
||||||
========================================}
|
========================================}
|
||||||
@ -948,10 +969,12 @@ begin
|
|||||||
|
|
||||||
Result := CompareMem(pSource, AValue, ASize);
|
Result := CompareMem(pSource, AValue, ASize);
|
||||||
end;
|
end;
|
||||||
|
{$IFDEF D2005}
|
||||||
{$ENDREGION}
|
{$ENDREGION}
|
||||||
|
|
||||||
|
|
||||||
{$REGION 'Abstract hash implementation'}
|
{$REGION 'Abstract hash implementation'}
|
||||||
|
{$ENDIF}
|
||||||
{========================== TX2CustomHash
|
{========================== TX2CustomHash
|
||||||
Initialization
|
Initialization
|
||||||
========================================}
|
========================================}
|
||||||
@ -1285,10 +1308,12 @@ begin
|
|||||||
|
|
||||||
Result := Cursor.Next();
|
Result := Cursor.Next();
|
||||||
end;
|
end;
|
||||||
|
{$IFDEF D2005}
|
||||||
{$ENDREGION}
|
{$ENDREGION}
|
||||||
|
|
||||||
|
|
||||||
{$REGION 'Delphi 2006 enumerator support'}
|
{$REGION 'Delphi 2006 enumerator support'}
|
||||||
|
{$ENDIF}
|
||||||
{========================================
|
{========================================
|
||||||
TX2HashEnumerator
|
TX2HashEnumerator
|
||||||
========================================}
|
========================================}
|
||||||
@ -1352,10 +1377,12 @@ function TX2HashStringEnumerator.GetCurrent(): String;
|
|||||||
begin
|
begin
|
||||||
Result := TX2HashStringManager(Manager).ToValue(Cursor);
|
Result := TX2HashStringManager(Manager).ToValue(Cursor);
|
||||||
end;
|
end;
|
||||||
|
{$IFDEF D2005}
|
||||||
{$ENDREGION}
|
{$ENDREGION}
|
||||||
|
|
||||||
|
|
||||||
{$REGION 'Base hash classes'}
|
{$REGION 'Base hash classes'}
|
||||||
|
{$ENDIF}
|
||||||
{========================================
|
{========================================
|
||||||
TX2CustomPointerHash
|
TX2CustomPointerHash
|
||||||
========================================}
|
========================================}
|
||||||
@ -1499,10 +1526,12 @@ function TX2CustomStringHash.Delete(const AKey: String): Boolean;
|
|||||||
begin
|
begin
|
||||||
Result := inherited Delete(PChar(AKey), Length(AKey));
|
Result := inherited Delete(PChar(AKey), Length(AKey));
|
||||||
end;
|
end;
|
||||||
|
{$IFDEF D2005}
|
||||||
{$ENDREGION}
|
{$ENDREGION}
|
||||||
|
|
||||||
|
|
||||||
{$REGION 'Concrete hash classes'}
|
{$REGION 'Concrete hash classes'}
|
||||||
|
{$ENDIF}
|
||||||
{========================================
|
{========================================
|
||||||
TX2PPHash
|
TX2PPHash
|
||||||
========================================}
|
========================================}
|
||||||
@ -2058,7 +2087,9 @@ begin
|
|||||||
inherited SetValue(Find(Key, True),
|
inherited SetValue(Find(Key, True),
|
||||||
TX2HashStringManager(ValueManager).ToPointer(Value));
|
TX2HashStringManager(ValueManager).ToPointer(Value));
|
||||||
end;
|
end;
|
||||||
|
{$IFDEF D2005}
|
||||||
{$ENDREGION}
|
{$ENDREGION}
|
||||||
|
{$ENDIF}
|
||||||
|
|
||||||
|
|
||||||
initialization
|
initialization
|
||||||
|
@ -10,83 +10,408 @@ unit X2UtSingleInstance;
|
|||||||
|
|
||||||
interface
|
interface
|
||||||
uses
|
uses
|
||||||
SysUtils;
|
Classes,
|
||||||
|
SysUtils,
|
||||||
|
Messages;
|
||||||
|
|
||||||
|
const
|
||||||
|
IM_COMMANDLINE = $00000001;
|
||||||
|
IM_APP = $00000100;
|
||||||
|
|
||||||
type
|
type
|
||||||
{
|
EInstanceNotActive = class(Exception);
|
||||||
:$ Notifier interface
|
EInstanceNoAppID = class(Exception);
|
||||||
|
|
||||||
:: Applications who want to receive notifications on new instances must
|
|
||||||
:: implements this interface and call RegisterInstance.
|
{
|
||||||
|
:$ Notifier observer interface.
|
||||||
|
|
||||||
|
:: Applications that want to receive notifications on new instances must
|
||||||
|
:: implement this interface and call Attach(Instance).
|
||||||
}
|
}
|
||||||
IX2InstanceNotifier = interface
|
IX2InstanceObserver = interface
|
||||||
['{4C435D46-6A7F-4CD7-9400-338E3E8FB5C6}']
|
['{4C435D46-6A7F-4CD7-9400-338E3E8FB5C6}']
|
||||||
procedure OnInstance(const ACmdLine: String);
|
procedure OnInstance(const ACmdLine: String);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
//:$ Checks for a previous instance of the application
|
{
|
||||||
//:: Returns False if a previous instance was found, True if this is the
|
:$ Extended notifier observer interface.
|
||||||
//:: first registered instance. ApplicationID must be unique to prevent
|
|
||||||
//:: application conflicts, usage of a generated GUID is recommended.
|
|
||||||
//:! Set ANotify to False if you're using SingleInstance in a console
|
|
||||||
//:! application without a message loop.
|
|
||||||
function SingleInstance(const AApplicationID: String;
|
|
||||||
const ANotify: Boolean = True): Boolean;
|
|
||||||
|
|
||||||
//:$ Registers the instance for notifications
|
:: Applications that want to receive custom notifications as well must
|
||||||
|
:: implement this interface and call Attach(Instance).
|
||||||
|
}
|
||||||
|
IX2InstanceObserverEx = interface(IX2InstanceObserver)
|
||||||
|
['{755A6548-3EA8-46C2-9FF5-FDE4BD67B699}']
|
||||||
|
procedure OnNotify(AMessage: Integer; const AData: String);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
:$ Internal file mapping layout.
|
||||||
|
}
|
||||||
|
PX2InstanceMapData = ^TX2InstanceMapData;
|
||||||
|
TX2InstanceMapData = record
|
||||||
|
RefCount: Integer;
|
||||||
|
Window: THandle;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{
|
||||||
|
:$ Instance object.
|
||||||
|
|
||||||
|
:: Manages an instance. Instances are identified by an ApplicationID, which
|
||||||
|
:: must be unique. For simple single instance checking you can use the
|
||||||
|
:: SingleInstance wrapper function.
|
||||||
|
}
|
||||||
|
TX2Instance = class(TObject)
|
||||||
|
private
|
||||||
|
FActive: Boolean;
|
||||||
|
FApplicationID: String;
|
||||||
|
FFirstInstance: Boolean;
|
||||||
|
FGlobal: Boolean;
|
||||||
|
|
||||||
|
FFileMapData: PX2InstanceMapData;
|
||||||
|
FFileMapping: THandle;
|
||||||
|
FObservers: TInterfaceList;
|
||||||
|
protected
|
||||||
|
function GetCount(): Integer; virtual;
|
||||||
|
procedure SetApplicationID(const Value: String); virtual;
|
||||||
|
procedure SetGlobal(const Value: Boolean); virtual;
|
||||||
|
procedure SetActive(const Value: Boolean); virtual;
|
||||||
|
|
||||||
|
procedure WindowProc(var Message: TMessage); virtual;
|
||||||
|
|
||||||
|
property FileMapping: THandle read FFileMapping;
|
||||||
|
property FileMapData: PX2InstanceMapData read FFileMapData;
|
||||||
|
property Observers: TInterfaceList read FObservers;
|
||||||
|
public
|
||||||
|
constructor Create();
|
||||||
|
destructor Destroy(); override;
|
||||||
|
|
||||||
|
procedure Open(); virtual;
|
||||||
|
procedure Close(); virtual;
|
||||||
|
|
||||||
|
//:$ Sends a notification to the first instance.
|
||||||
|
//:! For custom messages you are recommended to start message IDs counting
|
||||||
|
//:! from IM_APP. Anything below is reserved for internal use.
|
||||||
|
procedure Notify(AMessage: Integer; const AData: String); virtual;
|
||||||
|
|
||||||
|
//:$ Registers the instance for notifications.
|
||||||
//:: If an application wants to be notified of new instances it must
|
//:: If an application wants to be notified of new instances it must
|
||||||
//:: implement the IX2InstanceNotifier and register the interface using
|
//:: implement the IInstanceNotifier and register the interface using
|
||||||
//:: this function.
|
//:: this function.
|
||||||
procedure RegisterInstance(const ANotifier: IX2InstanceNotifier);
|
procedure Attach(const ANotifier: IX2InstanceObserver);
|
||||||
|
|
||||||
//:$ Unregisters a previously registered instance
|
//:$ Unregisters a previously registered instance.
|
||||||
procedure UnregisterInstance(const ANotifier: IX2InstanceNotifier);
|
procedure Detach(const ANotifier: IX2InstanceObserver);
|
||||||
|
|
||||||
|
property Active: Boolean read FActive write SetActive;
|
||||||
|
property ApplicationID: String read FApplicationID write SetApplicationID;
|
||||||
|
property FirstInstance: Boolean read FFirstInstance;
|
||||||
|
property Global: Boolean read FGlobal write SetGlobal;
|
||||||
|
property Count: Integer read GetCount;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{
|
||||||
|
:$ Checks for a previous instance of the application.
|
||||||
|
|
||||||
|
:: Returns False if a previous instance was found, True if this is the
|
||||||
|
:: first registered instance. ApplicationID must be unique to prevent
|
||||||
|
:: application conflicts, usage of a generated GUID is recommended.
|
||||||
|
::
|
||||||
|
:: If AGlobal is True, the check is performed system-wide. This only
|
||||||
|
:: affects Terminal Services and XP Fast User Switching sessions.
|
||||||
|
::
|
||||||
|
:: This function is a wrapper for the TX2Instance object. You can access
|
||||||
|
:: the created object through the Instance function.
|
||||||
|
|
||||||
|
:! Set ANotify to False if you're using SingleInstance in a console
|
||||||
|
:! application without a message loop.
|
||||||
|
:!
|
||||||
|
:! If AGlobal is True, ANotify only works if the previous instance was
|
||||||
|
:! started by the same user.
|
||||||
|
}
|
||||||
|
function SingleInstance(const AApplicationID: String;
|
||||||
|
ANotify: Boolean = True;
|
||||||
|
AGlobal: Boolean = True): Boolean;
|
||||||
|
|
||||||
|
{
|
||||||
|
:$ Returns a singleton TX2Instance object.
|
||||||
|
|
||||||
|
:: The object is automatically configured when using the SingleInstance
|
||||||
|
:: function.
|
||||||
|
}
|
||||||
|
function Instance(): TX2Instance;
|
||||||
|
|
||||||
|
|
||||||
//:$ Works like System.ParamCount, but uses the specified string instead
|
{
|
||||||
//:$ of the actual command line
|
:$ Registers the instance for notifications
|
||||||
|
|
||||||
|
:: Calls Attach on the singleton Instance.
|
||||||
|
}
|
||||||
|
procedure AttachInstance(const ANotifier: IX2InstanceObserver);
|
||||||
|
|
||||||
|
{
|
||||||
|
:$ Unregisters a previously registered instance.
|
||||||
|
|
||||||
|
:: Calls Detach on the singleton Instance.
|
||||||
|
}
|
||||||
|
procedure DetachInstance(const ANotifier: IX2InstanceObserver);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
:$ Works like System.ParamCount, but uses the specified string instead
|
||||||
|
:$ of the actual command line.
|
||||||
|
}
|
||||||
function ParamCountEx(const ACmdLine: String): Integer;
|
function ParamCountEx(const ACmdLine: String): Integer;
|
||||||
|
|
||||||
//:$ Works like System.ParamStr, but uses the specified string instead
|
{
|
||||||
//:$ of the actual command line
|
:$ Works like System.ParamStr, but uses the specified string instead
|
||||||
|
:$ of the actual command line
|
||||||
|
}
|
||||||
function ParamStrEx(const ACmdLine: String; AIndex: Integer): String;
|
function ParamStrEx(const ACmdLine: String; AIndex: Integer): String;
|
||||||
|
|
||||||
//:$ Works like SysUtils.FindCmdLineSwitch, but uses the specified string
|
{
|
||||||
//:$ instead of the actual command line
|
:$ Works like SysUtils.FindCmdLineSwitch, but uses the specified string
|
||||||
|
:$ instead of the actual command line
|
||||||
|
}
|
||||||
function FindCmdLineSwitchEx(const ACmdLine, ASwitch: String;
|
function FindCmdLineSwitchEx(const ACmdLine, ASwitch: String;
|
||||||
const AChars: TSysCharSet;
|
const AChars: TSysCharSet;
|
||||||
const AIgnoreCase: Boolean): Boolean; overload;
|
const AIgnoreCase: Boolean): Boolean; overload;
|
||||||
|
|
||||||
//:$ Works like SysUtils.FindCmdLineSwitch, but uses the specified string
|
{
|
||||||
//:$ instead of the actual command line
|
:$ Works like SysUtils.FindCmdLineSwitch, but uses the specified string
|
||||||
|
:$ instead of the actual command line
|
||||||
|
}
|
||||||
function FindCmdLineSwitchEx(const ACmdLine, ASwitch: String): Boolean; overload;
|
function FindCmdLineSwitchEx(const ACmdLine, ASwitch: String): Boolean; overload;
|
||||||
|
|
||||||
//:$ Works like SysUtils.FindCmdLineSwitch, but uses the specified string
|
{
|
||||||
//:$ instead of the actual command line
|
:$ Works like SysUtils.FindCmdLineSwitch, but uses the specified string
|
||||||
|
:$ instead of the actual command line
|
||||||
|
}
|
||||||
function FindCmdLineSwitchEx(const ACmdLine, ASwitch: String;
|
function FindCmdLineSwitchEx(const ACmdLine, ASwitch: String;
|
||||||
const AIgnoreCase: Boolean): Boolean; overload;
|
const AIgnoreCase: Boolean): Boolean; overload;
|
||||||
|
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
uses
|
uses
|
||||||
Classes,
|
|
||||||
Messages,
|
|
||||||
Windows;
|
Windows;
|
||||||
|
|
||||||
|
|
||||||
const
|
const
|
||||||
CWindowClass = 'X2UtInstance.Window';
|
WindowClass = 'X2UtSingleInstance.Window';
|
||||||
CDataCmdLine = $1010;
|
|
||||||
|
|
||||||
var
|
var
|
||||||
GNotifiers: TInterfaceList;
|
GlobalInstance: TX2Instance;
|
||||||
GFileMapping: THandle;
|
|
||||||
GWindow: THandle;
|
|
||||||
|
|
||||||
|
|
||||||
{$WARN SYMBOL_PLATFORM OFF}
|
{$WARN SYMBOL_PLATFORM OFF}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{ TX2Instance }
|
||||||
|
constructor TX2Instance.Create();
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
|
||||||
|
FObservers := TInterfaceList.Create;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TX2Instance.Destroy();
|
||||||
|
begin
|
||||||
|
Active := False;
|
||||||
|
FreeAndNil(FObservers);
|
||||||
|
|
||||||
|
inherited;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TX2Instance.Notify(AMessage: Integer; const AData: String);
|
||||||
|
var
|
||||||
|
copyStruct: TCopyDataStruct;
|
||||||
|
|
||||||
|
begin
|
||||||
|
if not Active then
|
||||||
|
raise EInstanceNotActive.Create('Instance not Active');
|
||||||
|
|
||||||
|
if FileMapData^.Window = 0 then
|
||||||
|
Exit;
|
||||||
|
|
||||||
|
copyStruct.dwData := AMessage;
|
||||||
|
copyStruct.cbData := Length(AData);
|
||||||
|
copyStruct.lpData := PChar(AData);
|
||||||
|
|
||||||
|
SendMessage(FileMapData^.Window, WM_COPYDATA, 0, Integer(@copyStruct));
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TX2Instance.Open();
|
||||||
|
const
|
||||||
|
ScopePrefix: array[Boolean] of String = ('Local\', 'Global\');
|
||||||
|
|
||||||
|
begin
|
||||||
|
if Active then
|
||||||
|
Exit;
|
||||||
|
|
||||||
|
if Length(ApplicationID) = 0 then
|
||||||
|
raise EInstanceNoAppID.Create('ApplicationID not specified');
|
||||||
|
|
||||||
|
FFirstInstance := True;
|
||||||
|
|
||||||
|
{ Attempt to create shared memory }
|
||||||
|
SetLastError(0);
|
||||||
|
FFileMapping := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0,
|
||||||
|
SizeOf(TX2InstanceMapData),
|
||||||
|
PChar(ScopePrefix[Global] +
|
||||||
|
'SingleInstance.' + ApplicationID));
|
||||||
|
if FFileMapping = 0 then
|
||||||
|
RaiseLastOSError();
|
||||||
|
|
||||||
|
FActive := True;
|
||||||
|
try
|
||||||
|
FFirstInstance := (GetLastError() <> ERROR_ALREADY_EXISTS);
|
||||||
|
FFileMapData := MapViewOfFile(FFileMapping, FILE_MAP_WRITE, 0, 0, 0);
|
||||||
|
|
||||||
|
if not Assigned(FFileMapData) then
|
||||||
|
RaiseLastOSError();
|
||||||
|
|
||||||
|
if FFirstInstance then
|
||||||
|
begin
|
||||||
|
FileMapData^.Window := CreateWindow(WindowClass, '', 0, 0, 0, 0, 0, 0,
|
||||||
|
0, SysInit.HInstance, nil);
|
||||||
|
if FileMapData^.Window = 0 then
|
||||||
|
RaiseLastOSError();
|
||||||
|
|
||||||
|
SetWindowLong(FileMapData^.Window, GWL_WNDPROC,
|
||||||
|
Integer(MakeObjectInstance(WindowProc)));
|
||||||
|
end;
|
||||||
|
|
||||||
|
Inc(FFileMapData^.RefCount);
|
||||||
|
except
|
||||||
|
Close();
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TX2Instance.Close();
|
||||||
|
begin
|
||||||
|
if not Active then
|
||||||
|
Exit;
|
||||||
|
|
||||||
|
if Assigned(FileMapData) then
|
||||||
|
begin
|
||||||
|
Dec(FileMapData^.RefCount);
|
||||||
|
if FirstInstance then
|
||||||
|
DestroyWindow(FileMapData^.Window);
|
||||||
|
|
||||||
|
UnmapViewOfFile(FileMapData);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if FileMapping <> 0 then
|
||||||
|
CloseHandle(FileMapping);
|
||||||
|
|
||||||
|
FActive := False;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TX2Instance.Attach(const ANotifier: IX2InstanceObserver);
|
||||||
|
begin
|
||||||
|
if Observers.IndexOf(ANotifier) = -1 then
|
||||||
|
Observers.Add(ANotifier as IX2InstanceObserver);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TX2Instance.Detach(const ANotifier: IX2InstanceObserver);
|
||||||
|
begin
|
||||||
|
Observers.Remove(ANotifier as IX2InstanceObserver);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TX2Instance.WindowProc(var Message: TMessage);
|
||||||
|
var
|
||||||
|
copyData: PCopyDataStruct;
|
||||||
|
data: String;
|
||||||
|
observerIndex: Integer;
|
||||||
|
observerExIntf: IX2InstanceObserverEx;
|
||||||
|
|
||||||
|
begin
|
||||||
|
if Assigned(FileMapData) then
|
||||||
|
case Message.Msg of
|
||||||
|
WM_COPYDATA:
|
||||||
|
begin
|
||||||
|
copyData := PCopyDataStruct(Message.LParam);
|
||||||
|
data := '';
|
||||||
|
|
||||||
|
if copyData^.cbData > 0 then
|
||||||
|
SetString(data, PChar(copyData^.lpData), copyData^.cbData);
|
||||||
|
|
||||||
|
case copyData^.dwData of
|
||||||
|
IM_COMMANDLINE:
|
||||||
|
for observerIndex := 0 to Pred(Observers.Count) do
|
||||||
|
IX2InstanceObserver(Observers[observerIndex]).OnInstance(data);
|
||||||
|
else
|
||||||
|
for observerIndex := 0 to Pred(Observers.Count) do
|
||||||
|
if Supports(Observers[observerIndex], IX2InstanceObserverEx, observerExIntf) then
|
||||||
|
observerExIntf.OnNotify(copyData^.dwData, data);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
else
|
||||||
|
Message.Result := DefWindowProc(FileMapData^.Window, Message.Msg,
|
||||||
|
Message.WParam, Message.LParam);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function TX2Instance.GetCount(): Integer;
|
||||||
|
begin
|
||||||
|
Result := 0;
|
||||||
|
if Active then
|
||||||
|
Result := FileMapData^.RefCount;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TX2Instance.SetActive(const Value: Boolean);
|
||||||
|
begin
|
||||||
|
if Value then
|
||||||
|
Open
|
||||||
|
else
|
||||||
|
Close;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TX2Instance.SetApplicationID(const Value: String);
|
||||||
|
var
|
||||||
|
wasActive: Boolean;
|
||||||
|
|
||||||
|
begin
|
||||||
|
if Value <> FApplicationID then
|
||||||
|
begin
|
||||||
|
wasActive := Active;
|
||||||
|
Active := False;
|
||||||
|
|
||||||
|
FApplicationID := Value;
|
||||||
|
|
||||||
|
Active := wasActive;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TX2Instance.SetGlobal(const Value: Boolean);
|
||||||
|
var
|
||||||
|
wasActive: Boolean;
|
||||||
|
|
||||||
|
begin
|
||||||
|
if Value <> FGlobal then
|
||||||
|
begin
|
||||||
|
wasActive := Active;
|
||||||
|
Active := False;
|
||||||
|
|
||||||
|
FGlobal := Value;
|
||||||
|
|
||||||
|
Active := wasActive;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Copied from System unit because Borland didn't make it public
|
// Copied from System unit because Borland didn't make it public
|
||||||
function GetParamStr(P: PChar; var Param: string): PChar;
|
function GetParamStr(P: PChar; var Param: String): PChar;
|
||||||
var
|
var
|
||||||
i, Len: Integer;
|
i, Len: Integer;
|
||||||
Start, S, Q: PChar;
|
Start, S, Q: PChar;
|
||||||
@ -159,117 +484,53 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
{========================================
|
{ Single instance wrappers }
|
||||||
Window Procedure
|
function SingleInstance(const AApplicationID: String;
|
||||||
========================================}
|
ANotify, AGlobal: Boolean): Boolean;
|
||||||
function WndProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
|
|
||||||
var
|
var
|
||||||
sCmdLine: String;
|
newCmdLine: String;
|
||||||
iNotifier: Integer;
|
dummy: String;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
Result := DefWindowProc(hWnd, uMsg, wParam, lParam);
|
with Instance do
|
||||||
|
begin
|
||||||
|
ApplicationID := AApplicationID;
|
||||||
|
Global := AGlobal;
|
||||||
|
Active := True;
|
||||||
|
|
||||||
case uMsg of
|
Result := FirstInstance;
|
||||||
WM_COPYDATA:
|
|
||||||
if PCopyDataStruct(lParam)^.dwData = CDataCmdLine then begin
|
|
||||||
with PCopyDataStruct(lParam)^ do
|
|
||||||
SetString(sCmdLine, PChar(lpData), cbData - 1);
|
|
||||||
|
|
||||||
for iNotifier := GNotifiers.Count - 1 downto 0 do
|
if (not Result) and ANotify then
|
||||||
IX2InstanceNotifier(GNotifiers[iNotifier]).OnInstance(sCmdLine);
|
begin
|
||||||
|
{ For compatibility with ParamStr(0), we'll modify the command-line to
|
||||||
|
include the full executable path. }
|
||||||
|
newCmdLine := '"' + ParamStr(0) + '" ' + GetParamStr(CmdLine, dummy);
|
||||||
|
Notify(IM_COMMANDLINE, newCmdLine);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function Instance(): TX2Instance;
|
||||||
|
begin
|
||||||
|
if not Assigned(GlobalInstance) then
|
||||||
|
GlobalInstance := TX2Instance.Create;
|
||||||
|
|
||||||
|
Result := GlobalInstance;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure AttachInstance(const ANotifier: IX2InstanceObserver);
|
||||||
|
begin
|
||||||
|
Instance.Attach(ANotifier);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure DetachInstance(const ANotifier: IX2InstanceObserver);
|
||||||
|
begin
|
||||||
|
Instance.Detach(ANotifier);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
{========================================
|
{ Parameter helpers }
|
||||||
Single Instance Check
|
function ParamCountEx(const ACmdLine: String): Integer;
|
||||||
========================================}
|
|
||||||
function SingleInstance;
|
|
||||||
var
|
|
||||||
pData: ^THandle;
|
|
||||||
pCopy: TCopyDataStruct;
|
|
||||||
pCmdLine: PChar;
|
|
||||||
sDummy: String;
|
|
||||||
|
|
||||||
begin
|
|
||||||
Result := False;
|
|
||||||
if GFileMapping <> 0 then
|
|
||||||
exit;
|
|
||||||
|
|
||||||
// Attempt to create shared memory
|
|
||||||
GFileMapping := CreateFileMapping($ffffffff, nil, PAGE_READWRITE, 0,
|
|
||||||
SizeOf(THandle), PChar('X2UtInstance.' +
|
|
||||||
AApplicationID));
|
|
||||||
if GFileMapping = 0 then
|
|
||||||
exit;
|
|
||||||
|
|
||||||
if GetLastError() = ERROR_ALREADY_EXISTS then begin
|
|
||||||
if ANotify then begin
|
|
||||||
pData := MapViewOfFile(GFileMapping, FILE_MAP_READ, 0, 0, 0);
|
|
||||||
if Assigned(pData) then begin
|
|
||||||
// Pass command-line parameters
|
|
||||||
with pCopy do begin
|
|
||||||
pCmdLine := PChar('"' + ParamStr(0) + '" ' + GetParamStr(CmdLine, sDummy));
|
|
||||||
|
|
||||||
dwData := CDataCmdLine;
|
|
||||||
cbData := StrLen(pCmdLine) + 1;
|
|
||||||
|
|
||||||
GetMem(lpData, cbData);
|
|
||||||
StrCopy(lpData, pCmdLine);
|
|
||||||
end;
|
|
||||||
|
|
||||||
SendMessage(pData^, WM_COPYDATA, 0, Integer(@pCopy));
|
|
||||||
UnmapViewOfFile(pData);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
CloseHandle(GFileMapping);
|
|
||||||
GFileMapping := 0;
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
pData := MapViewOfFile(GFileMapping, FILE_MAP_WRITE, 0, 0, 0);
|
|
||||||
if Assigned(pData) then begin
|
|
||||||
// Create window
|
|
||||||
GWindow := CreateWindow(CWindowClass, '', 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
SysInit.HInstance, nil);
|
|
||||||
pData^ := GWindow;
|
|
||||||
end else begin
|
|
||||||
CloseHandle(GFileMapping);
|
|
||||||
GFileMapping := 0;
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
Result := True;
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
{========================================
|
|
||||||
Notifier Registration
|
|
||||||
========================================}
|
|
||||||
procedure RegisterInstance;
|
|
||||||
begin
|
|
||||||
if GNotifiers.IndexOf(ANotifier) = -1 then
|
|
||||||
GNotifiers.Add(ANotifier);
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure UnregisterInstance;
|
|
||||||
var
|
|
||||||
iIndex: Integer;
|
|
||||||
|
|
||||||
begin
|
|
||||||
iIndex := GNotifiers.IndexOf(ANotifier);
|
|
||||||
if iIndex > -1 then
|
|
||||||
GNotifiers.Delete(iIndex);
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
{========================================
|
|
||||||
Parameter Functions
|
|
||||||
========================================}
|
|
||||||
function ParamCountEx;
|
|
||||||
var
|
var
|
||||||
pCmdLine: PChar;
|
pCmdLine: PChar;
|
||||||
sParam: String;
|
sParam: String;
|
||||||
@ -288,7 +549,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function ParamStrEx;
|
function ParamStrEx(const ACmdLine: String; AIndex: Integer): String;
|
||||||
var
|
var
|
||||||
pCmdLine: PChar;
|
pCmdLine: PChar;
|
||||||
|
|
||||||
@ -351,28 +612,20 @@ var
|
|||||||
wndClass: TWndClass;
|
wndClass: TWndClass;
|
||||||
|
|
||||||
initialization
|
initialization
|
||||||
GNotifiers := TInterfaceList.Create();
|
{ Register window class }
|
||||||
|
|
||||||
// Register window class
|
|
||||||
FillChar(wndClass, SizeOf(wndClass), #0);
|
FillChar(wndClass, SizeOf(wndClass), #0);
|
||||||
with wndClass do begin
|
with wndClass do
|
||||||
lpfnWndProc := @WndProc;
|
begin
|
||||||
|
lpfnWndProc := @DefWindowProc;
|
||||||
hInstance := SysInit.HInstance;
|
hInstance := SysInit.HInstance;
|
||||||
lpszClassName := CWindowClass;
|
lpszClassName := WindowClass;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
Windows.RegisterClass(wndClass);
|
Windows.RegisterClass(wndClass);
|
||||||
|
|
||||||
finalization
|
finalization
|
||||||
FreeAndNil(GNotifiers);
|
Windows.UnregisterClass(WindowClass, SysInit.HInstance);
|
||||||
|
|
||||||
if GFileMapping <> 0 then
|
FreeAndNil(GlobalInstance);
|
||||||
// Free file mapping
|
|
||||||
CloseHandle(GFileMapping);
|
|
||||||
|
|
||||||
if GWindow <> 0 then
|
|
||||||
DestroyWindow(GWindow);
|
|
||||||
|
|
||||||
Windows.UnregisterClass(CWindowClass, SysInit.HInstance);
|
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
Loading…
Reference in New Issue
Block a user