25 Aug 2010

onebit_26

Note: This is part of a series, you can find the rest of the parts in the series index.

Windows Vista introduced a feature called User Account Control (UAC) which added the following to Windows (in addition to a lot of hair pulling by some users). Visually it brought a small shield overlay icon image which indicates to the user that when you click that icon or button you will be prompted to confirm your action and possibly to enter an administrators username and password. This was introduced to stop people from shooting themselves in the foot by making certain actions which could break Windows require special privileges (called administrator privileges, which I find is confusing with administrator users and groups. So I call it root privileges).

works-on-my-machine-starburstI have been a fan of this idea since it was launched and as a developer I have kept it turned on, mainly because the my customers may not have it turned off. Imagine the scenario where I have it off and something works and on a customers machine it fails because UAC is on. I see a Works on my machine scenario coming up. Sad smile

Pull has a part of it which actually bumps up against UAC – registering protocol handlers. I do not want the entire of Pull to need root privileges when running, I only want the small part where you can register or unregister a protocol handler to run in root privileges.

Multiple Processes

The first issue is that root privileges are given to an entire process, and you cannot give it to a thread or method or some sub part. To solve this for Pull, meant creating a second executable file named ProtocolHandler.exe, which takes a few command line parameters and handles the registering and unregistering of protocol handlers.

This enables Pull to launch this second executable with the required root privileges and have it do the dirty work without Pull needing any root privileges. 

image

Running with Root Privileges

Kicking off another process in C# is very easy thanks to the Process class which handles the launching with the Start method (line 11 below). The Process class knows which process to run thanks to the ProcessStartInfo class which is setup before hand and passed to the StartInfo property.

To enable root privileges in the new process all you need to do is set the Verb property of ProcessStartInfo to runas (line 6 below).

Something Pull wants is to wait for the process to finish running, so that the user is confused by it immediately returning and nothing has happened yet. This is solved by using the WaitForExit method on the Process (line 12 below).

private static void RunProtocolHandler(string arguments)
{
    ProcessStartInfo processStartInfo = new ProcessStartInfo();
    processStartInfo.FileName = Path.Combine(Directory.GetCurrentDirectory(), "ProtocolHandler.exe");
    processStartInfo.Arguments = arguments;
    processStartInfo.Verb = "runas";

    using (Process process = new Process())
    {
        process.StartInfo = processStartInfo;
        process.Start();
        process.WaitForExit();
    }
}

The Shield

imageThe final component was to follow the UI guidelines and place a shield icon on the buttons which launch the other application, so that the user is aware this will require root privileges. While you can just grab a shield image and place it on the button that is not recommended because:

  • What if the logo changes in future Windows versions? – you are out of date Sad smile
  • What if the shield is not needed, because the person is running with root privileges already?

To handle this for me I have a small class called UACShield which offers two methods:

  • IsAdmin: this simple method returns true if you have root privileges and false if you don’t. This is done using pure .NET and just checking if the user has the Administrator role (line 14 below).
  • AddShieldToButton: this method takes a button, and if a user is not an admin adds the shield icon. It does this by calling into the Win32 API and calling the SendMessage to update the button. One caveat of this is that the button’s flat style must match the system’s flat style. This means that if you have some special UI tweaks on the button this may break those tweaks.
internal class UACShield
{
    private class NativeMethods
    {
        [DllImport("user32")]
        public static extern UInt32 SendMessage(IntPtr hWnd, UInt32 msg, UInt32 wParam, UInt32 lParam);
        public const int BCM_SETSHIELD = 0x160C; //Elevated button
    }

    public static bool IsAdmin()
    {
        using (WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent())
        {
            return new WindowsPrincipal(currentIdentity).IsInRole(WindowsBuiltInRole.Administrator);
        }
    }
                                                 
    public static void AddShieldToButton(Button button)
    {
        if (IsAdmin())
        {
            // no need for admins
            return;
        }

        button.FlatStyle = FlatStyle.System;
        NativeMethods.SendMessage(button.Handle, NativeMethods.BCM_SETSHIELD, 0, 0xFFFFFFFF);
    }
}

Comments

Visitor's picture

I just downloaded your pull program

Overall it looks really nice. In relation to this post I was not able to run the protocol Handlers on windows 7.

Also I had to Run as Administrator the whole program to get it to save the download to path.

One little thing that would make the program more user friendly would be to put info popups for the icons on the main page (even though they are relatively intuitive it would make it easier to figure out what the buttons do.)

in any case good job!

Visitor's picture

ok I guess I should say where I downloaded it since it looks like you have updated it on codeplex. I got it from http://www.softpedia.com/get/IPOD-TOOLS/Podcast/Pull.shtml which looks to be the same as the full download on codeplex. Currently I am not able to get the pull.application to work.

Robert MacLean's picture

The version on that website does appear to be older than on Codeplex. So I would suggest downloading the Codeplex version and trying again. You should not need to run the full app as admin, it will prompt when it needs those priviledges. What may be worth trying is also to go to settings, and first unregister the protocol handlers and then register them. Great idea about the tooltips - will include it in the next release.
Visitor's picture

Well I just did some comparisons and the version on codeplex and softplex are the same version.

is there any way you can build and upload a newer version to codeplex? I really like the software. So far it is the only one that does what I want it to - download podcasts without any extra features.

Thanks for the nice product.

Robert MacLean's picture

Sure thing - Build based on the current code: http://pull.codeplex.com/releases/view/51521
Pulled Apart - Part XI: Talking to yourself is ok, but answe's picture

[...] I have used this previously in Pull to tell Windows to add the shield icon if needed (see Part IX) to the protocol handler [...]

Add new comment