提升权限在UseShellExecute=false时无法工作

23

我想以提升的权限启动子进程(确切地说,是控制台应用程序),但隐藏窗口。

我执行以下步骤:

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

var process = new Process
{
    StartInfo = info
};

process.Start();

而且这个有效:

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

但是,UseShellExecute = true 会创建一个新的窗口,并且我也无法重定向输出。

所以当我执行以下操作时:

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

这不会提升权限,而且上面的代码返回 false。为什么??


顺便提一下,你可以写成 DataReceivedEventHandler actionWrite = ...process.ErrorDataReceived += actionWrite - SLaks
你能否使用UseShellExecute = true,Verb = "runas"和ErrorDataReceived事件进行测试,就像@SLaks的评论中所述? - Kiquenet
3个回答

23

只有在使用ShellExecuteEx()启动进程时,ProcessStartInfo.Verb才会生效。这需要UseShellExecute = true。如果要重定向I/O并隐藏窗口,则只有通过CreateProcess()启动进程才能实现。这需要UseShellExecute = false。

这就是为什么它不起作用的原因。不确定禁止启动绕过UAC的隐藏进程是否是有意的。可能是。非常可能。

查看 此问答 获取需要显示UAC提升提示的清单。


非常感谢您提供的WinAPI幕后描述。您认为如何才能在具有隐藏窗口的进程中按需获取提升的特权?或者这是互相排斥的事情吗? - abatishchev
能否通过清单文件开/关功能?即,当我第一次手动启动应用程序时 - 不使用清单文件,而当第二次以编程方式启动时 - 强制使用。 - abatishchev
在用户不知情的情况下以提升的权限启动进程是不可能的。您需要一个单独的.exe文件,以便它拥有自己的清单。基本上,您只需要Main()方法。 - Hans Passant
我不想将进程启动对用户隐藏,我仍然需要他的UAC确认,我只是想要隐藏已启动进程的窗口。我认为这也不可能,对吗? - abatishchev
2
嗯... 这相当令人沮丧。 - tofutim

13

就我的情况而言,一旦提升的子进程完成,获取输出是可以的。这是我想出的解决方案。它使用了一个临时文件:

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

这对我来说似乎正在工作。复杂的细节(Windows 10,F#)- 要点在 https://gist.github.com/ImaginaryDevelopment/f4bc33ae75bdf29e5aad8fd4045f193d - Maslow
被接受的答案是一个解释,这是一个解决方案。+1 - caesay

0

请查看此答案

这似乎提供了一种解决方法。但是我建议在您可以访问子进程的源代码时尝试其他方法,例如命名管道


1
我还没有测试过UAC文章的方法,但它本身包含了一个示例。但是,如果你需要一个命名管道的示例,你可以很容易地找到许多这样的示例(比如这个这个),只需进行一些搜索即可。关键是像I/O重定向一样,你也可以使用文本流来发送和接收数据(即使两个进程位于不同的计算机上)。当然,你必须处理一些细节才能得到一个完全工作的应用程序。 - Masood Khaari
@abatishchev 我认为最好使用Trace to File,而不是重定向输出,verb = runas。命名管道更加复杂且难以调试。 - Kiquenet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接