如何只在需要时才提升特权?

这个问题适用于 WindowsVista!

我有一个应用程序,通常没有管理权限。有一个活动确实需要管理特权,但是我不想以更高的特权启动应用程序本身,因为我知道大多数时间用户甚至不会使用该特性。

我正在考虑某种方法,通过这种方法,我可以提高应用程序在某些事件上的特权(如按下按钮)。例如:

如果用户点击这个按钮,然后他会被提示与 UAC 对话框或同意。我如何做到这一点?

82041 次浏览

You need a UAC moniker and the code to run elevated as a COM object.

See this question.

Documentation on MSDN.

As it was said there:

Process.StartInfo.UseShellExecute = true;
Process.StartInfo.Verb = "runas";

will run the process as admin to do whatever you need with the registry, but return to your app with the normal privileges.

I don't believe that it is possible to elevate the currently running process. It is built into Windows Vista that administrator privileges are given to a process upon startup, as I understand. If you look at various programs that utilise UAC, you should see that they actually launch a separate process each time an administrative action needs to be performed (Task Manager is one, Paint.NET is another, the latter being a .NET application in fact).

The typical solution to this problem is to specify command line arguments when launching an elevated process (abatishchev's suggestion is one way to do this), so that the launched process knows only to display a certain dialog box, and then quit after this action has been completed. Thus it should hardly be noticeable to the user that a new process has been launched and then exited, and would rather appear as if a new dialog box within the same app has been opened (especially if you some hackery to make the main window of the elevated process a child of the parent process). If you don't need UI for the elevated access, even better.

For a full discussion of UAC on Vista, I recommend you see this very through article on the subject (code examples are in C++, but I suspect you'll need to use the WinAPI and P/Invoke to do most of the things in C# anyway). Hopefully you now at least see the right approach to take, though designing a UAC compliant program is far from trivial...

The following MSDN KB article 981778 describes how to 'self-elevate' an application:

http://support.microsoft.com/kb/981778

It contains downloadable samples in Visual C++, Visual C#, Visual Basic.NET.

This approach gets around the need to start a separate process, but in fact it is the original application that is restarted, running as an elevated user. Nevertheless this may still be very useful in some contexts where it is not practical to duplicate code in a separate executable.

To remove the elevation, you need to quit the application.

I know this is an old post, but this is in response to anyone else who comes across MarcP's suggestion. The msdn post he referenced indeed does restart the applications in all of the code examples. The code samples use the runas verb proposed already in other suggestions.

I downloaded the code to make sure, but this is from the original msdn article:

4. Click Yes to approve the elevation. Then, the original application restarts, running as an elevated administrator.
5. Close the application.

Perhaps someone comes in handy this simple example:

using System;
using System.Linq;
using System.Reflection;
using System.Diagnostics;
using System.Security.Principal;
using System.Windows.Forms;


namespace WindowsFormsApp1
{
internal static class Program
{
private class Form1 : Form
{
internal Form1()
{
var button = new Button{ Dock = DockStyle.Fill };
button.Click += (sender, args) => RunAsAdmin();
Controls.Add(button);


ElevatedAction();
}
}


[STAThread]
internal static void Main(string[] arguments)
{
if (arguments?.Contains("/run_elevated_action") == true)
{
ElevatedAction();
return;
}


Application.Run(new Form1());
}


private static void RunAsAdmin()
{
var path = Assembly.GetExecutingAssembly().Location;
using (var process = Process.Start(new ProcessStartInfo(path, "/run_elevated_action")
{
Verb = "runas"
}))
{
process?.WaitForExit();
}
}


private static void ElevatedAction()
{
MessageBox.Show($@"IsElevated: {IsElevated()}");
}


private static bool IsElevated()
{
using (var identity = WindowsIdentity.GetCurrent())
{
var principal = new WindowsPrincipal(identity);


return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
}


}
}