VISTA User Account Control with .NET

Before moving over to Silverlight I'll spend one last time to post some additional experiences I've had with Windows Vista Development.

In this "mini-series" I'm gonna start with clearly the most well-known, most prominent and most feared features: User Account Control (UAC).

NOTE: You can find demos to all topics covered here!

As I'm sure you already know, UAC starts every application with standard user privileges, also if your current user account has administrative rights assigned. This is done for security reasons: As we developer often work as administrator (yeah! this is for real men! ;-)) it's easy for malware and spyware apps to install themselves into registry auto-startup keys and do other *very bad* things! So now with Vista, those Apps don't get administrative permissions, unless they explicitely request so. *BANG* - then UAC gets active and display a so called "elevation prompt". The user is asked for permission to grant administrative priviliges to an application.

What is the impact of this UAC to "old" .NET applications ?! Let me visualize this to you in a small chart:

UAC Flow

If an application doesn't do anything requiring administrative permissions (things like writing to %PROGRAMFILES%, Windows folder, or registry require so) it works the same way as it did with XP.

To allow compatibility for legacy apps, there is a redirection functionality, which redirects access for "old" apps, which are NOT aware of UAC (no UAC manifest):

  • Folders: %ProgramFiles%; %Windir%; %Windir%\system32 --> to %HOMEPATH%\AppData\Local\VirtualStore
  • Registry: HKLM\Software --> to HKCU\Software\Classes\VirtualStore\MACHINE\SOFTWARE

So if a pre-Vista application wants to write to one of these locations it is redirected to the corresponding virtual store.

When reading a file, first the virtual store is looked for it, afterwards the real location!

To establish UAC compliancy for your application, you need to do the following:

  1. Add a new file called AssemblyName.exe.manifest (the so called application manifest) to your project

  2. Insert this XML fragment into the manifest:

     <?xml version="1.0" encoding="utf-8" ?>
    <assembly xmlns="urn:schemas-microsoft-com:asmv.v1"
              manifestVersion="1.0">
      <assemblyIdentity version="1.0.0.0" 
        processorArchitecture="X86"
        name="AppName"
        type="win32" />
      <description>App Description</description>
      <trustInfo xmlns="urn:schemas-microsoft.com:asm.v2">
        <security>
          <requestedPrivileges>
            <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
          </requestedPrivileges>
        </security>
      </trustInfo>
    </assembly>
    

  3. Modify the sections marked:

    • uiAccess: Your ap needs acces to OTHER UIs (see MSDN on this, should normally be false)
    • requestedExecutionLevel:
      • requireAdministrator: App requires administrative privileges --> elevation prompt will be displayed at startup
      • highestAvailable: If user is in admin group, start with elevated privileges, else with default privileges
      • asInvoker: Always run with default privileges

    4.   Either copy the application manifest into the executable's directory or 
          embed it into the executable this way:

    • Go to the project properties - Build

    • Add this line to post build actions:

      "$(DevEnvDir)..\..\SDK\v2.0\bin\mt.exe" -manifest "$(ProjectDir)$(TargetName).exe.manifest" –outputresource:"$(TargetDir)$(TargetFileName)";#1

Now your application is UAC aware, redirection is disabled! That way, if you start the application without administrative privileges, you'll get an UnauthorizedException if you write to one of the administrative locations mentioned above.

Note: You cannot elevate an already running process. Thus you should refactor your app to be seperated into admin & non-admin operations - running the default application wiht normale privileges and starting another elevated process for each administrative operation! Another option is using an elevated COM object (see UAC-Blog for details).