31

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!

Cody Gray
  • 222,280
  • 47
  • 466
  • 543
KenG
  • 313
  • 1
  • 3
  • 4

3 Answers3

46

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

Cody Gray
  • 222,280
  • 47
  • 466
  • 543
  • This looks good and is very thorough, thanks! My child exe is already marked as "requireAdministrator" in its manifest; sorry, should have been clear about that. – KenG Feb 04 '11 at 14:20
  • 1
    @KenG: You're welcome; I'm glad this works for you. I originally did assume that you had the manifest and were just trying to execute and wait, but I later realized your question didn't actually say that, so I went back and added some more details. This way, I suspect my answer will also be useful for others in the future. – Cody Gray Feb 05 '11 at 04:48
  • 1
    If the app being spawned has a UAC manifest associated with it, then you can use `CreateProcess()`, you don't need to use `ShellExecutEx()`. `CreateProcess()` performs the necessary UAC prompt and elevation if the app's manifest says to do so. – Remy Lebeau Aug 08 '12 at 09:27
  • 1
    @Uri You're right, I have no idea where that came from... I've fixed the code, all you need to do is close the process. – Cody Gray Jul 08 '13 at 05:14
  • 1
    @RemyLebeau are you sure? I have a .NET assembly with proper manifest which on startup displays UAC, but once I start it via `CreateProcess` it ends up with 740 error `ERROR_ELEVATION_REQUIRED`. Any clue? – Michal Hosala Nov 04 '14 at 09:17
  • 2
    @MichalHosala: No, you are right. Per [Vista UAC: The Definitive Guide](http://www.codeproject.com/Articles/19165/Vista-UAC-The-Definitive-Guide): "CreateProcess() fails miserably for processes in Vista that require elevation via their manifest file. It should be noted that the manifest file doesn't really say "you have to use the administrative token to start this process". Instead, it says, "you can't use a token with fewer than these rights to start this process". So, the error message returned from CreateProcess(), ERROR_ELEVATION_REQUIRED (740), is somewhat misleading." – Remy Lebeau Nov 04 '14 at 19:41
  • 3
    @MichalHosala: So if the manifest says "requireAdministrator" and the spawning process is not elevated, `CreateProcess()` fails with `ERROR_ELEVATION_REQUIRED`. – Remy Lebeau Nov 04 '14 at 19:43
  • Note that on Windows XP using `runas` will open a window asking for which user you want to choose. Also there might be problems if you run exe with path relative to current directory. So you might want to use `CreateProcess` on Windows XP. – Nux Sep 18 '15 at 15:21
  • 2
    It should be explicitly noted, per the other comments, that if the manifest has the proper requirement in it, you do *not* need to use `runas` in the `lpVerb` member of the SHELLEXECUTEINFO structure, but you *do* need to use `ShellExecuteEx` instead of `CreateProcess` if the calling process is not already elevated. You can infer that from the other comments, but it hasn't been explicitly stated. – BlueMonkMN Mar 31 '16 at 16:52
0

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.

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.

John
  • 5,309
  • 1
  • 21
  • 38
0

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.

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.

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

Greg
  • 79
  • 2
  • `ShellExecute/Ex()` with the "runas" verb specified can launch elevated processes from within the Program Files folder if the calling app is not elevated. I do it in one of my projects, it works fine. – Remy Lebeau Aug 08 '12 at 09:29