4

我需要确保我的 C++ 应用程序只有 1 个实例正在运行。

我如何使用 Win API;

  1. 检索有关我当前应用程序的信息? GetCurrentProcess()会给我一个关于我的应用程序的句柄,我如何检索有关它的信息

  2. 检索用户所有正在运行的进程的列表? EnumProcesses()给出了一个列表,但似乎需要一个预先分配的缓冲区,那么我如何找出当前正在运行的进程数?

  3. 我需要将我的服务器的 exe 名称与正在运行的进程进行比较,如果发现多个,则会引发错误

注意:我不能使用任何 boost 库,并且我对使用mutex类似帖子中看到的 , 不感兴趣。

4

3 回答 3

10

您可以使用该CreateMutex函数创建一个系统范围的命名互斥锁来表示您的进程是否正在运行。ERROR_ALREADY_EXISTS如果进程已经在运行,它将返回:

 (void)::CreateMutex( NULL,
                      TRUE,
                      TEXT( "My_Special_Invokation_Test_Mutex" ) );
 switch ( ::GetLastError() ) {
     case ERROR_SUCCESS:
         // Process was not running already
         break;
     case ERROR_ALREADY_EXISTS:
         // Process is running already
         break;
     default:
         // Error occured, not sure whether process is running already.
         break;
 }

现在,如果您坚持不使用互斥锁,则可以使用该CreateFile函数。确保为dwShareMode字段传递零以获得独占访问语义,CREATE_NEWdwCreationDisposition字段传递零(以便仅在文件不存在时创建文件)和参数传递,以便FILE_FLAG_DELETE_ON_CLOSEdwFlagsAndAttributes进程终止后删除文件。像这样的东西:

LPCTSTR lockFileName = ...;
(void)::CreateFile( lockFileName,
                    GENERIC_READ,
                    0,
                    NULL,
                    CREATE_NEW,
                    FILE_FLAG_DELETE_ON_CLOSE,
                    NULL );
switch ( ::GetLastError() ) {
     case ERROR_SUCCESS:
         // Process was not running already
         break;
     case ERROR_FILE_EXISTS:
         // Process is running already
         break;
     default:
         // Error occured, not sure whether process is running already.
         break;
}

请参阅这篇关于临时文件生成和使用最佳实践的文章,了解如何安全地处理临时文件。

长话短说,当然可以为您的任务使用锁定文件,但我认为正确执行此操作更难。

于 2013-08-12T10:01:38.450 回答
4

Nawaz答案的更新版本:-

Handle mutex = CreateMutex (0, 0, "SomeUniqueName");

switch (GetLastError ())
{
case ERROR_ALREADY_EXISTS:
  // app already running
  break;

case ERROR_SUCCESS:
  // first instance
  break;

default:
  // who knows what happened!
  break;
}

这确实存在安全问题,恶意应用程序可能会在您的应用程序启动之前创建一个名为“SomeUniqueName”的互斥锁,这会阻止您的应用程序运行。为了解决这个问题,您可以根据一些常量系统参数(例如 MAC 地址)的散列来命名互斥锁。MSDN 文档对单实例应用程序有这样的说法:

如果您使用命名互斥锁将应用程序限制为单个实例,则恶意用户可以在您创建此互斥锁之前创建此互斥锁并阻止您的应用程序启动。为防止出现这种情况,请创建一个随机命名的互斥体并将名称存储起来,以便只能由授权用户获取。或者,您可以为此目的使用文件。要将您的应用程序限制为每个用户一个实例,请在用户的配置文件目录中创建一个锁定文件。

于 2013-08-12T10:09:55.770 回答
2

由于不需要互斥锁,您可以例如使用文件映射来代替。CreateFilemapping的文档说:

如果对象在函数调用之前存在,则函数返回现有对象的句柄(具有当前大小,而不是指定大小),GetLastError 返回 ERROR_ALREADY_EXISTS。如果函数失败,则返回值为 NULL。

这导致以下无互斥体实现:

Handle h = CreateFileMapping(0, 0, PAGE_READONLY, 0, 4096, name);
bool already_running = !!h && (GetLastError() == ERROR_ALREADY_EXISTS);

要么调用成功并且映射已经存在,那么另一个进程已经在运行。

或者,创建了新的映射,或者调用失败。在任何一种情况下,都没有其他进程正在运行。如果调用失败,几乎可以肯定之前可能尝试过的任何其他进程也失败了。由于一旦调用成功,映射就已经存在,两个相同的调用可能成功一次然后失败的唯一可能原因是“没有更多的句柄”,而这不会(嗯,不应该)发生。无论如何,如果确实发生了这种情况,那么您在其他地方就会遇到更严重的问题。

那件事可能适用于您选择的每种类型的命名内核对象(即,每种类型的内核对象都有一个Create和一个Open版本)。

文件映射对象的优点是,如果您还想做 IPC(例如,将命令行转发到已经运行的实例,然后退出),那么您已经有了可以使用的映射(尽管管道确实可以也很好)。

但除此之外,我看不出这个(或任何其他解决方案)如何优于以任何方式使用互斥锁方法。真的,为什么不使用互斥锁?这就是他们的目的。

于 2013-08-12T14:13:17.597 回答