代码之家  ›  专栏  ›  技术社区  ›  teapot_of_wine

从模拟用户下启动某些系统默认.lnk文件时出现问题

  •  1
  • teapot_of_wine  · 技术社区  · 11 年前

    我正在编写32位服务应用程序,希望能够启动 已登录用户 .我通过模拟用户并使用 创建进程作为用户 使用命令行: %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位
    • Windows 8 Pro x64
    • 使用命令行通过用户模拟和CreateProcessAsUser启动快捷方式 %windir%\system32\cmd/c“启动/b/i”“”<lnk文件的路径>" "
    • 该方法几乎适用于“开始”菜单中的所有快捷方式,但“开始/附件”文件夹中的一些快捷方式除外(并非所有快捷方式都适用,例如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;
      }
      

    我在这里错过了什么?

    2 回复  |  直到 11 年前
        1
  •  3
  •   Ben Voigt    11 年前

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

    看起来像是WOW64问题--对于32位服务, %WINDIR%\System32 实际重定向到 SysWOW64 并且这些可执行文件在那里不存在。

    实际上,您的32位服务正在查找32位 cmd.exe 它确实存在于 系统WOW64 ,然后是32位cmd。exe在查找路径时存在上述问题 %windir%\system32\SnippingTool.exe 在快捷方式中找到。

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

    尝试生成的本机版本 命令提示符 (系统上为64位),使用 %WINDIR%\SysNative\cmd.exe

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

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

        2
  •  0
  •   Mark Jansen    11 年前

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

    According to the msdn page

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

    此外:

    CreateProcessAsUser允许您访问调用方或目标用户的安全上下文中的指定目录和可执行映像。默认情况下,CreateProcessAsUser访问调用方安全上下文中的目录和可执行映像。在这种情况下,如果调用方无法访问目录和可执行映像,则函数将失败。要使用目标用户的安全上下文访问目录和可执行映像,请在对 模拟记录在用户上 函数,然后调用CreateProcessAsUser。