Проблемы с некоторыми системными .lnk-файлами, запускаемыми по умолчанию от имени другого пользователя

Я пишу 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-файл существует именно в этом каталоге!

Резюме:

  • обслуживание 32 бит
  • Windows 8 Pro x64
  • запуск ярлыков от имени пользователя и CreateProcessAsUser из командной строки %windir%\system32\cmd /c " start /b /i "" "<path-to-lnk-file>" "
  • Подход работает почти для всех ярлыков в меню «Пуск», за исключением некоторых в папке «Пуск / Стандартные» (не все из них, например, Paint.lnk открывается нормально)
  • пример кода:

    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;
    }
    

Что мне здесь не хватает?

1

Решение

Отсутствует не файл 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

Кроме того, у вас есть проблемы с цитированием. Вы пытаетесь вложить кавычки, но в действительности получается, что вторая кавычка соответствует первой кавычке и выходит из кавычек, а не вложения.

В будущем, когда в службе происходит сбой, полезно выполнить тот же вызов из обычного консольного приложения. В этом случае вы бы сразу обнаружили, что проблема совершенно не связана с олицетворением. Второй шаг, если он работает из консольного приложения, работающего в профиле, будет использовать «Запуск от имени» с консольным приложением для проверки логики олицетворения, все еще без дополнительной сложности среды обслуживания.

3

Другие решения

CreateProcessAsUser не загружает профиль указанного пользователя в раздел реестра HKEY_USERS. Следовательно, для доступа к информации в разделе реестра HKEY_CURRENT_USER необходимо загрузить информацию профиля пользователя в HKEY_USERS с помощью функции LoadUserProfile перед вызовом CreateProcessAsUser. Обязательно вызовите UnloadUserProfile после завершения нового процесса.

Согласно странице MSDN

MSDN предлагает использовать CreateProcessWithLogonW или CreateProcessWithTokenW или вручную загружать информацию профиля пользователя.

А также:

CreateProcessAsUser позволяет получить доступ к указанному каталогу и исполняемому образу в контексте безопасности вызывающего или целевого пользователя. По умолчанию CreateProcessAsUser обращается к каталогу и исполняемому образу в контексте безопасности вызывающей стороны. В этом случае, если вызывающая сторона не имеет доступа к каталогу и исполняемому образу, функция завершается ошибкой. Чтобы получить доступ к каталогу и исполняемому образу с помощью контекста безопасности целевого пользователя, укажите hToken в вызове ImpersonateLoggedOnUser Функция перед вызовом CreateProcessAsUser.

0

По вопросам рекламы [email protected]