2014-12-08 12 views
5

So che l'ho già fatto ma non riesco a ricordare come.Delphi Limitazione di una singola istanza di un programma in base ai parametri della riga di comando

Ho un programma che ho impostato per eseguire singleton utilizzando il mutex sul nome dell'eseguibile. 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; 

Il programma accetta determinati parametri della riga di comando che determinano su quali dati agire. Non voglio più di un'istanza del programma in esecuzione con gli stessi argomenti della riga di comando. Ma voglio essere in grado di avviare una seconda istanza con argomenti diversi.

L'ho fatto circa un anno fa ma non ricordo come. Modifico il nome con i parametri della riga di comando nel DPR e poi lo collaudo con il mutex.

Ho provato a rinominare Application.ExeName ma è di sola lettura, quindi devo aver cambiato qualcos'altro.

Di seguito è riportato un codice che non verrà compilato ma aggiunto per chiarire cosa voglio fare. btw - il '##" è sempre i primi due caratteri della 3 parametri, ma i test per questo con una 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. 
+1

Se la domanda 'foo.exe -a -b' trattata come uguale a' foo.exe - b -a' e 'foo.exe -B -a'? –

+2

Il parametro per il nome del mutex può essere al massimo 'MAX_PATH' lungo che non è sufficiente ad es. per il passaggio di ca. Parametri della riga di comando di 250 caratteri ('MAX_PATH - 'EPRmailer''). In tal caso la creazione del mutex fallisce, ma non a causa dell'errore 'ERROR_ALREADY_EXISTS' e restituirai un valore errato dalla tua funzione. Per evitare ciò penserei a un hash qui (dopo aver ordinato i parametri). – TLama

+0

Se Application.Title è abbastanza unico, può essere utilizzato al posto di Application.ExeName, poiché è molto più breve e non contiene caratteri '\'. –

risposta

8

Si utilizza un approccio sbagliato Invece di rinominare Application.ExeName, è necessario inviare stringa configurabile.. per la funzione che i test per le applicazioni di duplicati.

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. 

funzione CreateSingleInstance è destinato a essere utilizzato una sola volta in applicazione perché alloca mutex che rimarrà attivo fino a quando termina di applicazione e quindi di Windows si chiude automaticamente la maniglia mutex.

Nota: se MyInstanceName supera MAX_PATH caratteri o contiene rovesciato funzione '\' caratteri fallirà

CreateMutex documentation

+0

Questa funzione restituirà sempre False se ci passi più del valore 'InstanceName' di MAX_PATH' lungo. Anche il cast di 'PWideChar' non è adatto alle vecchie versioni di Delphi ;-) In realtà, è sufficiente scrivere' Result: = (CreateMutex (nil, False, PChar (InstanceName)) <> 0) e (GetLastError = ERROR_ALREADY_EXISTS); ' ma il rischio di superare la lunghezza del nome mutex rimane. – TLama

+0

Inoltre, funzionerà solo se si chiama 'IsAppRunning()' una volta, poiché il mutex non viene chiuso, quindi le chiamate successive rileveranno 'ERROR_ALREADY_EXISTS'. –

+0

@TLama Ho modificato il codice, ma ho aggiunto un controllo un po 'più complesso per rendere più visibile cosa sta succedendo, inclusa la chiusura del mutex handle sebbene non sia necessario se la funzione viene utilizzata solo per l'avvio dell'app –

Problemi correlati