Я пишу 32-битное сервисное приложение, в котором я хочу иметь возможность запускать пункты меню «Пуск» для зарегистрированные пользователи. Мне удалось выполнить эту задачу, выдав себя за пользователя и запустив выбранный .lnk-файл, используя CreateProcessAsUser с командной строкой: %windir%\system32\cmd /c " start /b /i "" "<path-to-lnk-file>" "
, И это работает почти для всех ярлыков, кроме множества системных ярлыков от аксессуары папка (например, Sticky Notes.lnk, Snipping Tool.lnk). Во время запуска Snipping Tool я получаю окно сообщения с этой ошибкой от cmd:
Windows не может найти «C: \ ProgramData \ Microsoft \ Windows \ Меню Пуск \ Программы \ Стандартные \ Snipping Tool.lnk». Убедитесь, что вы правильно ввели имя, а затем повторите попытку.
Но .lnk-файл существует именно в этом каталоге!
Резюме:
%windir%\system32\cmd /c " start /b /i "" "<path-to-lnk-file>" "
пример кода:
int launchAppForCurrentLoggedUser()
{
HANDLE userToken = WTSApiHelper::currentLoggedUserToken();
if (userToken == INVALID_HANDLE_VALUE) {
return -1;
}
//Duplicating token with access TOKEN_DUPLICATE | TOKEN_ALL_ACCESS,
//impersonation level SecurityImpersonation and token type TokenPrimary.
//Also closing original userToken
HANDLE dup = WTSApiHelper::duplicateToken(userToken);
if (dup == INVALID_HANDLE_VALUE) {
return -1;
}
int res = -1;
uint8 *env = NULL;
BOOL succeeded = CreateEnvironmentBlock((LPVOID *)&env, dup, FALSE);
if (!succeeded) {
Log("failed to get environment variables for user (error 0x%x).", GetLastError());
}
PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof(PROCESS_INFORMATION));
STARTUPINFOW si;
memset(&si, 0, sizeof(STARTUPINFOW));
si.cb = sizeof(STARTUPINFOW);
si.lpDesktop = L"winsta0\\Default";
WCHAR params[] = L"/c \" start /b /i \"\" \"C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Accessories\\Snipping Tool.lnk\" \" ";
WCHAR cmd[] = L"C:\\Windows\\system32\\cmd.exe";
DWORD flags = env ? CREATE_UNICODE_ENVIRONMENT : 0;
succeeded = CreateProcessAsUserW(dup, cmd, params, NULL, NULL, FALSE, flags | CREATE_NO_WINDOW, env, NULL, &si, &pi);
if (!succeeded) {
Log("cannot launch process for user with error 0x%x.", GetLastError());
} else {
nres = 0;
}
DestroyEnvironmentBlock(env);
CloseHandle(dup);
return nres;
}
Что мне здесь не хватает?
Отсутствует не файл LNK, а его цель.
Похоже, проблема WOW64 — для вашего 32-битного сервиса, %WINDIR%\System32
на самом деле перенаправляет на SysWOW64
и эти исполняемые файлы там не существуют.
Ну, на самом деле ваш 32-битный сервис находит 32-битный cmd.exe
который существует в SysWOW64
, а затем 32-разрядный cmd.exe имеет вышеуказанную проблему при поиске пути %windir%\system32\SnippingTool.exe
нашел в ярлыке.
Я могу воспроизвести проблему с помощью 32-разрядной командной строки. 32-битные процессы, пытающиеся использовать эти ярлыки, просто терпят неудачу.
Попробуйте порождать родную версию cmd.exe
(64-разрядный в вашей системе), используя %WINDIR%\SysNative\cmd.exe
Кроме того, у вас есть проблемы с цитированием. Вы пытаетесь вложить кавычки, но в действительности получается, что вторая кавычка соответствует первой кавычке и выходит из кавычек, а не вложения.
В будущем, когда в службе происходит сбой, полезно выполнить тот же вызов из обычного консольного приложения. В этом случае вы бы сразу обнаружили, что проблема совершенно не связана с олицетворением. Второй шаг, если он работает из консольного приложения, работающего в профиле, будет использовать «Запуск от имени» с консольным приложением для проверки логики олицетворения, все еще без дополнительной сложности среды обслуживания.
CreateProcessAsUser не загружает профиль указанного пользователя в раздел реестра HKEY_USERS. Следовательно, для доступа к информации в разделе реестра HKEY_CURRENT_USER необходимо загрузить информацию профиля пользователя в HKEY_USERS с помощью функции LoadUserProfile перед вызовом CreateProcessAsUser. Обязательно вызовите UnloadUserProfile после завершения нового процесса.
MSDN предлагает использовать CreateProcessWithLogonW или CreateProcessWithTokenW или вручную загружать информацию профиля пользователя.
А также:
CreateProcessAsUser позволяет получить доступ к указанному каталогу и исполняемому образу в контексте безопасности вызывающего или целевого пользователя. По умолчанию CreateProcessAsUser обращается к каталогу и исполняемому образу в контексте безопасности вызывающей стороны. В этом случае, если вызывающая сторона не имеет доступа к каталогу и исполняемому образу, функция завершается ошибкой. Чтобы получить доступ к каталогу и исполняемому образу с помощью контекста безопасности целевого пользователя, укажите hToken в вызове ImpersonateLoggedOnUser Функция перед вызовом CreateProcessAsUser.