【问题标题】:How can I run a child process that requires elevation and wait?如何运行需要提升并等待的子进程?
【发布时间】:2011-06-21 01:34:04
【问题描述】:

Win 7/UAC 快把我逼疯了。

在我的 C++ 应用程序中,我需要在 Windows 7 上运行一个需要提升的可执行文件。我想关闭这个东西并等待它完成后再继续。最简单的方法是什么?

我通常通过CreateProcess() 做这种事情,但是对于需要提升的可执行文件它会失败。

我尝试使用cmd.exe /c ...CreateProcess 运行,它可以运行,但会弹出一个丑陋的 cmd 终端窗口。

我读到ShellExecute() 将允许提升,但在使用ShellExecute() 时等待exe 完成似乎并不容易。 像system() 这样简单的东西会起作用吗?

非常感谢任何其他想法!

【问题讨论】:

    标签: c++ winapi uac shellexecute createprocess


    【解决方案1】:

    使用 ShellExecuteEx,而不是 ShellExecute。此函数将为创建的进程提供一个句柄,您可以使用该句柄在该句柄上调用 WaitForSingleObject 以阻塞直到该进程终止。最后,只需在进程句柄上调用 CloseHandle 即可将其关闭。

    示例代码(为了清晰和简洁省略了大部分错误检查):

    SHELLEXECUTEINFO shExInfo = {0};
    shExInfo.cbSize = sizeof(shExInfo);
    shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
    shExInfo.hwnd = 0;
    shExInfo.lpVerb = _T("runas");                // Operation to perform
    shExInfo.lpFile = _T("C:\\MyApp.exe");       // Application to start    
    shExInfo.lpParameters = "";                  // Additional parameters
    shExInfo.lpDirectory = 0;
    shExInfo.nShow = SW_SHOW;
    shExInfo.hInstApp = 0;  
    
    if (ShellExecuteEx(&shExInfo))
    {
        WaitForSingleObject(shExInfo.hProcess, INFINITE);
        CloseHandle(shExInfo.hProcess);
    }
    

    lpVerb 指定“runas”动词是导致 UAC 提升即将启动的应用程序的原因。这相当于将应用程序清单中的权限级别设置为“requireAdministrator”。管理员和受限用户都需要提升 UAC。

    但值得注意的是,除非绝对必要,否则您应该更喜欢将清单添加到要启动的应用程序的“标准”方式,以指定其所需的执行级别。如果您走这条路线,您只需将“open”作为lpVerb 传递。示例清单如下所示:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
            <dependency>
                    <dependentAssembly>
                            <assemblyIdentity
                                    type="win32"
                                    name="Microsoft.Windows.Common-Controls"
                                    version="6.0.0.0"
                                    processorArchitecture="X86"
                                    publicKeyToken="6595b64144ccf1df"
                                    language="*"
                            />
                    </dependentAssembly>
            </dependency>
            <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
                    <security>
                            <requestedPrivileges>
                                    <requestedExecutionLevel 
                                           level="requireAdministrator" 
                                           uiAccess="false"/>
                            </requestedPrivileges>
                    </security>
            </trustInfo>
    </assembly>
    

    最后,确保应用程序中触发执行需要 UAC 提升的流程的任何元素都是 marked accordingly。在用户界面中对此进行建模是您的工作; Windows 不会为您处理它。这是通过在入口点上显示盾牌图标来完成的;例如:

            

    【讨论】:

    • @KenG:不客气;我很高兴这对你有用。我最初确实假设您拥有清单并且只是试图执行和等待,但后来我意识到您的问题实际上并没有这么说,所以我回去添加了更多细节。这样一来,我怀疑我的回答将来也会对其他人有用。
    • 如果正在生成的应用有与之关联的 UAC 清单,那么您可以使用CreateProcess(),您不需要使用ShellExecutEx()CreateProcess() 执行必要的 UAC 提示和提升,如果应用的清单要求这样做。
    • @MichalHosala:不,你是对的。每Vista UAC: The Definitive Guide:“对于需要通过清单文件提升的Vista 中的进程,CreateProcess() 惨遭失败。应该注意,清单文件并没有真正说“你必须使用管理令牌来启动这个进程”。相反,它说,“您不能使用少于这些权限的令牌来启动此过程”。因此,从 CreateProcess() 返回的错误消息 ERROR_ELEVATION_REQUIRED (740) 有点误导。”
    • @MichalHosala:因此,如果清单显示“requireAdministrator”并且生成过程未提升,CreateProcess() 将失败并显示ERROR_ELEVATION_REQUIRED
    • 应该明确指出,对于其他 cmets,如果清单中有适当的要求,您确实不需要需要在 @987654342 中使用 runas @ SHELLEXECUTEINFO 结构的成员,但如果调用进程尚未提升,您确实需要使用ShellExecuteEx 而不是CreateProcess。您可以从其他 cmets 中推断出这一点,但尚未明确说明。
    【解决方案2】:

    您可以做的是为子进程“stdin”创建一个管道(就像您要将输入重定向到该进程一样)并等待管道中断。

    请注意,该进程不会真正接收重定向的输入。

    例如如果您尝试从未提升的命令提示符执行

    echo help | diskpart
    

    您将看到海拔高度,命令窗口将等到您关闭单独的窗口。

    【讨论】:

      【解决方案3】:

      要以提升的特权运行,要求启动它的进程具有提升的特权。这通常需要一个安全的服务或已经运行的东西来启动你的进程。这是从 Vista 开始的更改。它与授权(通过您的 ACL 令牌)能够启动进程并使用从启动进程继承的适当级别的特权启动它有关。微软一直在努力让人们创建一个提升的进程来处理所有提升的功能需求,并将其余的留在特权最少的用户空间。自从 Vista 是 Alpha 版以来,就一直在这样做。这很痛苦,但出于安全原因,Microsoft 希望您这样做。

      顺便说一句,如果您从 Program Files 目录等安全位置启动应用程序,ShellExec 调用将不起作用。在几年前不得不迁移到服务模型之前尝试过。

      因此,要启动您的进程并绕过 UAC,唯一的方法是从已经具有继承安全特权的进程启动

      【讨论】:

      • ShellExecute/Ex() 指定了“runas”动词,如果调用应用程序未提升,则可以从 Program Files 文件夹中启动提升的进程。我在我的一个项目中这样做了,效果很好。
      猜你喜欢
      • 2011-03-31
      • 2013-12-17
      • 2016-08-31
      • 2019-10-20
      • 1970-01-01
      • 1970-01-01
      • 2018-11-19
      • 2015-08-21
      相关资源
      最近更新 更多