当由 Windows 服务生成的进程调用时,CreateMutex() 失败并显示 ERROR_ACCESS_DENIED
CreateMutex() fails with ERROR_ACCESS_DENIED when called by process spawned by Windows Service
我刚刚花了几天时间来追踪一个错误,该错误是由于调用 ::CreateMutex() 失败而引发未处理的异常。请记住 CreateMutex()
不会尝试锁定互斥量,只是为了打开一个句柄。 即使互斥量已经存在也不应该失败,至少只要我将 bInitialOwner 设置为 FALSE,我就这样做了。
但是,如果我从 Windows 服务生成的程序中调用 ::CreateMutexA(0, 0, "testtesttest123");
(w3wp.exe
是它的父级,scvhost.exe
是它的祖父级,它 运行s under IIS APPPOOL\Worker1
user-account),然后它失败并且 returns NULL 如果互斥量已经被另一个进程 运行ning 在 IIS APPPOOL\*
下创建。
我创建了一个重现此问题的最小示例:
void main()
{
HANDLE handle = CreateMutexA(0, 0, "testtesttest123");
std::string lastError = MyGetLastErrorAsText();
MyAppendToLog("%p %s\n", handle, lastError.c_str());
Sleep(INFINITE);
}
我通过从资源管理器(普通用户帐户)双击 运行 可执行文件 2 次,然后使其由 Windows 服务生成,然后再次 运行 它来自资源管理器。
这将创建以下日志:
C:\Test\ReproduceMutexCrash.exe: 00000034 ERROR_SUCCESS
C:\Test\ReproduceMutexCrash.exe: 00000034 ERROR_ALREADY_EXISTS
c:\inetpub\wwwroot\Worker1\ReproduceMutexCrash.exe: 00000034 ERROR_SUCCESS
c:\inetpub\wwwroot\Worker2\ReproduceMutexCrash.exe: 00000000 ERROR_ACCESS_DENIED
C:\Test\ReproduceMutexCrash.exe: 00000034 ERROR_ALREADY_EXISTS
这里的工人 1 运行 在 IIS APPPOOL\Worker1
下,而工人 2 运行 在 IIS APPPOOL\Worker2
用户帐户下。
从日志中可以明显看出,如果互斥锁是由普通用户进程创建的,这不会阻止另一个用户进程甚至 Windows 服务打开句柄。但是,如果 Windows 服务创建了一个互斥量,那么 ::CreateMutex()
在被另一个 Windows 服务调用时会失败,尽管用户进程仍然可以顺利打开句柄。
有人知道为什么会这样吗?
编辑: 我看到当进程由 Explorer 生成时互斥对象为 \Sessions\BaseNamedObjects\testtesttest123
,而当进程由创建时为 \BaseNamedObjects\testtesttest123
IIS
,所有者是 Worker1
(第一个设法创建互斥锁的进程)。
感谢评论的帮助,我终于弄明白了为什么会这样。这确实是因为权限问题,并且因为进程来自不同的用户帐户 运行。我通过 运行 2 个单独的进程验证了这一点,但是来自同一个用户帐户 - IIS APPPOOL\Worker1
,并且对于两个 ::CreateMutex()
都有效并返回了一个有效的 HANDLE。
仅当互斥量已被 IIS APPPOOL\Worker1
下的进程锁定并且 IIS APPPOOL\Worker2
下的进程试图访问它时才会失败。
此外,我尝试在没有任何安全措施的情况下创建互斥锁,by creating an empty DACL:
SECURITY_ATTRIBUTES sa = { sizeof(sa) };
SECURITY_DESCRIPTOR SD;
InitializeSecurityDescriptor(&SD, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&SD, TRUE, NULL, FALSE);
sa.lpSecurityDescriptor = &SD;
HANDLE mutex = CreateMutexA(&sa, 0, "testtesttest123");
使用空 DACL,来自不同用户帐户的 2 个进程可以打开同一个互斥体。
此外,用户进程创建的互斥量没有导致 ERROR_ACCESS_DENIED 的原因是它在不同的范围内创建互斥量 - \Sessions\BaseNamedObjects\testtesttest123
,因此没有权限- 冲突,因为 Web 服务进程在 \BaseNamedObjects\testtesttest123
处创建了一个新的互斥体,所以它们无论如何都没有访问同一个对象。
我刚刚花了几天时间来追踪一个错误,该错误是由于调用 ::CreateMutex() 失败而引发未处理的异常。请记住 CreateMutex()
不会尝试锁定互斥量,只是为了打开一个句柄。 即使互斥量已经存在也不应该失败,至少只要我将 bInitialOwner 设置为 FALSE,我就这样做了。
但是,如果我从 Windows 服务生成的程序中调用 ::CreateMutexA(0, 0, "testtesttest123");
(w3wp.exe
是它的父级,scvhost.exe
是它的祖父级,它 运行s under IIS APPPOOL\Worker1
user-account),然后它失败并且 returns NULL 如果互斥量已经被另一个进程 运行ning 在 IIS APPPOOL\*
下创建。
我创建了一个重现此问题的最小示例:
void main()
{
HANDLE handle = CreateMutexA(0, 0, "testtesttest123");
std::string lastError = MyGetLastErrorAsText();
MyAppendToLog("%p %s\n", handle, lastError.c_str());
Sleep(INFINITE);
}
我通过从资源管理器(普通用户帐户)双击 运行 可执行文件 2 次,然后使其由 Windows 服务生成,然后再次 运行 它来自资源管理器。
这将创建以下日志:
C:\Test\ReproduceMutexCrash.exe: 00000034 ERROR_SUCCESS C:\Test\ReproduceMutexCrash.exe: 00000034 ERROR_ALREADY_EXISTS c:\inetpub\wwwroot\Worker1\ReproduceMutexCrash.exe: 00000034 ERROR_SUCCESS c:\inetpub\wwwroot\Worker2\ReproduceMutexCrash.exe: 00000000 ERROR_ACCESS_DENIED C:\Test\ReproduceMutexCrash.exe: 00000034 ERROR_ALREADY_EXISTS
这里的工人 1 运行 在 IIS APPPOOL\Worker1
下,而工人 2 运行 在 IIS APPPOOL\Worker2
用户帐户下。
从日志中可以明显看出,如果互斥锁是由普通用户进程创建的,这不会阻止另一个用户进程甚至 Windows 服务打开句柄。但是,如果 Windows 服务创建了一个互斥量,那么 ::CreateMutex()
在被另一个 Windows 服务调用时会失败,尽管用户进程仍然可以顺利打开句柄。
有人知道为什么会这样吗?
编辑: 我看到当进程由 Explorer 生成时互斥对象为 \Sessions\BaseNamedObjects\testtesttest123
,而当进程由创建时为 \BaseNamedObjects\testtesttest123
IIS
,所有者是 Worker1
(第一个设法创建互斥锁的进程)。
感谢评论的帮助,我终于弄明白了为什么会这样。这确实是因为权限问题,并且因为进程来自不同的用户帐户 运行。我通过 运行 2 个单独的进程验证了这一点,但是来自同一个用户帐户 - IIS APPPOOL\Worker1
,并且对于两个 ::CreateMutex()
都有效并返回了一个有效的 HANDLE。
仅当互斥量已被 IIS APPPOOL\Worker1
下的进程锁定并且 IIS APPPOOL\Worker2
下的进程试图访问它时才会失败。
此外,我尝试在没有任何安全措施的情况下创建互斥锁,by creating an empty DACL:
SECURITY_ATTRIBUTES sa = { sizeof(sa) };
SECURITY_DESCRIPTOR SD;
InitializeSecurityDescriptor(&SD, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&SD, TRUE, NULL, FALSE);
sa.lpSecurityDescriptor = &SD;
HANDLE mutex = CreateMutexA(&sa, 0, "testtesttest123");
使用空 DACL,来自不同用户帐户的 2 个进程可以打开同一个互斥体。
此外,用户进程创建的互斥量没有导致 ERROR_ACCESS_DENIED 的原因是它在不同的范围内创建互斥量 - \Sessions\BaseNamedObjects\testtesttest123
,因此没有权限- 冲突,因为 Web 服务进程在 \BaseNamedObjects\testtesttest123
处创建了一个新的互斥体,所以它们无论如何都没有访问同一个对象。