Elevating privileges doesn't work with UseShel

2019-01-06 20:56发布

问题:

I want to start a child process (indeed the same, console app) with elevated privileges but with hidden window.

I do next:

var info = new ProcessStartInfo(Assembly.GetEntryAssembly().Location)
{
    UseShellExecute = true, // !
    Verb = "runas", 
};

var process = new Process
{
    StartInfo = info
};

process.Start();

and this works:

var identity = new WindowsPrincipal(WindowsIdentity.GetCurrent());
identity.IsInRole(WindowsBuiltInRole.Administrator); // returns true

But UseShellExecute = true creates a new window and I also I can't redirect output.

So when I do next:

var info = new ProcessStartInfo(Assembly.GetEntryAssembly().Location)
{
    RedirectStandardError = true,
    RedirectStandardOutput = true,
    UseShellExecute = false, // !
    Verb = "runas"
};

var process = new Process
{
    EnableRaisingEvents = true,
    StartInfo = info
};

DataReceivedEventHandler actionWrite = (sender, e) =>
{
    Console.WriteLine(e.Data);
};

process.ErrorDataReceived += actionWrite;
process.OutputDataReceived += actionWrite;

process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();

This doesn't elevate privileges and code above returns false. Why??

回答1:

ProcessStartInfo.Verb will only have an effect if the process is started by ShellExecuteEx(). Which requires UseShellExecute = true. Redirecting I/O and hiding the window can only work if the process is started by CreateProcess(). Which requires UseShellExecute = false.

Well, that's why it doesn't work. Not sure if forbidding to start a hidden process that bypasses UAC was intentional. Probably. Very probably.

Check this Q+A for the manifest you need to display the UAC elevation prompt.



回答2:

In my case, it was ok to get the outputs once the elevated child process is done. Here's the solution I came up. It uses a temporary file :

var output = Path.GetTempFileName();
var process = Process.Start(new ProcessStartInfo
{
    FileName = "cmd",
    Arguments = "/c echo I'm an admin > " + output, // redirect to temp file
    Verb = "runas", // UAC prompt
    UseShellExecute = true,
});
process.WaitForExit();
string res = File.ReadAllText(output);
// do something with the output
File.Delete(output);


回答3:

Check this answer.

This seems to provide a workaround. But I recommend to try other methods like Named Pipes when you have access to source code of the child process.