Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi Restricting a single instance of a program based on command line parameters

I know I have done this before but can't seem to remember how.

I have a program I have set up to run singleton using the mutex on the executable name. unit GlobalSU;

interface
 function IsAppRunning: Boolean;

implementation

uses
 Windows, SysUtils, Forms;

function IsAppRunning: Boolean;
var
 rtn : Cardinal;
begin
  result := False;
  CreateMutex(nil, False, PWideChar(ExtractFileName(Application.ExeName)));
  rtn := GetLastError;
  if rtn = ERROR_ALREADY_EXISTS then
   result := True;
end;

The program accepts certain command line parameters that dictate what data to act on. I don't want more than one instance of the program running with the same command line arguments. But I do want to be able to start up a second instance with different arguments.

I did this about a year ago but can't remember how. I modify the name with the command line parameters in the DPR and then test for it with the mutex.

I tried renaming Application.ExeName but it is read only so I must have been changing something else.

Below is code that won't compile but added to clarify what I want to do. btw - the '##" is always the first two characters of the 3rd parameters but I test for it with a RegEx.

program EPRmailer;

uses
  Vcl.Forms,
  uMainMailer in 'uMainMailer.pas' {frmMainMailer},
  configXML in 'configXML.pas',
  GlobalSU in 'GlobalSU.pas',
  CVUtils in 'CVUtils.pas',
  QMConst in 'QMConst.pas',
  ServerAttachmentDMu in 'ServerAttachmentDMu.pas';

{$R *.res}
 var
   i : integer;
begin

  for i := 0 to ParamCount do
  if TestParam('##', ParamStr(i)) then
  Application.ExeName := Application.ExeName + '-' + ParamStr(i);

  if IsAppRunning then exit;

  Application.Initialize;
  ReportMemoryLeaksOnShutdown := DebugHook <> 0;
  Application.MainFormOnTaskbar := false;
  Application.CreateForm(TfrmMainMailer, frmMainMailer);
  frmMainMailer.RunEPR;

end.
like image 344
Meta Mussel Avatar asked Dec 08 '14 17:12

Meta Mussel


1 Answers

You are using wrong approach. Instead of renaming Application.ExeName, you should send configurable string to your function that tests for duplicate applications.

function CreateSingleInstance(const InstanceName: string): boolean;
var
  MutexHandle: THandle;
begin
  MutexHandle := CreateMutex(nil, false, PChar(InstanceName));
  // if MutexHandle created check if already exists
  if (MutexHandle <> 0) then
    begin
      if GetLastError = ERROR_ALREADY_EXISTS then
        begin
          Result := false;
          CloseHandle(MutexHandle);
        end
      else Result := true;
    end
  else Result := false;
end;

var
  MyInstanceName: string;
begin
  Application.Initialize;
  // Initialize MyInstanceName here
  ...
  if CreateSingleInstance(MyInstanceName) then
    begin
      // Form creation 
      ...
    end
  else Application.Terminate;
end. 

Function CreateSingleInstance is meant to be used once in application because it allocates mutex that will remain active until application terminates and then Windows will automatically close mutex handle.

Note: if MyInstanceName exceeds MAX_PATH characters or contains backslash '\' characters function will fail

CreateMutex documentation

like image 141
Dalija Prasnikar Avatar answered Sep 28 '22 22:09

Dalija Prasnikar