如何避免不同用户的多个实例但允许单个用户会话上的多个实例

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;
}