C#在运行时提升程序

时间:2018-01-06 06:12:08

标签: c# winforms uac elevated-privileges

我有一个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传递参数。

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

2 个答案:

答案 0 :(得分:4)

您可以在第二个实例启动时传递它们,而不是在命令行上提供参数,例如在两个实例之间使用named pipe。为了确定开始的进程是否是第一个进程,我使用了一个名为mutex,主要受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

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

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

全部放在一起......

这就是你最终的结果:

first and second

你必须相信我的UAC提示不包含命令参数...... :(

答案 1 :(得分:1)

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

修改嵌入程序中的清单。在Visual Studio 2008(或更高版本)上,转到:Project&gt;添加新项&gt;选择&#34; Application Manifest File&#34;。

<requestedExecutionLevel>元素更改为:

<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />

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