tags:

views:

138

answers:

1

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?

+3  A: 

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 (null != Process.Start(info))
{ 
    // 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 your 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 (null != identity)
        {
            WindowsPrincipal principal = new WindowsPrincipal(identity);
            return principal.IsInRole(WindowsBuiltInRole.Administrator);
        }

        return false;
    }
Matthew Ferreira
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
@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.
Matthew Ferreira
@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
@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.
Matthew Ferreira
Argh! Yoda conditions in C#! My eyes! (otherwise good answer)
Trillian
How do you "launch your executable again as administrator"?
Gabe
@Trillian, It's an impossible C/C++ habit to break. I know that they're unnecessary in C#.
Matthew Ferreira
@Gabe, The very first code sample I provide.
Matthew Ferreira
Matthew: I see, it's the `info.Verb = "runas";` portion. You should put in a comment indicating such.
Gabe