This is a mirror of official site: http://jasper-net.blogspot.com/

A Practical Means to Perform Functions with Administrator Privilege (Elevate Process Privilege)

| Sunday, April 10, 2011
Introduction

Recently, I needed a new functionality in my project - clicking a button marked with a UAC shield and writing to registry HKLM. I don't expect the whole project to start with Administrator privilege, but to be elevated when necessary.

I referred to many articles introducing how to elevate process privilege. Most of them are just demos which restart the program with Administrator privilege. I suppose this is not practical in programming because this method cannot inherit the context of the previous program. Some other methods include invoking COM or another program, but it's complex to maintain another project, isn't it?

The advantage of my method introduced in this article is that you would not need to restart your program so that the context can be kept. Moreover, the end user will not see a restarting of the program, and he/she will consider it is THIS program which does the work, which would be a good operation experience :).

Background

My method sounds quite simple: to invoke another process which has the same executable path with the invoking one, and pass arguments to the invoked one.

Using the Code

Let's begin with a simple trick - adding an UAC shield to the button. Please refer to the final Reference Article section for details.

You must include this using in your project in order to import Win32 DLL functions:

using System.Runtime.InteropServices;

Import Win32 DLL functions and define constants for the UAC shield:

[DllImport("user32")]
public static extern UInt32 SendMessage(IntPtr hWnd, UInt32 msg, 
UInt32 wParam, UInt32 lParam);
private const int BCM_FIRST = 0x1600;
private const int BCM_SETSHIELD = (BCM_FIRST + 0x000C);

Add a UAC shield to the button:

private void AddUACShieldToButton(Button ctrl)
{
    ctrl.FlatStyle = FlatStyle.System; // this is a MUST-DO.
    SendMessage(ctrl.Handle, BCM_SETSHIELD, 0, 0xFFFFFFFF);
}

Now, let's see the starting part of the project. It is a program which behaves differently according to the arguments.

static void Main(String[] args)
{
    if (false == args.Length.Equals(0))
    {
        // there are arguments in command line, start the worker mode
        switch (Convert.ToUInt32(args[0]))
        {
            case 0:
                worker.WriteHKLMSoftware(args[1]);
                break;
            case 1:
                worker.UpdateDNARegistry();
                break;
        }
    }
    else
    {
        // no argument in command line, start the GUI mode
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}

Well, the program has 2 switches - a common GUI mode and a worker mode, which is separated by command line arguments. The end user will always starts the program with no arguments, so the GUI mode starts. When the user clicks button1, it calls the following method, if the process starts without Administrator privilege:

public static bool RunWorkerInstance(String exePath, String arg)
{
    Process workProcess = new Process();
    workProcess.StartInfo.UseShellExecute = true;
    workProcess.StartInfo.WorkingDirectory = Environment.CurrentDirectory;
    workProcess.StartInfo.FileName = exePath;
    workProcess.StartInfo.Verb = "runas";
    workProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
    workProcess.StartInfo.Arguments = arg;
    try
    {
        workProcess.Start();
        workProcess.WaitForExit();
        workProcess.Close();
       return true;
    }
    catch ()
    {
        return false;
    }
}

When the invoking program reaches "workProcess.Start()", a UAC dialog will be shown to ask you whether to start the program. You can see the product name shown in the UAC dialog is the same with the running program, which will be a good operation experience to the end user.

And then, the invoked process starts and operates with Administrator privilege, which is achieved by the "runas" and UseShellExecute parameters. The invoking process waits for the invoked process to finish and then goes on.

Note, better set the invoked process to run hidden (workProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden) so that no DOS window will be shown.
Now let's see the arguments passed to the invoked process. My arguments layout is:

"{CommandWord} {arg1},{arg2},{arg3}..."

Read more: Codeproject

Posted via email from Jasper-net

0 comments: