C++ 互斥体未跨 windows 个帐户锁定
C++ mutex not locking across windows accounts
当我 运行 我的 C++ 程序时,winAPI 互斥锁正确锁定,所以如果我尝试 运行 第二个实例,它会检查我是否可以打开锁定的互斥锁:
OpenMutex(SYNCHRONIZE, FALSE, mutexName.c_str());
我得到了预期的错误:
mutexName << " is already running on this machine! Aborting!";
到目前为止一切顺利
问题是当我有程序 运行ning 的 1 个实例并切换到不同的 windows 帐户时。互斥锁应该被锁定,但是当我 运行 第二个实例时它忽略了互斥锁被锁定并且无论如何启动!
所以我的问题是针对这一行的:
m_hMutex = ::OpenMutex(SYNCHRONIZE, FALSE, mutexName.c_str());
当它是 运行 第二次时为什么它设置 m_hMutex != NULL 当第二个实例是 运行 在同一个 windows 帐户上...
但当第二个实例在新帐户上 运行 时设置 m_hMutex == NULL?
两种情况下函数参数完全一样,这段代码是运行在D:\
每个登录的用户都在自己的桌面会话中运行。为了跨用户会话访问 named 内核对象,如互斥锁,您需要在对象名称前加上 Global\
命名空间前缀,否则您最终会创建单独的每个用户会话中的本地互斥对象。
根据 CreateMutex()
and OpenMutex()
文档:
The name can have a "Global" or "Local" prefix to explicitly create the object in the global or session namespace. The remainder of the name can contain any character except the backslash character. For more information, see Kernel Object Namespaces.
根据 Kernel Object Namespaces 文档:
The separate client session namespaces enable multiple clients to run the same applications without interfering with each other. For processes started under a client session, the system uses the session namespace by default. However, these processes can use the global namespace by prepending the "Global\"
prefix to the object name. For example, the following code calls `CreateEvent and creates an event object named CSAPP in the global namespace:
CreateEvent( NULL, FALSE, FALSE, "Global\CSAPP" );
...
Another use of the global namespace is for applications that use named objects to detect that there is already an instance of the application running in the system across all sessions. This named object must be created or opened in the global namespace instead of the per-session namespace. The more common case of running the application once per session is supported by default because the named object is created in a per session namespace.
此外,您应该使用 CreateMutex()
而不是 OpenMutex()
来避免竞争条件,这种竞争条件会允许您的应用程序的另一个实例在当前实例有机会创建互斥量之前创建它。如果 CreateMutex()
成功并且互斥锁已经存在,GetLastError()
将报告 ERROR_ALREADY_EXISTS
。除非 CreateMutex()
报告 ERROR_ACCESS_DENIED
错误,否则不要调用 OpenMutex()
,根据 CreateMutex
documentation:
If lpName
matches the name of an existing named mutex object, this function requests the MUTEX_ALL_ACCESS
access right. In this case, the bInitialOwner
parameter is ignored because it has already been set by the creating process.
...
If the mutex is a named mutex and the object existed before this function call, the return value is a handle to the existing object, GetLastError
returns ERROR_ALREADY_EXISTS
, bInitialOwner
is ignored, and the calling thread is not granted ownership. However, if the caller has limited access rights, the function will fail with ERROR_ACCESS_DENIED
and the caller should use the OpenMutex
function.
尝试这样的事情:
mutexName = "Global\MyMutexName";
m_hMutex = ::CreateMutex(NULL, FALSE, mutexName.c_str());
if ((!m_hMutex) && (GetLastError() == ERROR_ACCESS_DENIED)) {
m_hMutex = ::OpenMutex(SYNCHRONIZE, FALSE, mutexName.c_str());
}
if (!m_hMutex) {
... << mutexName << " cannot be accessed! Aborting!";
//...
}
else if (GetLastError() == ERROR_ALREADY_EXISTS) {
... << mutexName << " is already running on this machine! Aborting!";
//...
}
else {
//...
}
或者,在 Vista 及更高版本上,您可以使用 CreateMutexEx()
代替:
mutexName = "Global\MyMutexName";
m_hMutex = ::CreateMutexEx(NULL, mutexName.c_str(), 0, SYNCHRONIZE);
if (!m_hMutex) {
... << mutexName << " cannot be accessed! Aborting!";
//...
}
else if (GetLastError() == ERROR_ALREADY_EXISTS) {
... << mutexName << " is already running on this machine! Aborting!";
//...
}
else {
//...
}
问题是 Windows 为各种内核对象维护单独的名称空间。一个命名空间中的对象可以与另一个命名空间中的对象同名,而不会相互干扰。如果您希望一个对象在所有会话之间共享,那么您必须在全局命名空间中创建它。
来自 MSDN
The separate client session namespaces enable multiple clients to run the same applications without interfering with each other. For processes started under a client session, the system uses the session namespace by default. However, these processes can use the global namespace by prepending the "Global\" prefix to the object name.
当我 运行 我的 C++ 程序时,winAPI 互斥锁正确锁定,所以如果我尝试 运行 第二个实例,它会检查我是否可以打开锁定的互斥锁:
OpenMutex(SYNCHRONIZE, FALSE, mutexName.c_str());
我得到了预期的错误:
mutexName << " is already running on this machine! Aborting!";
到目前为止一切顺利
问题是当我有程序 运行ning 的 1 个实例并切换到不同的 windows 帐户时。互斥锁应该被锁定,但是当我 运行 第二个实例时它忽略了互斥锁被锁定并且无论如何启动!
所以我的问题是针对这一行的:
m_hMutex = ::OpenMutex(SYNCHRONIZE, FALSE, mutexName.c_str());
当它是 运行 第二次时为什么它设置 m_hMutex != NULL 当第二个实例是 运行 在同一个 windows 帐户上...
但当第二个实例在新帐户上 运行 时设置 m_hMutex == NULL?
两种情况下函数参数完全一样,这段代码是运行在D:\
每个登录的用户都在自己的桌面会话中运行。为了跨用户会话访问 named 内核对象,如互斥锁,您需要在对象名称前加上 Global\
命名空间前缀,否则您最终会创建单独的每个用户会话中的本地互斥对象。
根据 CreateMutex()
and OpenMutex()
文档:
The name can have a "Global" or "Local" prefix to explicitly create the object in the global or session namespace. The remainder of the name can contain any character except the backslash character. For more information, see Kernel Object Namespaces.
根据 Kernel Object Namespaces 文档:
The separate client session namespaces enable multiple clients to run the same applications without interfering with each other. For processes started under a client session, the system uses the session namespace by default. However, these processes can use the global namespace by prepending the
"Global\"
prefix to the object name. For example, the following code calls `CreateEvent and creates an event object named CSAPP in the global namespace:CreateEvent( NULL, FALSE, FALSE, "Global\CSAPP" );
...
Another use of the global namespace is for applications that use named objects to detect that there is already an instance of the application running in the system across all sessions. This named object must be created or opened in the global namespace instead of the per-session namespace. The more common case of running the application once per session is supported by default because the named object is created in a per session namespace.
此外,您应该使用 CreateMutex()
而不是 OpenMutex()
来避免竞争条件,这种竞争条件会允许您的应用程序的另一个实例在当前实例有机会创建互斥量之前创建它。如果 CreateMutex()
成功并且互斥锁已经存在,GetLastError()
将报告 ERROR_ALREADY_EXISTS
。除非 CreateMutex()
报告 ERROR_ACCESS_DENIED
错误,否则不要调用 OpenMutex()
,根据 CreateMutex
documentation:
If
lpName
matches the name of an existing named mutex object, this function requests theMUTEX_ALL_ACCESS
access right. In this case, thebInitialOwner
parameter is ignored because it has already been set by the creating process....
If the mutex is a named mutex and the object existed before this function call, the return value is a handle to the existing object,
GetLastError
returnsERROR_ALREADY_EXISTS
,bInitialOwner
is ignored, and the calling thread is not granted ownership. However, if the caller has limited access rights, the function will fail withERROR_ACCESS_DENIED
and the caller should use theOpenMutex
function.
尝试这样的事情:
mutexName = "Global\MyMutexName";
m_hMutex = ::CreateMutex(NULL, FALSE, mutexName.c_str());
if ((!m_hMutex) && (GetLastError() == ERROR_ACCESS_DENIED)) {
m_hMutex = ::OpenMutex(SYNCHRONIZE, FALSE, mutexName.c_str());
}
if (!m_hMutex) {
... << mutexName << " cannot be accessed! Aborting!";
//...
}
else if (GetLastError() == ERROR_ALREADY_EXISTS) {
... << mutexName << " is already running on this machine! Aborting!";
//...
}
else {
//...
}
或者,在 Vista 及更高版本上,您可以使用 CreateMutexEx()
代替:
mutexName = "Global\MyMutexName";
m_hMutex = ::CreateMutexEx(NULL, mutexName.c_str(), 0, SYNCHRONIZE);
if (!m_hMutex) {
... << mutexName << " cannot be accessed! Aborting!";
//...
}
else if (GetLastError() == ERROR_ALREADY_EXISTS) {
... << mutexName << " is already running on this machine! Aborting!";
//...
}
else {
//...
}
问题是 Windows 为各种内核对象维护单独的名称空间。一个命名空间中的对象可以与另一个命名空间中的对象同名,而不会相互干扰。如果您希望一个对象在所有会话之间共享,那么您必须在全局命名空间中创建它。
来自 MSDN
The separate client session namespaces enable multiple clients to run the same applications without interfering with each other. For processes started under a client session, the system uses the session namespace by default. However, these processes can use the global namespace by prepending the "Global\" prefix to the object name.