为什么在系统服务中运行的此代码中 LoadUserProfile() 失败并出现错误 5“拒绝访问”?

Fin*_*nix 0 c++ windows winapi visual-studio

我有一段代码在我的系统级 Windows 服务中运行,如下所示。如果我将 CreateEnvironmentBlock() 和 CreateProcessAsUser() 添加到代码中,那就可以了。但我使用“LoadUserProfile()”,它失败并出现错误 5,这应该意味着“访问被拒绝”。请帮忙检查一下缺少什么。我想要的是从此系统服务检索用户级注册表值。代码中的注释是另一种方式,但也未能检索到用户级注册表值。

void GetUserRegistry()
{
#ifdef Q_OS_WIN

    DWORD lastError = 0;

    DWORD sessionId = WTSGetActiveConsoleSessionId();
    qInfo() << "Session ID = " << sessionId;

    wchar_t* ppUserName[100];
    DWORD sizeOfUserName;
    WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, sessionId, WTSUserName, ppUserName, &sizeOfUserName);
    qInfo() << "Windows User Name = " << QString::fromWCharArray(*ppUserName);

    std::wstring strValueOfBinDir = L"Unknown Value";
    // LONG regOpenResult = ERROR_SUCCESS;

    HANDLE hUserToken = NULL;
    HANDLE hFakeToken = NULL;

    if (WTSQueryUserToken(sessionId, &hUserToken))
    {
         if (DuplicateTokenEx(hUserToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, 0, SecurityImpersonation, TokenPrimary, &hFakeToken) == TRUE)
         {
            qInfo() << "Before ImpersonateLoggedOnUser()......";
            if (ImpersonateLoggedOnUser(hFakeToken))
            {
                HKEY hKey;

                // regOpenResult = RegOpenCurrentUser(KEY_READ, &hKey);

                PROFILEINFO profileInfo;
                ZeroMemory( &profileInfo, sizeof( PROFILEINFO ) );
                profileInfo.dwSize = sizeof( PROFILEINFO );
                profileInfo.lpUserName = *ppUserName;
                // wchar_t roamingPath[] = L"C:\\Users\\Finix";  // L"C:\\Users\\Finix\\AppData\\Roaming";
                // profileInfo.lpProfilePath = roamingPath;

                if (LoadUserProfile(hFakeToken, &profileInfo))
                {
                    HANDLE hProfile = profileInfo.hProfile;

                    RegOpenKeyEx(HKEY_CURRENT_USER,
                                 TEXT("Software\\Baidu\\BaiduYunGuanjia"),
                                 0,
                                 KEY_READ,
                                 &hKey);

                    GetStringRegKey(hKey, TEXT("installDir"), strValueOfBinDir, TEXT("Unknown"));

                    UnloadUserProfile(hFakeToken, hProfile);
                }
                else
                {
                    lastError = GetLastError();
                }

                RevertToSelf();

            }
            else
            {
                qCritical() << "Failed to ImpersonateLoggedOnUser...";
            }

            CloseHandle(hFakeToken);
        }
        else
        {
            qCritical() << "Failed to call DuplicateTokenEx...";
        }

        CloseHandle(hUserToken);
    }
    else
    {
        qCritical() << "Failed to get the user token of session " << sessionId;
    }

    if (lastError)
    {
        qCritical() << "Failed to LoadUserProfile(), The ERROR is " << lastError;
    }

//    if (regOpenResult != ERROR_SUCCESS)
//    {
//        qCritical() << "Failed to call RegOpenCurrentUser(), Error is " << regOpenResult;
//    }

    qInfo() << "The value of Registry is " << QString::fromWCharArray( strValueOfBinDir.c_str() );

#endif
}
Run Code Online (Sandbox Code Playgroud)

Har*_*ton 6

您无法拨打电话的原因LoadUserProfile是您正在冒充用户。 LoadUserProfile需要管理员权限,即您应该在自己的上下文中调用它,而不是在模拟的上下文中。它不是一个供用户加载自己的配置文件的函数,通常系统会代表用户调用它。

您不应该调用的原因LoadUserProfile是用户已经登录,因此配置文件已经加载。我们知道相关用户已登录,因为WTSQueryUserToken无法用于为未登录的用户获取令牌。

(嗯,可能存在用户当前正在登录或注销的边缘情况。但是LoadUserProfile在登录/注销期间调用该函数在任何情况下都可能是不明智的。)

如果您只想打开登录用户的注册表配置单元,请使用RegOpenCurrentUser. (请注意,您确实希望对此函数使用模拟,如文档所述。)