我需要确保只运行一个C ++应用程序实例。
使用Win API我该怎么做;
检索有关我当前应用程序的信息?
GetCurrentProcess()
会在我的应用程序上给我一个处理,如何检索有关它的信息
检索用户的所有正在运行的进程的列表?
EnumProcesses()
给出了一个列表,但似乎需要预先分配的缓冲区,那么如何找出当前正在运行的进程数?
我需要将服务器的exe名称与正在运行的进程进行比较,如果找到多个
,则需要引发错误 注意:我无法使用任何提升库,我对使用mutex
不感兴趣,在类似帖子中看到。
答案 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);
调用成功且映射已存在,则另一个进程已在运行。
或者,创建新映射,或者调用失败。在任何一种情况下,都没有其他进程在运行。如果调用失败,那么对于之前可能尝试过的任何其他进程来说几乎肯定都会失败。由于一旦调用成功,映射已经存在,两个相同的调用可能成功一次然后失败的唯一可能原因是“不再有句柄”,而这只是没有(好吧,不应该)发生了。无论如何,如果确实发生了这种情况,那么你在其他地方就会遇到更严重的问题。
这个东西可能适用于你选择的每种类型的命名内核对象(即同时具有Create
和Open
版本的每种类型的内核对象。
文件映射对象的优点是,如果您还想进行IPC(例如,将命令行转发到已经运行的实例,然后退出),那么您已经有了一个可以使用的映射(尽管肯定是管道也会很好。)
但除此之外,我不知道这个(或任何其他解决方案)如何以任何方式优于使用互斥方法。真的,为什么不使用互斥?这就是他们的目的。