如何判断我的程序的一个实例是否正在运行?我以为我可以用数据文件做这个但它只是凌乱:(
我想这样做,因为我只希望一个实例在一个点上打开.
正如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
.
您可以创建信号量并停止执行(将代码放入*.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;
强大的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#代码,如果这会有所帮助.