1

我正在编写 32 位服务应用程序,我希望能够为已登录的用户启动开始菜单项。我确实设法通过模拟用户并使用带有命令行的CreateProcessAsUser启动选定的 .lnk 文件来完成此任务: %windir%\system32\cmd /c " start /b /i "" "<path-to-lnk-file>" "。它适用于几乎所有的快捷方式,除了来自附件文件夹的系统快捷方式(例如 Sticky Notes.lnk、Snipping Tool.lnk)。在启动截图工具期间,我从 cmd 收到带有此错误的消息框:

Windows 找不到“C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Accessories\Snipping Tool.lnk”。请确保您输入了正确的名称,然后重试。

但是 .lnk 文件就存在于这个目录中!

概括:

  • 服务是 32 位的
  • 视窗 8 专业版 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;
    }
    

我在这里想念什么?

4

2 回答 2

3

丢失的不是 LNK 文件,而是它的目标。

似乎是一个 WOW64 问题——对于您的 32 位服务,%WINDIR%\System32实际上重定向到SysWOW64并且这些可执行文件在那里不存在。

好吧,实际上你的32位服务是在查找cmd.exe中确实存在的32位SysWOW64,然后32位cmd.exe在查找%windir%\system32\SnippingTool.exe快捷方式中找到的路径时出现上述问题。

我可以使用 32 位命令提示符重现该问题。尝试使用这些快捷方式的 32 位进程只会失败。

尝试生成本机版本cmd.exe(在您的系统上为 64 位),使用%WINDIR%\SysNative\cmd.exe

此外,您还有报价问题。您正在尝试嵌套引号,但实际发生的是第二个引号与第一个引号匹配并退出引用,而不是嵌套。

将来,当服务出现故障时,从普通控制台应用程序运行相同的调用会很有帮助。在这种情况下,您会立即发现该问题与冒充完全无关。第二步,如果它在配置文件中运行的控制台应用程序中工作,则将使用控制台应用程序的“运行方式”来测试模拟逻辑,但仍然不会增加服务环境的复杂性。

于 2015-08-03T14:21:22.307 回答
0

CreateProcessAsUser 不会将指定用户的配置文件加载到 HKEY_USERS 注册表项中。因此,要访问 HKEY_CURRENT_USER 注册表项中的信息,您必须在调用 CreateProcessAsUser 之前使用 LoadUserProfile 函数将用户的配置文件信息加载到 HKEY_USERS 中。确保在新进程退出后调用 UnloadUserProfile。

根据msdn页面

MSDN 建议使用 CreateProcessWithLogonW 或 CreateProcessWithTokenW,或者手动加载用户的配置文件信息。

并且:

CreateProcessAsUser 允许您在调用者或目标用户的安全上下文中访问指定的目录和可执行映像。默认情况下,CreateProcessAsUser 访问调用者安全上下文中的目录和可执行映像。在这种情况下,如果调用者无权访问目录和可执行映像,则函数将失败。要使用目标用户的安全上下文访问目录和可执行映像,请在调用 CreateProcessAsUser 之前在对ImpersonateLoggedOnUser函数的调用中指定 hToken 。

于 2015-08-03T13:25:41.323 回答