Pregunta

¿Cómo puedo saber si se está ejecutando una instancia de mi programa? Pensé que podría hacer esto con un archivo de datos, pero sería complicado :(

Quiero hacer esto, ya que solo quiero que 1 instancia esté abierta en algún momento.

¿Fue útil?

Solución

Puede crear un semáforo y detener la ejecución (poner el código en su archivo * .dpr) y llevar su aplicación en ejecución a la pantalla.

var
  Semafor: THandle;

begin
  { Don't start twice ... if already running bring this instance to front }
  Semafor := CreateSemaphore(nil, 0, 1, 'MY_APPLICATION_IS_RUNNING');
  if ((Semafor <> 0) and { application is already running }
     (GetLastError = ERROR_ALREADY_EXISTS)) then 
  begin
    RestoreWindow('TMyApplication');
    CloseHandle(Semafor);
    Halt;
  end;

  Application.CreateForm(....);    
  Application.Initialize;
  Application.Run;
  CloseHandle(Semafor);
end;

EDITAR (se agregó el método RestoreWindow ):

El aFormName es el nombre de su clase de formulario principal en su aplicación.

procedure RestoreWindow(aFormName: string);
var
  Wnd,
  App: HWND;    
begin
  Wnd := FindWindow(PChar(aFormName), nil);
  if (Wnd <> 0) then 
  begin { Set Window to foreground }
    App := GetWindowLong(Wnd, GWL_HWNDPARENT);
    if IsIconic(App) then 
      ShowWindow(App, SW_RESTORE);

    SetForegroundwindow(App);
  end;
end;

Otros consejos

Como Jon sugirió por primera vez, puedes intentar crear un mutex. Llame a CreateMutex . Si obtiene un identificador no nulo, llame a GetLastError . Le dirá si usted fue quien creó el mutex o si el mutex ya estaba abierto antes ( Error_Already_Exists ). Tenga en cuenta que no es necesario adquirir la propiedad del mutex. El mutex no se está utilizando para la exclusión mutua. Se está utilizando porque es un objeto kernel con nombre. Un evento o semáforo también podría funcionar.

La técnica mutex le da una respuesta booleana: Sí, hay otra instancia, o no, no la hay.

Frecuentemente quieres saber más que eso. Por ejemplo, es posible que desee conocer el identificador de la ventana principal de la otra instancia para que pueda decir que aparezca en primer plano en lugar de su otra instancia. Ahí es donde un archivo mapeado en memoria puede ser útil; puede contener información sobre la primera instancia para que las instancias posteriores puedan consultarla.

Tenga cuidado al elegir el nombre del mutex. Lea la documentación detenidamente y tenga en cuenta que algunos caracteres (como la barra diagonal inversa) no están permitidos en algunas versiones del sistema operativo, pero son necesarios para ciertas funciones en otras versiones del sistema operativo.

Recuerde también el problema de otros usuarios. Si su programa podría ejecutarse a través de un escritorio remoto o un cambio rápido de usuario, entonces podría haber otros usuarios que ya estén ejecutando su programa, y ??es posible que no desee restringir al usuario actual de ejecutar su programa. En ese caso, no use un nombre global. Si do desea restringir el acceso para todos los usuarios, asegúrese de que los atributos de seguridad del objeto mutex sean tales que todos puedan abrirle un identificador. Usar un puntero nulo para el parámetro lpSecurityAttributes no es suficiente para eso; el " descriptor de seguridad predeterminado " que MSDN menciona da acceso completo al usuario actual y no acceso a otros.

Se le permite editar el archivo DPR de su programa. Ese suele ser un buen lugar para hacer este tipo de cosas. Si espera hasta el evento OnCreate de uno de sus formularios, entonces su programa ya tiene un poco de impulso para ejecutarse normalmente, por lo que es torpe intentar terminar el programa en ese punto. Es mejor terminar antes de que se haya realizado demasiado trabajo de IU. Por ejemplo:

var
  mutex: THandle;
  mutexName: string;
begin
  mutexName := ConstructMutexName();

  mutex := CreateMutex(nil, False, PChar(mutexName));

  if mutex = 0 then
    RaiseLastOSError; // Couldn't open handle at all.

  if GetLastError = Error_Already_Exists then begin
    // We are not the first instance.
    SendDataToPreviousInstance(...);
    exit;
  end;
  // We are the first instance.

  // Do NOT close the mutex handle here. It must
  // remain open for the duration of your program,
  // or else later instances won't be able to
  // detect this instance.

  Application.Initialize;
  Application.CreateForm(...);
  Application.Run;
end.

Hay una cuestión de cuándo cerrar el identificador de exclusión mutua. No tienes que cerrarlo. Cuando su proceso finalmente finalice (incluso si se bloquea), el sistema operativo cerrará automáticamente todos los identificadores pendientes, y cuando no haya más identificadores abiertos, el objeto mutex se destruirá (permitiendo así que se inicie otra instancia de su programa y se considere ser la primera instancia).

Pero es posible que desee cerrar el asa de todos modos. Supongamos que elige implementar la función SendDataToPreviousInstance que mencioné en el código. Si quieres ponerte elegante, entonces podrías dar cuenta del caso de que la instancia anterior ya se está cerrando y no puede aceptar nuevos datos. Entonces realmente no querrás cerrar la segunda instancia. La primera instancia podría cerrar el identificador de mutex tan pronto como sepa que se está cerrando, convirtiéndose en un "pato cojo". ejemplo. La segunda instancia intentará crear el identificador de mutex, tener éxito y considerarse la primera instancia real. La instancia anterior se cerrará sin interrupciones. Use CloseHandle para cerrar el mutex; llámelo desde el controlador de eventos OnClose de su formulario principal, o donde sea que llame a Application.Terminate , por ejemplo.

El todopoderoso JVCL tiene un componente para este propósito. Consulte "TJvAppInstances".

Crea un sistema mutex .

No tengo código Delphi, pero aquí está el código C ++:

HANDLE Mutex;

const char MutexName[] = "MyUniqueProgramName";

Mutex = OpenMutex(MUTEX_ALL_ACCESS, false, MutexName);

if (Mutex)
     throw Exception("Program is already running.");
else
     Mutex = CreateMutex(NULL, true, MutexName);

La solución normal es crear un nombre, en todo el sistema mutex .

  • Si logras crearlo, eres la única aplicación en ejecución.
  • Si no lo haces, sabes que hay uno diferente.

EDITAR:

No he proporcionado código ya que no conozco a Delphi. Sin embargo, puedo proporcionar el código C # si eso sería útil.

Me gustaría agregar un punto a excelente respuesta de Rob Kennedy (aparte del hecho de que sería mejor hacer una función de su código en lugar de copiar todo en el archivo DPR. Solo necesita dos parámetros, el nombre del mutex y un booleano si el mutext debe ser por usuario o en todo el sistema).

La respuesta no da mucha consideración a la denominación del mutex. Si espera que su programa se instale a través de Inno Setup (y tal vez otras herramientas de configuración también), debe elegir el nombre con cuidado, ya que el mutex se puede usar para que el programa de configuración verifique si la aplicación se está ejecutando actualmente y avise al usuario que deberían cerrar todas las instancias de la aplicación. Si elige permitir una instancia del programa por usuario, es posible que también necesite crear un segundo mutex para todo el sistema, ya que es posible que la configuración no necesite tener instancias en ejecución de la aplicación en absoluto para ser capaz de reemplazar archivos. El nombre que se utilizará para la sincronización con un instalador InnoSetup debe estar codificado.

Yo diría que hay varias estrategias diferentes que puedes emplear. Pero el más fácil (y no específico de la plataforma) es el que usted mismo sugirió, es decir, al comienzo del programa, verifique si hay un archivo de bloqueo creado en una ubicación específica establecida. Si este archivo de bloqueo existe, entonces otra instancia ya se está ejecutando, si no existe, entonces no hay otra instancia ejecutándose. Cuando su programa se cierra, elimina el archivo de bloqueo.

Sin embargo, al emplear esta estrategia tiene otro problema, ¿qué sucede si su programa falla? El archivo de bloqueo aún permanece, y este caso específico debe ser manejado.

Otra estrategia es la solución de mutex en todo el sistema, donde registra su presencia dentro del sistema operativo (o también es posible que esto se haga de forma automática). Cuando una segunda instancia intenta iniciar, comprueba si ya hay un proceso activo con una ID específica. Si ya existe, el segundo proceso elige no comenzar, y opcionalmente pone en foco la ventana del primer proceso (si el proceso en cuestión posee una ventana que es).

Sin embargo, esta estrategia es específica de la plataforma, y ??la implementación diferirá de una plataforma a otra.

Simplemente puede usar la función api de Windows FindWindow. En el nombre de clase delphi de la ventana es el mismo que el nombre de clase, puede redefinir el nombre de clase anulando la función CreateParams. Para verificar si existe una ventana, agregue el código antes de crear la ventana principal, antes de Application.Initialize;

Program test
var 
  handle :HWND;
begin
  handle := FindWindow('TMySuperApp', nil);

  if IsWindow(handle) then
  begin 
       //app is running
       exit;
  end.

  Application.Initialize;
  Application.CreateForm(TMySuperApp, SuperApp);
  Application.Run;
end;

Control del número de instancias de aplicación:

http://delphi.about.com/od/windowsshellapi/l/ aa100703a.htm

Vea esta unidad (usando CreateMutex): UiApp

Además, en esta página, puede leer las ventajas y desventajas de este trabajo con diferentes métodos (mutex, FindWindows, ...).

Esta unidad tiene la solución para activar la instancia anterior de la aplicación cuando se detecta.

Un saludo y perdón por mi mal inglés.


Neftal & # 237; -Germ & # 225; n Est & # 233; vez-

Si desea detener la ejecución su aplicación más de una vez en al mismo tiempo (coloque el código en * .dpr archivo del proyecto). mostrará un mensaje después de que se ejecute la segunda aplicación y la detendrá instantáneamente.

Forms,
  Unit1 in 'Unit1.pas' {Form1},
// add this units ....
TlHelp32,SysUtils,Windows,Dialogs;

{$R *.res}


function ProcessCount(const ExeName: String): Integer;
var
  ContinueLoop: BOOL;
  FSnapshotHandle: THandle;
  FProcessEntry32: TProcessEntry32;
begin
  FSnapshotHandle:= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  FProcessEntry32.dwSize:= SizeOf(FProcessEntry32);
  ContinueLoop:= Process32First(FSnapshotHandle, FProcessEntry32);
  Result:= 0;
  while Integer(ContinueLoop) <> 0 do begin
    if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
      UpperCase(ExeName)) or (UpperCase(FProcessEntry32.szExeFile) =
      UpperCase(ExeName))) then Inc(Result);
    ContinueLoop:= Process32Next(FSnapshotHandle, FProcessEntry32);
  end;
  CloseHandle(FSnapshotHandle);
end;


begin
  if ProcessCount(ExtractFileName(Application.ExeName)) > 1 then begin
    MessageDlg('Application is already running!', mtError, [mbOK], 0);
    Application.Terminate;
  end else begin

  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
  end;

end.

En el pasado, he usado un socket para evitar que se ejecuten varias instancias al mismo tiempo. Si el socket está en uso, no continúe el programa, si está disponible, deje que todo se ejecute normalmente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top