当由 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 处创建了一个新的互斥体,所以它们无论如何都没有访问同一个对象。