📄 jclappinst.pas
字号:
{**************************************************************************************************}
{ }
{ Project JEDI Code Library (JCL) }
{ }
{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
{ you may not use this file except in compliance with the License. You may obtain a copy of the }
{ License at http://www.mozilla.org/MPL/ }
{ }
{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
{ ANY KIND, either express or implied. See the License for the specific language governing rights }
{ and limitations under the License. }
{ }
{ The Original Code is JclAppInst.pas. }
{ }
{ The Initial Developer of the Original Code is Petr Vones. Portions created by Petr Vones are }
{ Copyright (C) Petr Vones. All Rights Reserved. }
{ }
{ Contributor(s): }
{ Marcel van Brakel }
{ Robert Marquardt (marquardt) }
{ Robert Rossmair (rrossmair) }
{ Matthias Thoma (mthoma) }
{ Petr Vones (pvones) }
{ }
{**************************************************************************************************}
{ }
{ This unit contains a class and support routines for controlling the number of concurrent }
{ instances of your application that can exist at any time. In addition there is support for }
{ simple interprocess communication between these instance including a notification mechanism. }
{ }
{ Unit owner: Petr Vones }
{ }
{**************************************************************************************************}
// Last modified: $Date: 2005/02/24 16:34:52 $
// For history see end of file
unit JclAppInst;
{$I jcl.inc}
interface
uses
Windows, Classes, Messages,
JclFileUtils, JclSynch;
// Message constants and types
type
TJclAppInstDataKind = Integer;
const
AI_INSTANCECREATED = $0001;
AI_INSTANCEDESTROYED = $0002;
AI_USERMSG = $0003;
AppInstDataKindNoData = -1;
AppInstCmdLineDataKind = 1;
// Application instances manager class
type
TJclAppInstances = class(TObject)
private
FCPID: DWORD;
FMapping: TJclSwapFileMapping;
FMappingView: TJclFileMappingView;
FMessageID: DWORD;
FOptex: TJclOptex;
function GetAppWnds(Index: Integer): HWND;
function GetInstanceCount: Integer;
function GetProcessIDs(Index: Integer): DWORD;
function GetInstanceIndex(ProcessID: DWORD): Integer;
protected
procedure InitData;
procedure NotifyInstances(const W, L: Longint);
procedure RemoveInstance;
public
constructor Create;
destructor Destroy; override;
class function BringAppWindowToFront(const Wnd: HWND): Boolean;
class function GetApplicationWnd(const ProcessID: DWORD): HWND;
class procedure KillInstance;
class function SetForegroundWindow98(const Wnd: HWND): Boolean;
function CheckInstance(const MaxInstances: Word): Boolean;
procedure CheckMultipleInstances(const MaxInstances: Word);
procedure CheckSingleInstance;
function SendCmdLineParams(const WindowClassName: string; const OriginatorWnd: HWND): Boolean;
function SendData(const WindowClassName: string; const DataKind: TJclAppInstDataKind;
Data: Pointer; const Size: Integer;
OriginatorWnd: HWND): Boolean;
function SendString(const WindowClassName: string; const DataKind: TJclAppInstDataKind;
const S: string; OriginatorWnd: HWND): Boolean;
function SendStrings(const WindowClassName: string; const DataKind: TJclAppInstDataKind;
const Strings: TStrings; OriginatorWnd: HWND): Boolean;
function SwitchTo(const Index: Integer): Boolean;
procedure UserNotify(const Param: Longint);
property AppWnds[Index: Integer]: HWND read GetAppWnds;
property InstanceIndex[ProcessID: DWORD]: Integer read GetInstanceIndex;
property InstanceCount: Integer read GetInstanceCount;
property MessageID: DWORD read FMessageID;
property ProcessIDs[Index: Integer]: DWORD read GetProcessIDs;
end;
function JclAppInstances: TJclAppInstances; overload;
function JclAppInstances(const UniqueAppIdGuidStr: string): TJclAppInstances; overload;
// Interprocess communication routines
function ReadMessageCheck(var Message: TMessage; const IgnoredOriginatorWnd: HWND): TJclAppInstDataKind;
procedure ReadMessageData(const Message: TMessage; var Data: Pointer; var Size: Integer);
procedure ReadMessageString(const Message: TMessage; var S: string);
procedure ReadMessageStrings(const Message: TMessage; const Strings: TStrings);
implementation
uses
SysUtils,
JclStrings;
{$IFDEF FPC} // missing declaration from unit Messages
type
TWMCopyData = packed record
Msg: Cardinal;
From: HWND;
CopyDataStruct: PCopyDataStruct;
Result: Longint;
end;
{$ENDIF FPC}
const
{ strings to form a unique name for file mapping and optex objects }
JclAIPrefix = 'Jcl';
JclAIOptex = '_Otx';
JclAIMapping = '_Map';
{ window message used for communication between instances }
JclAIMessage = '_Msg';
{ maximum number of instance that may exist at any time }
JclAIMaxInstances = 256;
{ name of the application window class }
ClassNameOfTApplication = 'TApplication';
type
{ management data to keep track of application instances. this data is shared amongst all instances
and must be appropriately protected from concurrent access at all time }
PJclAISharedData = ^TJclAISharedData;
TJclAISharedData = packed record
MaxInst: Word;
Count: Word;
ProcessIDs: array [0..JclAIMaxInstances] of DWORD;
end;
var
{ the single global TJclAppInstance instance }
AppInstances: TJclAppInstances;
ExplicitUniqueAppId: string;
//=== { TJclAppInstances } ===================================================
constructor TJclAppInstances.Create;
begin
inherited Create;
FCPID := GetCurrentProcessId;
InitData;
end;
destructor TJclAppInstances.Destroy;
begin
if (FMapping <> nil) and (FOptex <> nil) then
RemoveInstance;
FreeAndNil(FMapping);
FreeAndNil(FOptex);
inherited Destroy;
end;
class function TJclAppInstances.BringAppWindowToFront(const Wnd: HWND): Boolean;
begin
if IsIconic(Wnd) then
SendMessage(Wnd, WM_SYSCOMMAND, SC_RESTORE, 0);
Result := SetForegroundWindow98(Wnd);
end;
function TJclAppInstances.CheckInstance(const MaxInstances: Word): Boolean;
begin
FOptex.Enter;
try
with PJclAISharedData(FMappingView.Memory)^ do
begin
if MaxInst = 0 then
MaxInst := MaxInstances;
Result := Count < MaxInst;
ProcessIDs[Count] := GetCurrentProcessId;
Inc(Count);
end;
finally
FOptex.Leave;
end;
if Result then
NotifyInstances(AI_INSTANCECREATED, Integer(FCPID));
end;
procedure TJclAppInstances.CheckMultipleInstances(const MaxInstances: Word);
begin
if not CheckInstance(MaxInstances) then
begin
SwitchTo(0);
KillInstance;
end;
end;
procedure TJclAppInstances.CheckSingleInstance;
begin
CheckMultipleInstances(1);
end;
class function TJclAppInstances.GetApplicationWnd(const ProcessID: DWORD): HWND;
type
PTopLevelWnd = ^TTopLevelWnd;
TTopLevelWnd = record
ProcessID: DWORD;
Wnd: HWND;
end;
var
TopLevelWnd: TTopLevelWnd;
function EnumWinProc(Wnd: HWND; Param: PTopLevelWnd): BOOL; stdcall;
var
PID: DWORD;
C: array [0..Length(ClassNameOfTApplication) + 1] of Char;
begin
GetWindowThreadProcessId(Wnd, @PID);
if (PID = Param^.ProcessID) and (GetClassName(Wnd, C, SizeOf(C)) > 0) and
(C = ClassNameOfTApplication) then
begin
Result := False;
Param^.Wnd := Wnd;
end
else
Result := True;
end;
begin
TopLevelWnd.ProcessID := ProcessID;
TopLevelWnd.Wnd := 0;
EnumWindows(@EnumWinProc, LPARAM(@TopLevelWnd));
Result := TopLevelWnd.Wnd;
end;
function TJclAppInstances.GetAppWnds(Index: Integer): HWND;
begin
Result := GetApplicationWnd(GetProcessIDs(Index));
end;
function TJclAppInstances.GetInstanceCount: Integer;
begin
FOptex.Enter;
try
Result := PJclAISharedData(FMappingView.Memory)^.Count;
finally
FOptex.Leave;
end;
end;
function TJclAppInstances.GetInstanceIndex(ProcessID: DWORD): Integer;
var
I: Integer;
begin
Result := -1;
FOptex.Enter;
try
with PJclAISharedData(FMappingView.Memory)^ do
begin
for I := 0 to Count - 1 do
if ProcessIDs[I] = ProcessID then
begin
Result := I;
Break;
end;
end;
finally
FOptex.Leave;
end;
end;
function TJclAppInstances.GetProcessIDs(Index: Integer): DWORD;
begin
FOptex.Enter;
try
with PJclAISharedData(FMappingView.Memory)^ do
if Index >= Count then
Result := 0
else
Result := ProcessIDs[Index];
finally
FOptex.Leave;
end;
end;
procedure TJclAppInstances.InitData;
var
UniqueAppID: string;
begin
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -