【问题标题】:How to Avoid Multiple Instances of Different Users but Allow Multiple Instances on Single User Session如何避免不同用户的多个实例但允许单个用户会话上的多个实例
【发布时间】:2015-01-30 16:30:48
【问题描述】:

我有一个 Windows 应用程序。我想为单个用户会话允许多个实例,但我不希望来自不同用户的多个实例。简单来说,如果 A 登录到 Windows,那么他可以运行任意数量的应用程序,但后来 B 也登录了,他应该等到 A 的所有应用程序都关闭。

这可能吗?

【问题讨论】:

  • 这是安全需求还是业务需求?是否涉及服务器?您可以管理 Windows 安全策略吗?
  • 不涉及服务器。我希望纯粹在客户端检查应用程序
  • 你没有回答第一个和最后一个问题?还有你在开发什么类型的应用程序c#? Win32?
  • 这是业务需求。我不知道 Windows 安全策略。我们可以使用 C# 或 C++。
  • 使用互斥锁很容易做到。或者最坏的情况是互斥体和内存映射文件

标签: .net c windows winapi windows-applications


【解决方案1】:

可以使用全局Kernel Object Namespace 中的命名Mutex Object 来完成此要求。使用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 返回ERROR_SUCCESS (0)。后续实例将获取对现有互斥对象的引用,GetLastError 返回ERROR_ALREADY_EXISTS (183)。从另一个客户端会话启动的实例不会获取对互斥对象的引用,GetLastError 返回ERROR_ACCESS_DENIED (5)。

关于实现的几点说明:

  • 通过添加 "Global\" 前缀,在全局内核对象命名空间中创建互斥对象。
  • 互斥对象必须有一个唯一的名称才能从任何地方引用它。创建唯一名称的最简单方法是使用 GUID 的字符串表示形式(可以使用 Visual Studio 的 guidgen.exe 工具部分生成 GUID)。 不要使用示例代码中的 GUID,因为其他人也会这样做。
  • 没有调用CloseHandle 来释放互斥对象。这是故意的。 (参见CreateMutex进程终止时系统自动关闭句柄。互斥对象在其最后一个句柄关闭时销毁。
  • 互斥对象是使用默认安全描述符创建的。默认安全描述符中的 ACL 来自创建者的主要或模拟令牌。如果不同客户端会话中的进程使用相同的主/模拟令牌运行,则仍然可以从两个会话中获取互斥锁。在这种情况下,建议的解决方案可能不符合要求。如果用户运行应用程序冒充不同客户端会话的用户,会发生什么情况尚不清楚。
  • 互斥对象纯粹用作哨兵,不参与同步。

【讨论】:

  • 谢谢...直到我在服务器上尝试并按预期工作时,我才相信这段代码。
【解决方案2】:

我在win10/VS 2017 / 64 bits下使用过之前的代码

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

【讨论】:

  • 这没有实现问题中阐明的要求。值得注意的是,它不允许来自同一个用户会话的多个实例。此代码仅允许在所有用户会话中使用 single 实例。虽然是一个常见的要求,但它不适合这个问题。
  • 另外,在CreateMutex返回非空值的情况下,错误信息是错误的,错误代码是ERROR_ALREADY_EXISTS。在这种情况下,已获取互斥体 ,但输出显示为 “未获取互斥体”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多