Introduction
Any application that does not always need administrator privileges should not run with them by default. However, when a user wants to perform a task that requires elevation, you need to show them that this is required by displaying the Vista shield icon. When this is clicked, your application will then need to restart with administrator privileges. Interested? Then read on...
Making the VistaSecurity Class
First we need to create a VistaSecurity
class. Inside it we need the SendMessage
API.
[DllImport("user32")]
public static extern UInt32 SendMessage
(IntPtr hWnd, UInt32 msg, UInt32 wParam, UInt32 lParam);
internal const int BCM_FIRST = 0x1600;
internal const int BCM_SETSHIELD = (BCM_FIRST + 0x000C);
First, before we add a shield, we need to know if the process is elevated or not which is easily done. To add a shield to a button, make sure that the button uses FlatStyle.System
and then sends the appropriate message using the API. The important API function parameters are the handle of the button and the BCM_SETSHIELD
message.
static internal bool IsAdmin()
{
WindowsIdentity id = WindowsIdentity.GetCurrent();
WindowsPrincipal p = new WindowsPrincipal(id);
return p.IsInRole(WindowsBuiltInRole.Administrator);
}
static internal void AddShieldToButton(Button b)
{
b.FlatStyle = FlatStyle.System;
SendMessage(b.Handle, BCM_SETSHIELD, 0, 0xFFFFFFFF);
}
We then need a way to elevate the process if required. We just restart the process with the "runas"
Verb. The current unelevated process is then exited unless the System.ComponentModel.Win32Exception
is thrown, as this indicates that the user has clicked Cancel on the UAC prompt.
internal static void RestartElevated()
{
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.UseShellExecute = true;
startInfo.WorkingDirectory = Environment.CurrentDirectory;
startInfo.FileName = Application.ExecutablePath;
startInfo.Verb = "runas";
try
{
Process p = Process.Start(startInfo);
}
catch(System.ComponentModel.Win32Exception ex)
{
return;
}
Application.Exit();
}
That's it for the VistaSecurity
class.
Using the code
On the form, you need the following code in your constructor after InitializeComponent
which will add the shield.
if (!VistaSecurity.IsAdmin())
{
this.Text += " (Standard)";
VistaSecurity.AddShieldToButton(buttonGetElevation);
}
else
this.Text += " (Elevated)";
When the button is clicked we need to check if you have permission to perform the required action or elevate.
if (VistaSecurity.IsAdmin())
{
DoAdminTask();
}
else
{
VistaSecurity.RestartElevated();
}
Well, that's how to display a shield and get elevation!
Points of Interest
For the admin task in this demo, I added a file VISTA.TXT to All Users' start menu at \ProgramData\Microsoft\Windows\Start Menu\. To get rid of the file, run the demo again (admin credentials may be required!). The Try Admin Task button just tries to create the file without asking for elevation.
History
- 22 Apr 07 - Article written
- 24 Apr 07 - Checking of Administrator account works for non-English versions of Windows by using
WindowsBuiltInRole.Administrator
instead of a non-localized string. In DoAdminTask()
, MessageBoxes
show what has been done, more error catching added, and an Environment.SpecialFolder
is used to get the path.