16

I have an application (Windows service) that is installed into a directory in the Program Files folder. Alongside this application is another WinForms application that is used to configure the service (amongst other things). When it does configuration, it saves changes to a config file that lives alongside the service.

When running on Vista/Win7, UAC prevents the user from saving to the config file. What I would like to do is:

  • put the shield icon next to the menu item used to configure
  • prompt for UAC permissions when this item is chosen
  • only show the icon/prompt when on an OS that requires it
  • only show the icon/prompt when permissions are required (eg. if the application is installed somewhere that does not require UAC permission)

I don't really want to run the whole application as an administrator, as it is also used for other purposes that do not require UAC permissions (so setting an application manifest file is not the correct solution). I'm also assuming (correct me if I'm wrong) that once UAC permissions have been granted, my existing process cannot perform the action and that I will need to start a new process.

How can I best achieve this?

adrianbanks
  • 81,306
  • 22
  • 176
  • 206
  • I had to do a bit of jumping through hoops to code this up correctly, but thanks to Matthew I've got something similar to what I described above. – adrianbanks Oct 14 '10 at 14:57
  • Good to hear. UAC makes things a pain sometimes, but your software is really better for it. –  Oct 14 '10 at 15:07

2 Answers2

23

This is fairly easy. Put a shield icon on the button that saves changes to the configuration file, instead of the menu item. This follows the Windows behavior of not requesting UAC permissions until the last moment. The button actually will launch your executable again as administrator with a special command line (that you decide) to perform the configuration file saving. Use a named pipe (be sure to give it the correct permissions) to pass the configuration data to your second instance if your don't want to use the command line for data passing.

For launching your executable:

ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "YOUR EXE";
info.UseShellExecute = true;
info.Verb = "runas"; // Provides Run as Administrator
info.Arguments = "YOUR SPECIAL COMMAND LINE";

if (Process.Start(info) != null)
{ 
    // The user accepted the UAC prompt.
}

This works also when UAC doesn't exist (Windows XP), because it will simply run as administrator if possible, or prompt for credentials. You can check whether the OS requires UAC by simply doing Environment.OSVersion.Version.Major == 6. 6 is both Windows Vista and 7. You can make sure you're using Windows by looking at Environment.OSVersion.Platform.

For detecting whether you're application is already admin, you can do this:

public static bool IsAdministrator()
{
    WindowsIdentity identity = WindowsIdentity.GetCurrent();

    if (identity != null)
    {
        WindowsPrincipal principal = new WindowsPrincipal(identity);
        return principal.IsInRole(WindowsBuiltInRole.Administrator);
    }

    return false;
}
Mo Patel
  • 2,321
  • 4
  • 22
  • 37
  • YOu should detect if the user is currently running as admin. If so, you might wish to behave differently (e.g. not launch a separate instance and possibly omit the shield icon). – Brian Oct 13 '10 at 15:30
  • @Brian, As he pointed out, he doesn't want the whole application to run as admin, so in this situation, there is nothing to detect. Unless you're referring to the actual Administrator account, where even Windows still uses the shield. –  Oct 13 '10 at 15:34
  • @Matthew: Yes, I know the OP doesn't want the app to run as admin. That does not mean the user won't run it as admin anyhow, e.g. because the user is on Windows XP, deliberately used right-click run as admin, or the user has disabled UAC. – Brian Oct 13 '10 at 15:40
  • @Brian, I'll admit in my own stuff I do the detection for exactly the reasons you point out. I hide the shield icon, but still spawn the second process because it makes the architecture easier. I'll add an edit for simple admin detection. –  Oct 13 '10 at 15:48
  • 4
    Argh! Yoda conditions in C#! My eyes! (otherwise good answer) – Trillian Oct 13 '10 at 15:57
  • How do you "launch your executable again as administrator"? – Gabe Oct 13 '10 at 16:07
  • @Trillian, It's an impossible C/C++ habit to break. I know that they're unnecessary in C#. –  Oct 13 '10 at 17:27
  • Matthew: I see, it's the `info.Verb = "runas";` portion. You should put in a comment indicating such. – Gabe Oct 13 '10 at 17:30
9

Matthew Ferreira's answer goes in to the details on why you need to restart the whole application and what to do when to restart it, however he did not cover how to show the shield icon. Here is some code I use (I think I originally got it from another answer somewhere on this site) that will only show the shield icon when the program is not elevated

/// <summary>
/// Is a button with the UAC shield
/// </summary>
public partial class ElevatedButton : Button
{
    /// <summary>
    /// The constructor to create the button with a UAC shield if necessary.
    /// </summary>
    public ElevatedButton()
    {
        FlatStyle = FlatStyle.System;
        if (!IsElevated()) ShowShield();
    }


    [DllImport("user32.dll")]
    private static extern IntPtr
        SendMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

    private uint BCM_SETSHIELD = 0x0000160C;

    private bool IsElevated()
    {
        WindowsIdentity identity = WindowsIdentity.GetCurrent();
        WindowsPrincipal principal = new WindowsPrincipal(identity);
        return principal.IsInRole(WindowsBuiltInRole.Administrator);
    }


    private void ShowShield()
    {
        IntPtr wParam = new IntPtr(0);
        IntPtr lParam = new IntPtr(1);
        SendMessage(new HandleRef(this, Handle), BCM_SETSHIELD, wParam, lParam);
    }
}

The button checks when it is being constructed if it is in a administrative context and if it is not it draws the shield icon on the button.

If you want the shield icon windows uses, here is a sneaky trick that returns the shield icon as a Bitmap object.

Community
  • 1
  • 1
Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431