У меня есть приложение, которое определяет, есть ли еще один запущенный экземпляр приложения, и завершает работу, если он обнаружен. Эта часть вроде работает надежно. Мое приложение принимает аргумент командной строки, который я хотел бы передать уже запущенному экземпляру. Пока у меня есть следующий код:
Project1.dpr
program Project1;
uses
...
AppInstanceControl in 'AppInstanceControl.pas';
if not AppInstanceControl.RestoreIfRunning(Application.Handle) then
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TFormMain, FormMain);
Application.Run;
end;
end.
AppInstanceControl.pas
{На основе кода Зарко Гайича, который можно найти по адресу http://delphi.about.com/library/code/ncaa100703a.htm}
unit AppInstanceControl;
interface
uses
Windows,
SysUtils;
function RestoreIfRunning(const AAppHandle: THandle; const AMaxInstances: integer = 1): boolean;
implementation
uses
Messages;
type
PInstanceInfo = ^TInstanceInfo;
TInstanceInfo = packed record
PreviousHandle: THandle;
RunCounter: integer;
end;
var
UMappingHandle: THandle;
UInstanceInfo: PInstanceInfo;
UMappingName: string;
URemoveMe: boolean = True;
function RestoreIfRunning(const AAppHandle: THandle; const AMaxInstances: integer = 1): boolean;
var
LCopyDataStruct : TCopyDataStruct;
begin
Result := True;
UMappingName := StringReplace(
ParamStr(0),
'\',
'',
[rfReplaceAll, rfIgnoreCase]);
UMappingHandle := CreateFileMapping($FFFFFFFF,
nil,
PAGE_READWRITE,
0,
SizeOf(TInstanceInfo),
PChar(UMappingName));
if UMappingHandle = 0 then
RaiseLastOSError
else
begin
if GetLastError <> ERROR_ALREADY_EXISTS then
begin
UInstanceInfo := MapViewOfFile(UMappingHandle,
FILE_MAP_ALL_ACCESS,
0,
0,
SizeOf(TInstanceInfo));
UInstanceInfo^.PreviousHandle := AAppHandle;
UInstanceInfo^.RunCounter := 1;
Result := False;
end
else //already runing
begin
UMappingHandle := OpenFileMapping(
FILE_MAP_ALL_ACCESS,
False,
PChar(UMappingName));
if UMappingHandle <> 0 then
begin
UInstanceInfo := MapViewOfFile(UMappingHandle,
FILE_MAP_ALL_ACCESS,
0,
0,
SizeOf(TInstanceInfo));
if UInstanceInfo^.RunCounter >= AMaxInstances then
begin
URemoveMe := False;
if IsIconic(UInstanceInfo^.PreviousHandle) then
ShowWindow(UInstanceInfo^.PreviousHandle, SW_RESTORE);
SetForegroundWindow(UInstanceInfo^.PreviousHandle);
end
else
begin
UInstanceInfo^.PreviousHandle := AAppHandle;
UInstanceInfo^.RunCounter := 1 + UInstanceInfo^.RunCounter;
Result := False;
end
end;
end;
end;
if (Result) and (CommandLineParam <> '') then
begin
LCopyDataStruct.dwData := 0; //string
LCopyDataStruct.cbData := 1 + Length(CommandLineParam);
LCopyDataStruct.lpData := PChar(CommandLineParam);
SendMessage(UInstanceInfo^.PreviousHandle, WM_COPYDATA, Integer(AAppHandle), Integer(@LCopyDataStruct));
end;
end; (*RestoreIfRunning*)
initialization
finalization
//remove this instance
if URemoveMe then
begin
UMappingHandle := OpenFileMapping(
FILE_MAP_ALL_ACCESS,
False,
PChar(UMappingName));
if UMappingHandle <> 0 then
begin
UInstanceInfo := MapViewOfFile(UMappingHandle,
FILE_MAP_ALL_ACCESS,
0,
0,
SizeOf(TInstanceInfo));
UInstanceInfo^.RunCounter := -1 + UInstanceInfo^.RunCounter;
end
else
RaiseLastOSError;
end;
if Assigned(UInstanceInfo) then UnmapViewOfFile(UInstanceInfo);
if UMappingHandle <> 0 then CloseHandle(UMappingHandle);
end.
и в модуле основной формы:
procedure TFormMain.WMCopyData(var Msg: TWMCopyData);
var
LMsgString: string;
begin
Assert(Msg.CopyDataStruct.dwData = 0);
LMsgString := PChar(Msg.CopyDataStruct.lpData);
//do stuff with the received string
end;
Я почти уверен, что проблема в том, что я пытаюсь отправить сообщение дескриптору запущенного экземпляра приложения, но пытаюсь обработать сообщение в основной форме. Думаю, у меня есть два варианта:
A) Из дескриптора приложения каким-то образом получить дескриптор его основной формы и отправить туда сообщение.
Б) Обработка получения сообщения на уровне приложения, а не на уровне основной формы.
Я тоже не совсем уверен, как поступить. Есть ли лучший подход?
Спасибо.