служба Windows: получить переменную% APPDATA% каждого пользователя

Мое приложение работает как служба, и в какой-то момент ему нужно пройтись по всем существующим учетным записям пользователей в системе (я полагал, что это можно сделать с помощью NetUserEnum()) и попытаться получить доступ к определенному файлу по пути %APPDATA% каждого из найденных пользователей. Проблема в том, что я понятия не имею, как получить этот пользовательский путь (% APPDATA%).

Поскольку приложение работает как служба (SYSTEM), я не могу использовать среду или SHGetFolderPath(). Первоначально я думал, что могу использовать LogonUser(), но он всегда выдает мне ошибку 1326, независимо от того, запускаю ли я тестовый код приложения под пользователем, администратором или СИСТЕМОЙ. (winxp в качестве тестовой платформы). Если есть способ получить дескриптор входа пользователя, я могу использовать его в SHGetFolderPath() или ExpandEnvironmentStringsForUser() API, это правильно?

Итак, код, который я пробовал до сих пор с LogonUser(), примерно следующий (да, имя пользователя правильное):

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

Вероятно, ему нужен мой пароль, но я никак не могу узнать его на клиентской машине. Все API, которые я нашел с помощью быстрого поиска, полагаются на HANDLE из LogonUser(), которого у меня, по-видимому, нет...

Любые не хитрые и каверзные идеи приветствуются!


person einclude    schedule 08.10.2013    source источник
comment
%appdata%, похоже, различается для разных выпусков системы и, возможно, для языков, которые я не могу протестировать, поэтому я не могу просто перебрать это. я могу, но, по крайней мере, я надеюсь, что до этого не дойдет? /фейспалм/   -  person einclude    schedule 08.10.2013
comment
Ошибка 1326: ERROR_LOGON_FAILURE (ошибка входа в систему: неизвестное имя пользователя или неверный пароль). Например, вы не указываете никакого пароля.   -  person Remy Lebeau    schedule 09.10.2013


Ответы (3)


Решение и выводы на данный момент.

Вы не можете правильно использовать LogonUser().

Вместо этого ваши варианты сводятся к подбору пути после домашнего каталога пользователя (который вы по-прежнему получаете любым предпочтительным методом, я предлагаю NetUserEnum()), основываясь на одном из следующих:

  • SHGetFolderPath(CSIDL_APPDATA)
  • регистрационный ключ HKLM\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders

Если вы знаете какой-либо другой метод, пожалуйста, напишите.

person einclude    schedule 10.10.2013

служба или приложение запускаются как «локальная система», чтобы получить специальную папку

в этом примере мы получаем 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;
}
person KunMing Xie    schedule 04.12.2015

Вы можете использовать WTSEnumerateSessions() и WTSQueryUserToken() для получения маркера каждого сеанса входа в систему.

Также просмотрите LoadUserProfile(), который необходим службе для доступа к ключу HKEY_CURRENT_USER для конкретного пользователя. Пользователь должен войти в систему либо интерактивно, либо программно, либо олицетворить себя, чтобы получить требуемый токен пользователя.

Другой вариант — перечислить ключ HKEY_USERS для получения SID учетных записей пользователей, а затем использовать LookupAccountSid(), чтобы получить их имена пользователей, а затем отформатировать пути AppData вручную в зависимости от версии ОС. Не такой гибкий, но менее зависимый от пользовательских данных реестра.

В противном случае забудьте о попытках записи в пользовательские папки из службы. Вместо этого пишите в общие общие папки, например CSIDL_COMMON_APPDATA и FOLDERID_ProgramData.

person Remy Lebeau    schedule 08.10.2013
comment
он работает только для вошедших в систему пользователей, более того, для вошедших в систему RDP. Мне нужно собрать все существующие пользователи %APPDATA%, поэтому этот подход не работает. LoadUserProfile() запрашивает у меня HANDLE, которого у меня тоже нет. - person einclude; 09.10.2013
comment
Вы должны загрузить профиль пользователя, чтобы получить доступ к данным реестра этого пользователя. Пользователь должен войти в систему или выдать себя за него, чтобы получить токен этого пользователя и загрузить профиль этого пользователя. Это РУЧКА, которую ожидает функция LoadUserProfile(). - person Remy Lebeau; 09.10.2013
comment
есть ли еще подсказки о том, как загрузить профили незарегистрированных пользователей? поскольку это служба, она может быть запущена намного раньше, чем любой пользователь войдет в систему, поэтому сеансы недоступны. - person einclude; 09.10.2013
comment
спасибо за редактирование, но на моем winxp рядом со мной есть еще два пользователя, но я вижу только свой собственный ключ в HKU, будь то из SYSTEM (в radmin telnet, reg query HKEY_USERS) или regedit под пользователем admin. в любом случае, это все еще сводится к брутфорсу имени папки appdata? мой Бог - person einclude; 09.10.2013