使用Win API确定可执行文件的实例是否已在运行

时间:2013-08-12 09:50:01

标签: c++ winapi

我需要确保只运行一个C ++应用程序实例。

使用Win API我该怎么做;

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

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

  3. 我需要将服务器的exe名称与正在运行的进程进行比较,如果找到多个

    ,则需要引发错误

  4. 注意:我无法使用任何提升库,我对使用mutex不感兴趣,在类似帖子中看到。

3 个答案:

答案 0 :(得分:8)

您可以使用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_NEW字段为dwCreationDisposition(以便仅在文件不存在时才创建文件)和{ {1}} FILE_FLAG_DELETE_ON_CLOSE参数,以便在您的进程终止后删除文件。像这样:

dwFlagsAndAttributes

请参阅有关Temporary file generation and usage best practices的文章,了解如何安全地处理临时文件。

总而言之,肯定可以使用锁定文件来完成任务,但我认为这样做更难。

答案 1 :(得分: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 documentation可以说明单实例应用程序:

  

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

答案 2 :(得分:1)

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

  

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

这导致以下no-mutex实现:

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

调用成功且映射已存在,则另一个进程已在运行。

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

这个东西可能适用于你选择的每种类型的命名内核对象(即同时具有CreateOpen版本的每种类型的内核对象。

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

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