Setting UAC shield icon on buttons
Starting from Windows Vista, you should have noticed that actions which require administrative privileges and needs to elevate the current process, displays a shield icon right next to them. For example the Change date and time… button on Date and Time dialog requires admin rights to modify system date, hence displays a UAC shield right next to it:
If your application has any buttons or menu items like this, to conform the Windows UX Guidelines, it is a good idea to display a UAC shield icon on your controls. This CodeProject article has a good example that shows how to add shield icons to regular WinForms buttons. The gist is the following P/Invoke code:
const int BCM_FIRST = 0x1600; //Normal button
const int BCM_SETSHIELD = BCM_FIRST + 0x000C; //Elevated button
static void AddShieldToButton(Button b)
{
b.FlatStyle = FlatStyle.System;
SendMessage(b.Handle, BCM_SETSHIELD, 0, 0xFFFFFFFF);
}
Simply pass your Button
instance reference to AddShieldToButton
and you will have a nice UAC shield icon on your button on Windows Vista and above.
If you are using a third party library for your user interface controls (like DevExpress WinForms) or you need to add an icon to a menu item rather than a button, this method won’t work unfortunately. There is a support ticket on DevExpress Support Center about this issue and the suggested workaround is to use the Image
property of SimpleButton
class. So you can save a 16px * 16px version of this shield icon in your project resources and then use it. This has a minor issue though, if you want to strictly stick to the look and feel of the operating system, you may have problem while using a single hard-coded image. The reason is, this icon on Windows Vista was a shield icon with 4 colors (red, green, blue and yellow) but since Windows 7, it has only 2 colors (blue and yellow). Who knows what it will become on Windows 9 (do not get surprised if this icon becomes a flat metro-style icon). Not a big deal but if you really want to blend in with Windows desktop, these small things matter.
Windows stores these resources in its DLL files and it is a good idea to reuse them. This way, we will stick to what operating system has as the UAC shield icon and won’t need to maintain that image resource in our project. Even WinForms has a managed API to access these kind of icons like error, warning, information, shield, etc. You can get a shield Icon
instance by accessing SystemIcons.Shield
property since .NET Framework 2.0 SP1. There are 2 issues with this approach:
SystemIcons.Shield
returns anIcon
instance whereasSimpleButton.Image
expects anImage
instance.- The icon is in 32px * 32px size but a regular button on Windows should have a small 16px * 16px icon. (On 96 DPI desktops)
To resolve these issues, I have come up with the following piece of code:
static void AddShieldToButton(SimpleButton b)
{
var icon = Icon.FromHandle(SystemIcons.Shield.Handle);
var iconSize = SystemInformation.SmallIconSize;
var bitmap = new Bitmap(iconSize.Width, iconSize.Height);
using (var g = Graphics.FromImage(bitmap))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawIcon(icon, new Rectangle(0, 0, iconSize.Width, iconSize.Height));
}
b.Image = bitmap;
}
This is what you get as a result with DevExpress controls using this method:
This is ugly, huh? The issues are:
-
Even though I am on Windows 8.1, I am getting Windows Vista’s 4 color UAC shield icon. If you dig down the
SystemIcons.Shield
code with Reflector, you will see that the getter of this property first tries to load the system UAC shield icon using a P/Invoke code toLoadIconWithScaleDown
function ofcomctl32.dll
. This call fails, you can see that on your Visual Studio output pane reporting a first chance exception of typeEntryPointNotFoundException
thrown. The reason is,LoadIconWithScaleDown
seems to be removed on a later version ofcomctl32.dll
. Fortunately, the getter has a backup method which gets the icon loaded from theSystem.Drawing.dll
embedded resources, which unfortunately is the old UAC shield icon:new Icon(typeof(SystemIcons), "ShieldIcon.ico");
-
The resized icon looks rubbish. Even though we are using
InterpolationMode.HighQualityBicubic
, scaling down a 32px icon to 16px doesn’t end well.
Digging down a bit more, I have finally managed to get all these issues sorted with the following piece of code:
[DllImport("user32.dll")]
static extern IntPtr LoadImage(
IntPtr hinst,
string lpszName,
uint uType,
int cxDesired,
int cyDesired,
uint fuLoad);
static void AddShieldIcon(SimpleButton button)
{
var size = SystemInformation.SmallIconSize;
var image = LoadImage(IntPtr.Zero, "#106", 1, size.Width, size.Height, 0);
if (image == IntPtr.Zero)
{
return;
}
using (var icon = Icon.FromHandle(image))
{
var bitmap = new Bitmap(size.Width, size.Height);
using (var g = Graphics.FromImage(bitmap))
{
g.DrawIcon(icon, new Rectangle(0, 0, size.Width, size.Height));
}
button.Image = bitmap;
}
}
And this is the result:
Much better, if I may say so myself. This one uses LoadImage
P/Invoke call to get the correct size of the icon, so we don’t need to scale it down. Since this function returns an icon handle, we are just converting the icon to a bitmap. Using this method, you can even put UAC shield icons to your menu items:
Or you can get each and every size of the UAC shield icon, just because…you can: