Question

My application runs as service and at some point it needs to loop through all existing user accounts on the system(I figured this can be done with NetUserEnum()) and try accessing a certain file under each of found users' %APPDATA% path. Problem is I have no idea how to get that user-specific path (%APPDATA%).

Since application runs as service (SYSTEM), I cannot use environment or SHGetFolderPath(). Initially I thought I could make a use of LogonUser() but it always throws error 1326 at me, no matter if I run my application test code under user, admin or SYSTEM. (winxp as test platform). If there is a way to obtain user login handle, I can use that in SHGetFolderPath() or ExpandEnvironmentStringsForUser() APIs, is that correct?

So, the code I tried so far with LogonUser() is about the following(yes, username IS correct):

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

It probably wants my password but there is no way I know that on customer machine. All the APIs I found with quick search rely on HANDLE from LogonUser() which I apparently cannot have...

Any non-tricky and tricky ideas welcome!

Was it helpful?

Solution

Solution and conclusions so far.

You cannot use LogonUser() properly.

Instead, your options come to bruteforcing the path after user homedir(which you still obtain via any preferred method, my suggested is NetUserEnum()), based on one of the following:

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

If you know of any other method please comment.

OTHER TIPS

service or application run as "local system" to get special folder

this example we get CSIDL_DESKTOPDIRECTORY, work well at 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;
}

You can use WTSEnumerateSessions() and WTSQueryUserToken() to retrieve each login session's token.

Also look at LoadUserProfile(), which is needed for a service to access the HKEY_CURRENT_USER key for a specific user. The user must be logged in, either interactively or programmably, or impersonated in order to get the required user token.

Another option is to enumerate the HKEY_USERS key to retrieve the SIDs of the user accounts, then use LookupAccountSid() to retrieve their usernames, and then format the AppData paths manually based on OS version. Not as flexible, but less dependant on user-specific registry data.

Otherwise, forget trying to write to user-specific folders from a service. Write to common shared folders instead, like CSIDL_COMMON_APPDATA and FOLDERID_ProgramData.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top