Windows服务:获取每个用户的%APPDATA%变量

时间:2013-10-08 16:05:31

标签: c winapi windows-services environment-variables setuid

我的应用程序作为服务运行,在某些时候它需要循环遍历系统上的所有现有用户帐户(我认为这可以通过NetUserEnum()完成)并尝试访问每个找到的用户下的某个文件%APPDATA%路径。问题是我不知道如何获得该用户特定的路径(%APPDATA%)。

由于应用程序作为服务(SYSTEM)运行,因此我无法使用环境或SHGetFolderPath()。最初我以为我可以使用LogonUser()但它总是向我抛出错误1326,无论我在用户,管理员还是SYSTEM下运行我的应用程序测试代码。 (winxp作为测试平台)。如果有办法获取用户登录句柄,我可以在SHGetFolderPath()ExpandEnvironmentStringsForUser() API中使用它,这是正确的吗?

所以,到目前为止,我尝试使用LogonUser()的代码是关于以下内容(是的,用户名是正确的):

LogonUser(
    pw->usri1_name,
    L".",
    NULL,
    LOGON32_LOGON_BATCH,
    LOGON32_PROVIDER_DEFAULT,
    &authtoken
)

它可能需要我的密码,但我无法在客户机器上知道这一点。我通过快速搜索找到的所有API都依赖于来自LogonUser()的HANDLE,我显然不能拥有......

欢迎任何非棘手和棘手的想法!

3 个答案:

答案 0 :(得分:0)

到目前为止的解决方案和结论。

您无法正确使用LogonUser()

相反,您的选项来自强制用户homedir之后的路径(您仍然通过任何首选方法获得,我的建议为NetUserEnum()),基于以下之一: / p>

  • SHGetFolderPath(CSIDL_APPDATA)
  • reg key HKLM\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders

如果您知道任何其他方法,请发表评论。

答案 1 :(得分:0)

  

服务或应用程序作为“本地系统”运行以获取特殊文件夹

这个例子我们得到 CSIDL_DESKTOPDIRECTORY ,在xp,win7(32,64)上运行良好

DWORD ServiceGetDesktopDirectory(LPWSTR lpUserName, LPWSTR lpPassword,
    LPWSTR lpDomain, LPWSTR lpBuffer)
{

HANDLE hToken;
BOOL bRet;
bRet = LogonUserW(lpUserName,
                 lpDomain,
                 lpPassword,
                 LOGON32_LOGON_INTERACTIVE,
                 LOGON32_PROVIDER_DEFAULT,
                 &hToken);
if (!bRet) {
    error("LogonUser failed, gle = %lu", GetLastError());
    return FILE_ERR_INVALID_USERNAME_OR_PASSWORD;
}


NET_API_STATUS ntStatus;
USER_INFO_4 *pUserInfo;
ntStatus = NetUserGetInfo((LPCWSTR)lpDomain,
                          (LPCWSTR)lpUserName,
                          4,
                          (BYTE**)&pUserInfo);
if (ntStatus != NERR_Success) {
    error("NetUserGetInfo failed, ntStatus 0X%x", ntStatus);
    CloseHandle(hToken);
    return FILE_ERR_SYSTEM_ERROR;
}

PROFILEINFOW profile;
memset(&profile, 0, sizeof(PROFILEINFOW));
profile.dwSize = sizeof(PROFILEINFOW);
profile.lpUserName = lpUserName;
profile.lpProfilePath = pUserInfo->usri4_profile;
bRet = LoadUserProfileW(hToken, &profile);
if (!bRet) {
    error("LoadUserProfile failed, gle = %lu", GetLastError());
    CloseHandle(hToken);
    NetApiBufferFree(pUserInfo);
    return FILE_ERR_SYSTEM_ERROR;
}


HRESULT hr;
hr = SHGetFolderPathW(NULL,
              CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE,
              hToken,
              0,
              lpBuffer);
if (FAILED(hr)) {
    error("SHGetFolderPath failed, hr 0X%x", hr);
    NetApiBufferFree(pUserInfo);
    UnloadUserProfile(hToken, profile.hProfile);
    CloseHandle(hToken);
    return FILE_ERR_SYSTEM_ERROR;
}

NetApiBufferFree(pUserInfo);
UnloadUserProfile(hToken, profile.hProfile);
CloseHandle(hToken);
return FILE_ERR_OK;
}

答案 2 :(得分:-1)

您可以使用WTSEnumerateSessions()WTSQueryUserToken()来检索每个登录会话的令牌。

另请查看LoadUserProfile(),这是服务访问特定用户的HKEY_CURRENT_USER密钥所必需的。用户必须以交互方式或可编程方式登录,或者模拟以获取所需的用户令牌。

另一个选项是枚举HKEY_USERS密钥以检索用户帐户的SID,然后使用LookupAccountSid()检索其用户名,然后根据操作系统版本手动格式化AppData路径。不那么灵活,但不太依赖于特定于用户的注册数据。

否则,忘记尝试从服务写入特定于用户的文件夹。请改为使用常见的共享文件夹,例如CSIDL_COMMON_APPDATAFOLDERID_ProgramData