Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Win 7/UAC is driving me crazy.

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?

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

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

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?

Any other ideas are greatly appreciated!

like image 642
KenG Avatar asked Feb 04 '11 00:02

KenG


1 Answers

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

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 lpVerb is 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.

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:

<?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 shield displayed on a button                                     UAC shield displayed on a menu item

like image 103
Cody Gray Avatar answered Oct 03 '22 21:10

Cody Gray