当前位置:  开发笔记 > 编程语言 > 正文

如何判断我的程序的另一个实例是否已在运行?

如何解决《如何判断我的程序的另一个实例是否已在运行?》经验,为你挑选了5个好方法。

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

我想这样做,因为我只希望一个实例在一个点上打开.



1> Rob Kennedy..:

正如Jon首先建议的那样,您可以尝试创建互斥锁.打电话CreateMutex.如果你得到一个非空句柄,那么调用GetLastError.它会告诉您是否是创建互斥锁的人或者互斥锁是否已经在(Error_Already_Exists)之前打开.请注意,没有必要获取互斥锁的所有权.互斥锁不会用于互斥.它正在被使用,因为它是一个命名的内核对象.事件或信号量也可以起作用.

互斥技术为您提供了一个布尔答案:是的,有另一个实例,或者没有,没有.

你经常想知道更多.例如,您可能想知道另一个实例的主窗口的句柄,这样您就可以告诉它来到前台代替您的其他实例.这就是内存映射文件可以派上用场的地方; 它可以保存有关第一个实例的信息,以便后面的实例可以引用它.

选择互斥锁的名称时要小心.请仔细阅读文档,并记住某些操作系统版本中不允许使用某些字符(如反斜杠),但其他操作系统版本中的某些功能则需要这些字符.

还要记住其他用户的问题.如果您的程序可以通过远程桌面或快速用户切换运行,那么可能有其他用户已在运行您的程序,您可能不想限制当前用户运行您的程序.在这种情况下,请勿使用全局名称.如果您确实要限制所有用户的访问权限,请确保互斥对象的安全属性使每个人都能够打开它的句柄.对lpSecurityAttributes参数使用空指针是不够的; MSDN提到的"默认安全描述符"提供对当前用户的完全访问权限,不允许访问其他用户.

您可以编辑程序的DPR文件.这通常是做这种事情的好地方.如果你等到OnCreate其中一个表单的事件,那么你的程序已经有一些正常运行的动力,所以在那时尝试终止程序是笨拙的.最好在UI工作太多之前终止.例如:

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.


爱因斯坦,是的,如果这就是亚瑟想要的,那么全球互斥是好的.根据他以前的一个问题,我认为不是.我记得,在Win2k之前,名称中不允许使用反斜杠,因此您也不一定总是使用相同的名称.

2> Drejc..:

您可以创建信号量并停止执行(将代码放入*.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;


我认为我们有一个范围蔓延的案例.你正在回答关于RestoreWindow部分的答案.如果您想了解更多相关细节,我建议您查找或提出另一个问题:"如何让我的程序的一个实例激活另一个?"

3> utku_karatas..:

强大的JVCL有一个用于此目的的组件.请参阅"TJvAppInstances".



4> Kluge..:

您创建一个系统 互斥锁.

我没有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);


你那里有竞争条件.始终调用CreateMutex,而不是OpenMutex.然后使用GetLastError找出谁赢得了比赛.
@Rob Kennedy,你是对的.代码可能会失败,程序的两个实例几乎立即启动.你的建议是正确的.

5> Jon Skeet..:

通常的解决方案是创建一个命名的系统范围的 互斥锁.

如果您设法创建它,那么您就是正在运行的应用程序.

如果你不这样做,你知道有一个不同的.


编辑:

我没有提供代码,因为我不知道Delphi.我可以提供C#代码,如果这会有所帮助.

推荐阅读
mobiledu2402851373
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有