C++ 如何运行需要提升和等待的子进程?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4893262/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 16:51:51  来源:igfitidea点击:

How can I run a child process that requires elevation and wait?

c++winapiuacshellexecutecreateprocess

提问by KenG

Win 7/UAC is driving me crazy.

Win 7/UAC 快把我逼疯了。

From within my C++ application, I need to run an executable that requires elevation on Windows 7. I want to fire this thing off and wait for it to finish before proceeding. What's the easiest way to do this?

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

I normally do this kind of thing via CreateProcess(), but it fails for executables that require elevation.

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

I tried running using cmd.exe /c ...through CreateProcess, which works but pops up an ugly cmd terminal window.

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

I am reading that ShellExecute()will allow elevation, but it doesn't appear to be easy to wait for the exe to finish when using ShellExecute(). Will something simple like system()work?

我正在阅读这ShellExecute()将允许提升,但在使用ShellExecute(). 简单的东西会system()起作用吗?

Any other ideas are greatly appreciated!

非常感谢任何其他想法!

回答by Cody Gray

Use ShellExecuteEx, rather than ShellExecute. This function will provide a handle for the created process, which you can use to call WaitForSingleObjecton that handle to block until that process terminates. Finally, just call CloseHandleon the process handle to close it.

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

Sample code (most of the error checking is omitted for clarity and brevity):

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

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

Specifying the "runas" verb for the lpVerbis what causes UAC to elevate the application that's about to be launched. This is the equivalent of setting the permissions level in the application's manifest to "requireAdministrator". It will require UAC elevation for both an administrator and a limited user.

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

But it's worth noting that unless absolutely necessary, you should prefer the "standard" way of adding a manifest to the application you want to launch that specifies its required execution level. If you go this route, you will simply pass "open" as the lpVerb. A sample manifest is shown below:

但值得注意的是,除非绝对必要,否则您应该更喜欢向要启动的应用程序添加清单以指定其所需执行级别的“标准”方式。如果您走这条路线,您只需将“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>

Finally, make sure that whatever element in your application triggers execution of the process requiring UAC elevation is marked accordingly. It's your job to model this in the user interface; Windows doesn't handle it for you. This is done by displaying the shield icon on the entry point; for example:

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

        UAC shield displayed on a button                                    UAC shield displayed on a menu item

        显示在按钮上的 UAC 盾牌                                    菜单项上显示的 UAC 盾牌

回答by John

What you can do is create a pipe for the child process "stdin" (as if you were to redirect the input to that process) and wait for the pipe to break.

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

Note that the process will not really receive the redirected input.

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

E.g. if you try from unelevated command prompt to do

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

echo help | diskpart

you will see elevation and the command window will wait until you close the separate window.

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

回答by Greg

To run with elevated privileged, requires that the process launching this has elevated privileged. This usually require a secure service or something already running to launch your process. This is a change starting with Vista. It has to do with having the authority (via your ACL tokens) to be able to launch a process and launch it with the appropriate level of privileged inherited from the launching process. Microsoft has been pushing hard to have people create an elevated process that handles all elevated functionality needs and leave the rest in least privileged user space. Been doing this off and on since Vista was Alpha. It's a pain, but the way Microsoft would prefer you to do things for security reasons.

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

By the way, the ShellExec call will not work if you are launching your app from a secure location like Program Files directory, etc. Tried that before having to move to the service model years ago.

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

So to launch your process and by pass UAC, the only way to do it is to launch from a process that already has the security privileged to inherit

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