如何判断我的程序的另一个实例是否已经在运行?
-
19-08-2019 - |
题
我如何判断我的程序的一个实例是否正在运行?我以为我可以用数据文件来做到这一点,但它只会很混乱:(
我想这样做是因为我只希望在某一时刻打开 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.
在过去,我使用的插座,以防止多个实例在同一时间运行。如果插座在使用中,不要继续该计划,如果它是可用的,让一切运行正常。