C# 在运行时提升程序
C# Elevate program at runtime
我有一个 C# 程序,我希望其中的某些功能需要管理员密码。为了解决这个问题,我启动了应用程序的另一个实例作为提升的进程并将命令行参数传递给它,因此该进程知道它必须执行什么任务。
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
的参数。
有什么办法可以防止这种情况发生吗?或者提升 运行 程序的更好方法?
为什么您需要启动另一个程序实例才能以管理员权限启动。对您的程序进行更改,以便您可以 运行 首先使用管理员权限。
修改嵌入到程序中的清单。在 Visual Studio 2008(或更高版本)上,转到:项目 > 添加新项目 > select“Application Manifest File
”。
将 <requestedExecutionLevel>
元素更改为:
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
这将提示输入 UAC(或管理员权限)。
您可以在第二个实例启动后传递它们,而不是在命令行上提供参数,例如通过 named pipe between the two instances. To determine if the process that started is the first one, I use a named mutex, largely inspired on What is a good pattern for using a Global Mutex in C#? 除了我在这里使用本地互斥锁,将其限制为 (终端)会话。
主要
在这里您可以看到 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<string> 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);
}
二审
当第二个实例启动时,我们设置 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 提示不包含命令参数......:(
我有一个 C# 程序,我希望其中的某些功能需要管理员密码。为了解决这个问题,我启动了应用程序的另一个实例作为提升的进程并将命令行参数传递给它,因此该进程知道它必须执行什么任务。
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
的参数。
有什么办法可以防止这种情况发生吗?或者提升 运行 程序的更好方法?
为什么您需要启动另一个程序实例才能以管理员权限启动。对您的程序进行更改,以便您可以 运行 首先使用管理员权限。
修改嵌入到程序中的清单。在 Visual Studio 2008(或更高版本)上,转到:项目 > 添加新项目 > select“Application Manifest File
”。
将 <requestedExecutionLevel>
元素更改为:
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
这将提示输入 UAC(或管理员权限)。
您可以在第二个实例启动后传递它们,而不是在命令行上提供参数,例如通过 named pipe between the two instances. To determine if the process that started is the first one, I use a named mutex, largely inspired on What is a good pattern for using a Global Mutex in C#? 除了我在这里使用本地互斥锁,将其限制为 (终端)会话。
主要
在这里您可以看到 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<string> 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);
}
二审
当第二个实例启动时,我们设置 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 提示不包含命令参数......:(