我如何判断我的程序的一个实例是否正在运行?我以为我可以用数据文件来做到这一点,但它只会很混乱:(

我想这样做是因为我只希望在某一时刻打开 1 个实例。

有帮助吗?

解决方案

您可以创建一个信号量并停止执行(把代码放到你的* .dpr文件),并为您带来运行的应用程序在屏幕上。

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;

修改(添加的RestoreWindow法):

aFormName是主窗体类的名称在应用程序中。

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;

其他提示

作为乔恩第一次提出,你可以尝试创建一个互斥体。呼叫 CreateMutex 。如果你得到一个非空处理回,然后调用 GetLastError 。它会告诉你是否是谁创建互斥或互斥是否已经之前(Error_Already_Exists)开放的一个。需要注意的是的的必要获得互斥体的所有权。互斥锁没有被用于互斥。它被使用,因为它是一个名为内核对象。一个事件或信号可以工作了。

互斥锁技术给你一个布尔回答:是的,还有另外一个实例,或没有,没有。

您经常想知道的远远不止这些。例如,你可能想知道其他实例的主窗口的句柄,所以你可以告诉它到达前台到位的其他实例。这就是内存映射文件可以派上用场;它可以容纳关于第一实例的信息,以便以后的实例可以参考它。

选择互斥体的名称时

要小心。请仔细阅读文档,并记住一些字符(如反斜杠)没有在一些操作系统版本允许的,但需要在其他版本的操作系统的某些功能。

还记得其他用户的问题。如果你的程序可以通过远程桌面或快速用户切换运行,则可能有其他用户已经运行您的程序,你可能不是真的想从运行您的程序限制当前用户。在这种情况下,不使用全局名称。如果你的的想限制所有用户访问,然后确保互斥对象的安全属性是这样的,每个人都将能够打开它的句柄。使用NULL指针为lpSecurityAttributes参数是不够的是什么; “默认安全描述符”是MSDN提到给予完全访问当前用户和其他人没有权限。

你可以编辑你的程序的DPR文件。这通常做这种事情的好地方。如果等到你的形式之一的OnCreate事件,那么你的程序已经有一点点气势向着正常运行,所以它的笨拙尝试在该点终止程序。更好地终止之前太多的用户界面工作已经完成。例如:

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.

有年代时关闭互斥处理的问题。您不必将其关闭。当你的进程终于终止(即使它崩溃),操作系统会自动关闭所有的手柄,当没有更多的句柄开放,互斥对象将被销毁(从而使你的程序的另一个实例启动并认为自己是第一个实例)。

但是,您可能仍要关闭句柄。假设你选择了实施我在代码中提及的SendDataToPreviousInstance功能。如果你想获得幻想,那么你可以考虑,以前的实例已经关闭,无法接收新的数据的情况。然后,你不会真的要关闭的第二个实例。一审能否尽快关闭互斥处理,因为它知道它的关闭,实际上成为一个“跛脚鸭”的实例。第二个实例将尝试创建互斥处理,获得成功,并认为自己是真正的第一个实例。以前的实例将关闭中断。使用CloseHandle关闭互斥;从你的主窗体的OnClose事件处理程序,或其他任何地方你打电话Application.Terminate,例如调用它。

全强大JVCL 具有用于此目的的组件。请参见 “TJvAppInstances”。

您创建的系统互斥

我没有Delphi代码,但这里的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);

通常的解决方案是创建一个 命名,全系统 互斥的.

  • 如果你能创造它,你是一个正在运行的应用程序。
  • 如果你没有,你知道有一个不同的。

编辑:

我还没有提供代码作为我不知道Delphi。我可以提供C#代码,如果将是有益的。

我想补充一点 罗布·肯尼迪的出色回答 (除此之外,最好从他的代码中创建一个函数,而不是将所有内容复制到 DPR 文件中。您只需要两个参数:互斥体的名称和一个布尔值(互斥体是针对每个用户的还是针对系统范围的)。

答案并没有过多考虑互斥体的命名。如果您希望通过 Inno Setup(也许还有其他安装工具)安装程序,您应该仔细选择名称,因为互斥体可用于让安装程序检查应用程序当前是否正在运行,并提醒用户他们应该关闭应用程序的所有实例。如果您选择允许每个用户使用一个程序实例,您可能还需要创建第二个系统范围的互斥体,因为设置可能不需要运行应用程序的实例 根本不 为了能够替换文件。用于与 InnoSetup 安装程序同步的名称必须是硬编码的。

我要说的是,有,你可以采用几种不同的策略。但是,一个最简单的(而不是特定平台)是你自己的建议,即在程序检查的开始,看看是否有一组创建一个锁文件,具体位置之一。如果这个锁定文件存在,那么另一个实例已在运行,如果它不存在,那么就没有运行的另一个实例。当你的程序退出时,您删除锁定文件。

然而,采用这种策略,您有其他问题,如果你的程序崩溃,会发生什么?锁定文件仍然存在,并且该特定情况下需要处理。

另一种策略是全系统互斥方案,在您注册在操作系统内的存在(或者这也是合理的,这是自动完成的)。当第二个实例,然后尝试启动,它检查是否已经有活性的,特定ID的过程。如果它已经存在时,第二进程选择不启动,并任选地带来了焦点的第一进程的窗口(如果有问题的进程拥有的窗口中)。

然而,这种策略是特定于平台,并实施将来自平台不同。

您可以简单地使用FindWindow函数Windows API函数。在窗口的Delphi的类名是相同的类名,你可以通过覆盖的CreateParams重新定义函数类的名称。要检查是否存在窗口添加代码创建主窗口之前,在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;

请参阅此单元(使用CreateMutex):了UiApp

此外,在这个页面,你可以阅读的优点和缺点与型动物的方法(互斥,FindWindows,...)这项工作。

此单元具有溶液这个检测何时激活该应用程序的previos实例。

问候,并借口,我为我的英语不好。


Neftalí - 德语埃斯特韦斯 -

如果你想为停止执行您的应用程序的,曾多次同时(把代码到 *。DPR文件的项目的)。 将显示第二应用后的消息将是运行,并立即停止。

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.

在过去,我使用的插座,以防止多个实例在同一时间运行。如果插座在使用中,不要继续该计划,如果它是可用的,让一切运行正常。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top