C#在运行时提升程序

我有一个C#程序,我希望某些function需要管理员密码。 为了解决这个问题,我启动了另一个应用程序实例作为提升过程并将命令行参数传递给它,因此进程知道它必须执行什么任务。

Process proc = new Process(); proc.StartInfo.Arguments = "PARAMETERS HERE"); proc.StartInfo.FileName = Application.ExecutablePath; proc.StartInfo.UseShellExecute = true; proc.StartInfo.Verb = "runas"; proc.Start(); 

这工作正常,但我有一个小问题。 我刚刚注意到,弹出启动新进程的UAC提示不仅显示应用程序名称和路径, 还显示传递给它的命令行参数。 这样,用户可以看到传递的参数,并直接从run命令或command prompt传递参数。

有什么办法可以防止这种情况吗? 或者更好的方法来提升正在运行的程序?

您可以在第二个实例启动时传递它们,而不是在命令行上提供参数,例如通过在两个实例之间使用命名管道 。 为了确定启动的进程是否是第一个进程,我使用了一个名为mutex,很大程度上受到了在C#中使用Global Mutex的好模式的启发? 除了我在这里使用本地互斥,限制它(终端)会话。

主要

在这里,您可以看到Mutex的创建,并根据是否创建了Mutex ,我们知道我们是第一个还是第二个实例。

 static string MyPipeName = $"MyApp_{Environment.UserDomainName}_{Environment.UserName}"; static void Main(string[] args) { bool created; // true if Mutex is created by this process using(var mutex = new Mutex(false, @"Local\" + MyPipeName, out created)) // this needs proper securing { var gotit = mutex.WaitOne(2000); // take ownership if (!created) { if (gotit) { args = SecondInstance(mutex); Console.WriteLine("I'm the second instance"); } else { // no idea what to do here, log? crash? explode? } } else { FirstInstance(mutex); Console.WriteLine("I'm the first instance"); } ProgramLoop(args); // our main program, this can be Application.Run for Winforms apps. } } 

第一个例子

在FirstInstance方法中,我们设置一个委托,当被调用时,将启动一个NamedPipeServerStream ,释放互斥锁(在第二个进程中进行额外的安全保护),再次启动自身并等待客户端连接命名管道。 完成后,它会发送参数并等待确认。 一旦Mutex发布,它就会继续。

 static void FirstInstance(Mutex mutex) { StartSecondInstanceHandler += (args) => { using(var srv = new NamedPipeServerStream(MyPipeName)) // this needs proper securing { mutex.ReleaseMutex(); // kick off a second instance of this app Relaunch(); srv.WaitForConnection(); using(var sr = new StreamReader(srv)) { using(var sw = new StreamWriter(srv)) { Trace.WriteLine("Server Started and writing"); // send the arguments to the second instance sw.WriteLine(args); sw.Flush(); Trace.WriteLine("Server done writing"); // the client send back an ack, is not strictly needed Trace.WriteLine("ack: {0}", sr.ReadLine()); } } mutex.WaitOne(); } }; } // our public static delegate, accessible by calling // Program.StartSecondInstanceHandler("/fubar"); public static Action StartSecondInstanceHandler = null; // just launch the app static void Relaunch() { var p = new ProcessStartInfo(); p.FileName = Environment.CommandLine; p.UseShellExecute = true; p.Verb = "runas"; var started = Process.Start(p); } 

SecondInstance

当第二个实例启动时,我们设置一个NamedPipeClientStream ,连接到服务器并读取响应并发回确认。 Mutex被释放并返回参数(我在那里通过拆分空格来快速破解)。

 static string[] SecondInstance(Mutex mutex) { string arguments = String.Empty; Console.WriteLine("Client NamedPipe starting"); using(var nps = new NamedPipeClientStream(MyPipeName)) { nps.Connect(); // we expect the server to be running using(var sr = new StreamReader(nps)) { arguments = sr.ReadLine(); Console.WriteLine($"received args: {arguments}"); using(var sw = new StreamWriter(nps)) { sw.WriteLine("Arguments received!"); } } mutex.ReleaseMutex(); // we're done } return arguments.Split(' '); // quick hack, this breaks when you send /b:"with spaces" /c:foobar } 

程序循环

要完成这里是一个枯燥的程序循环

 static void ProgramLoop(string[] args) { // main loop string line; while((line = Console.ReadLine()) != String.Empty) { switch(line) { case "admin": if (StartSecondInstanceHandler != null) { Console.WriteLine("elevating ..."); StartSecondInstanceHandler("/foo:bar /baz:fu"); Console.WriteLine("... elevation started"); } else { Console.WriteLine("you are elevated with these arguments: {0}", String.Join(' ',args)); } break; default: Console.WriteLine("you typed '{0}', type 'admin' or leave empty to leave", line); break; } } } 

把它们放在一起……

这就是你最终得到的结果:

第一和第二

你必须相信我的UAC提示不包含命令参数…… 🙁

为什么需要启动另一个程序实例以使用Admin权限启动。 对程序进行更改,以便您可以使用Admin权限运行第一个实例本身。

修改嵌入程序中的清单。 在Visual Studio 2008(或更高版本)上,转到:Project> Add New Item>选择“ Application Manifest File ”。

元素更改为:

  

这将提示UAC(或管理员权限)。