如何避免不同用户的多个实例但允许单个用户会话上的多个实例
How to Avoid Multiple Instances of Different Users but Allow Multiple Instances on Single User Session
我有一个 windows 应用程序。我想为单个用户会话允许多个实例,但我不希望来自不同用户的多个实例。简而言之,如果 A 登录 Windows,那么他可以 运行 申请任意数量的实例,但稍后 B 也登录,他应该等到 A 的所有申请都关闭.
这可能吗?
此要求可以使用命名 Mutex Object in the global Kernel Object Namespace. A mutex object is created using the CreateMutex function 来完成。下面用一个小程序来说明它的用法:
int _tmain(int argc, _TCHAR* argv[]) {
if ( ::CreateMutexW( NULL,
FALSE,
L"Global\5BDC0675-2318-404A-96CA-DBDE9BC2F71D" ) != NULL ) {
auto const err{ GetLastError() };
std::wcout << L"Mutex acquired. GLE = " << err << std::endl;
// Continue execution
} else {
auto const err{ GetLastError() };
std::wcout << L"Mutex not acquired. GLE = " << err << std::endl;
// Exit application
}
_getch();
return 0;
}
第一个应用程序实例将创建互斥对象,并且GetLastError returns ERROR_SUCCESS
(0)。后续实例将获取对现有互斥对象的引用,以及 GetLastError returns ERROR_ALREADY_EXISTS
(183)。从另一个客户端会话启动的实例将不会获取对互斥对象的引用,并且 GetLastError returns ERROR_ACCESS_DENIED
(5).
关于实施的一些注意事项:
- 在全局内核对象命名空间中通过添加 "Global\" 前缀创建互斥对象。
- 互斥对象必须有一个唯一的名称才能从任何地方引用它。创建唯一名称的最简单方法是使用 GUID 的字符串表示形式(可以使用 Visual Studio 的 guidgen.exe 工具部分生成 GUID)。 不要使用示例代码中的 GUID,因为其他人也会。
- 没有调用CloseHandle to release the mutex object. This is intentional. (See CreateMutex: 进程终止时系统自动关闭句柄。互斥锁对象在其最后一个句柄关闭后被销毁。)
- 互斥对象是使用默认安全描述符创建的。默认安全描述符中的 ACL 来自创建者的主要或模拟令牌。如果在不同的客户端会话 运行 中使用相同的 primary/impersonation 令牌进行处理,仍然可以从两个会话中获取互斥量。在这种情况下,建议的解决方案可能不符合要求。如果用户 运行 的应用程序冒充不同客户端会话的用户,目前还不清楚会发生什么。
- 互斥对象纯粹用作哨兵,不参与同步。
我在 win10/VS 2017 / 64 位
下使用了以前的代码
针对 if (::CreateMutex..
进行测试是错误的
我们必须检查错误,所以使用:
BOOL checkUniqueInstance()
{
if (::CreateMutexW(NULL,
FALSE,
L"Global\27828F4B-5FC9-40C3-9E81-6C485020538F") != NULL) {
auto err = GetLastError();
if (err == 0) {
return TRUE;
}
}
CString msg;
msg.Format(_T("err: %d - Mutex NOT acquired"), GetLastError());
OutputDebugString(msg);
// Exit application
return FALSE;
}
我有一个 windows 应用程序。我想为单个用户会话允许多个实例,但我不希望来自不同用户的多个实例。简而言之,如果 A 登录 Windows,那么他可以 运行 申请任意数量的实例,但稍后 B 也登录,他应该等到 A 的所有申请都关闭.
这可能吗?
此要求可以使用命名 Mutex Object in the global Kernel Object Namespace. A mutex object is created using the CreateMutex function 来完成。下面用一个小程序来说明它的用法:
int _tmain(int argc, _TCHAR* argv[]) {
if ( ::CreateMutexW( NULL,
FALSE,
L"Global\5BDC0675-2318-404A-96CA-DBDE9BC2F71D" ) != NULL ) {
auto const err{ GetLastError() };
std::wcout << L"Mutex acquired. GLE = " << err << std::endl;
// Continue execution
} else {
auto const err{ GetLastError() };
std::wcout << L"Mutex not acquired. GLE = " << err << std::endl;
// Exit application
}
_getch();
return 0;
}
第一个应用程序实例将创建互斥对象,并且GetLastError returns ERROR_SUCCESS
(0)。后续实例将获取对现有互斥对象的引用,以及 GetLastError returns ERROR_ALREADY_EXISTS
(183)。从另一个客户端会话启动的实例将不会获取对互斥对象的引用,并且 GetLastError returns ERROR_ACCESS_DENIED
(5).
关于实施的一些注意事项:
- 在全局内核对象命名空间中通过添加 "Global\" 前缀创建互斥对象。
- 互斥对象必须有一个唯一的名称才能从任何地方引用它。创建唯一名称的最简单方法是使用 GUID 的字符串表示形式(可以使用 Visual Studio 的 guidgen.exe 工具部分生成 GUID)。 不要使用示例代码中的 GUID,因为其他人也会。
- 没有调用CloseHandle to release the mutex object. This is intentional. (See CreateMutex: 进程终止时系统自动关闭句柄。互斥锁对象在其最后一个句柄关闭后被销毁。)
- 互斥对象是使用默认安全描述符创建的。默认安全描述符中的 ACL 来自创建者的主要或模拟令牌。如果在不同的客户端会话 运行 中使用相同的 primary/impersonation 令牌进行处理,仍然可以从两个会话中获取互斥量。在这种情况下,建议的解决方案可能不符合要求。如果用户 运行 的应用程序冒充不同客户端会话的用户,目前还不清楚会发生什么。
- 互斥对象纯粹用作哨兵,不参与同步。
我在 win10/VS 2017 / 64 位
下使用了以前的代码针对 if (::CreateMutex..
进行测试是错误的我们必须检查错误,所以使用:
BOOL checkUniqueInstance()
{
if (::CreateMutexW(NULL,
FALSE,
L"Global\27828F4B-5FC9-40C3-9E81-6C485020538F") != NULL) {
auto err = GetLastError();
if (err == 0) {
return TRUE;
}
}
CString msg;
msg.Format(_T("err: %d - Mutex NOT acquired"), GetLastError());
OutputDebugString(msg);
// Exit application
return FALSE;
}