如何确保我的应用程序只有一个实例运行?

Posted

技术标签:

【中文标题】如何确保我的应用程序只有一个实例运行?【英文标题】:How to ensure only a single instance of my application runs? 【发布时间】:2011-07-20 09:51:49 【问题描述】:

Delphi XE VCL 是否支持确保只有一个应用程序实例在运行?

过去,我使用库代码来控制 Mutex,这似乎总是很复杂。当我在 Delphi XE 中开始一个新项目时,我想知道我是否需要挖掘旧代码,或者是否已经内置了 XE 支持?或者是否有其他易于应用且美观且现代的代码?

【问题讨论】:

是什么让您认为创建互斥锁不现代? 我已经为以下跨多个用户会话工作的类型实现了实例化:TEAppSingleInstance = (siYes, siMultipleAcrossUsers, siNo)。是意味着所有用户的单个实例,不意味着每个用户可以运行多个实例,跨用户的多个意味着每个用户只能为其会话运行一个实例,但多个用户可以同时运行应用程序。 How can I tell if another instance of my program is already running?的可能重复 【参考方案1】:

我就是这样做的。

closeProc(extractfilename(paramstr(0)));

function TForm1.closeProc(pname : string): integer;
const
PROCESS_TERMINATE = $0001;
var
ContinueLoop: BOOL;
FSnapshotHandle: THandle;
FProcessEntry32: TProcessEntry32;
i : integer;
pname2 : string;
begin
try
Result := 0;
i := 0;
FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
FProcessEntry32.dwSize := SizeOf(FProcessEntry32);
ContinueLoop := Process32First(FSnapshotHandle, FProcessEntry32);
while Integer(ContinueLoop) <> 0 do
    begin
    pname2 := trim(UpperCase(ExtractFileName(FProcessEntry32.szExeFile)));
    if ( pname2 = uppercase(pname)) then
      if FProcessEntry32.th32ProcessID <> GetCurrentProcessId then
        begin
          Result := Integer(TerminateProcess(OpenProcess(PROCESS_TERMINATE, BOOL(0), FProcessEntry32.th32ProcessID), 0));
          inc(i);
        end;
    ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32);
    if i > 50 then
      break;
    end;
CloseHandle(FSnapshotHandle);
except
end;
end;

【讨论】:

【参考方案2】:

您在启动应用程序时创建了一个名为 Mutex。检查GetLastError 以查看其他实例是否已在运行。

将此代码放在 DPR 文件中的“开始”之后。将 GUID 替换为您自己的 GUID。当我需要一个不太可能用于其他任何内容的文本常量时,我​​通常只需按 Ctrl+G 即可获得 GUID!

if CreateMutex(nil, True, '6EACD0BF-F3E0-44D9-91E7-47467B5A2B6A') = 0 then
  RaiseLastOSError;

if GetLastError = ERROR_ALREADY_EXISTS then
  Exit;

看起来代码泄漏了句柄,因为它没有保存CreateMutex 的返回值。它不是。当我们的应用程序终止时,Windows 会自动释放句柄,这对我们来说绝对没问题。

【讨论】:

您不需要对两行 Windows API 的特定 Delphi XE 支持。确保将 WindowsSysUtils 添加到 DPR 的使用子句中。 这将在会话命名空间中创建互斥锁。不同会话中的进程(想想快速用户切换)将能够在另一个会话中的进程运行时启动一个新进程。您可以使用 Global\ 作为名称的前缀来获取全局命名空间中的互斥锁。 好点大卫。然而,也许这种行为(会话命名空间)可能是一些开发人员真正想要的,即使他们没有考虑过。想象一下,您想部署一个可以使用 Windows 终端服务运行的富数据库客户端应用程序,您可能每个桌面一个应用程序而不是每台计算机一个应用程序。 更简单的例子:想象一下你为你的下一个大聊天程序实现了这个;妻子来到电脑前,实际上做了“切换用户”(我妻子做了!)并登录到她的帐户,尝试启动下一个大聊天程序。哎呀!无论如何,大卫,好点子,无论如何,每个人都应该阅读文档。 +1 用于直接访问 VCL 和 Win API 而不是 JCL。 JCL 没有错,但为什么在不需要的时候使用外部工具呢?我们多年来一直在我们的商店中使用此解决方案,没有出现任何问题。至于不同的用户,正如您所提到的,通常这就是您想要的 - 不同的用户应该获得不同的会话/实例。【参考方案3】:

我使用 JCL 来做到这一点:

program MyProgram;

uses
  JclAppInst;

begin
  JclAppInstances.CheckSingleInstance; // Added instance checking
  Application.Initialize;
  Application.CreateForm(TMainForm, MainForm);
  Application.Run;
end.

这方面的文档和通知方案位于the JCL Wiki。

【讨论】:

+1 用于使用 JCL。经过测试,有朝一日,它甚至可以移植到不同的平台。 谢谢 - 这也有通知。不是在 VCL 中,而是下一个最好的东西。

以上是关于如何确保我的应用程序只有一个实例运行?的主要内容,如果未能解决你的问题,请参考以下文章

如何确保所有实例在 Elastic Beanstalk 应用程序中运行相同的版本?

如何确保JAVA程序在一台机器上不能同时运行两个实例

如何确保我的 django 应用程序中只有 1 个模型创建迁移?

如何确保核心数据实体的单一实例

如何确保只有在子线程启动后主线程才能继续?

如何确保只有我的 javascript 客户端 Web 应用程序向 REST API 发出请求?