「User Account Control」のガイドラインによると、UACが有効になっている時に昇格が必要になるタスクを開始するボタン(あるいは他のコントロール)には、盾アイコンを表示することが推奨されています。ここではコントロールに盾アイコンを表示する方法を幾つか紹介します。
ボタンコントロールに盾アイコンを表示するには、SendMessageを使ってBCM_SETSHIELDメッセージを送信します。
以下に、この方法でボタンコントロールに盾アイコンを表示するメソッドの例を示します。
'Imports System.Windows.Forms 'Imports System.Runtime.InteropServices <DllImport("user32.dll")> _ Private Shared Function SendMessage(ByVal hWnd As HandleRef, _ ByVal Msg As UInt32, _ ByVal wParam As IntPtr, _ ByVal lParam As IntPtr) As IntPtr End Function Private Const BCM_FIRST As Integer = &H1600 Private Const BCM_SETSHIELD As Integer = BCM_FIRST + &HC ''' <summary> ''' UACの盾アイコンをボタンコントロールに表示(あるいは、非表示)する ''' </summary> ''' <param name="targetButton">盾アイコンを表示するボタンコントロール</param> ''' <param name="showShield">盾アイコンを表示する時はtrue。 ''' 非表示にする時はfalse1。</param> Public Shared Sub SetShieldIcon(ByVal targetButton As Button, _ ByVal showShield As Boolean) If targetButton Is Nothing Then Throw New ArgumentNullException("targetButton") End If 'Windows Vista以上か確認する If Environment.OSVersion.Platform <> PlatformID.Win32NT OrElse _ Environment.OSVersion.Version.Major < 6 Then Return End If 'FlatStyleをSystemにする targetButton.FlatStyle = FlatStyle.System '盾アイコンを表示(または非表示)にする SendMessage(New HandleRef(targetButton, targetButton.Handle), _ BCM_SETSHIELD, IntPtr.Zero, If(showShield, New IntPtr(1), IntPtr.Zero)) End Sub ''' <summary> ''' UACの盾アイコンをボタンコントロールに表示する ''' </summary> ''' <param name="targetButton">盾アイコンを表示するボタンコントロール</param> Public Shared Sub SetShieldIcon(ByVal targetButton As Button) SetShieldIcon(targetButton, True) End Sub
//using System.Windows.Forms; //using System.Runtime.InteropServices; [DllImport("user32.dll")] private static extern IntPtr SendMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam); private const int BCM_FIRST = 0x1600; private const int BCM_SETSHIELD = BCM_FIRST + 0x000C; /// <summary> /// UACの盾アイコンをボタンコントロールに表示(あるいは、非表示)する /// </summary> /// <param name="targetButton">盾アイコンを表示するボタンコントロール</param> /// <param name="showShield">盾アイコンを表示する時はtrue。 /// 非表示にする時はfalse1。</param> public static void SetShieldIcon(Button targetButton, bool showShield) { if (targetButton == null) { throw new ArgumentNullException("targetButton"); } //Windows Vista以上か確認する if (Environment.OSVersion.Platform != PlatformID.Win32NT || Environment.OSVersion.Version.Major < 6) { return; } //FlatStyleをSystemにする targetButton.FlatStyle = FlatStyle.System; //盾アイコンを表示(または非表示)にする SendMessage(new HandleRef(targetButton, targetButton.Handle), BCM_SETSHIELD, IntPtr.Zero, showShield ? new IntPtr(1) : IntPtr.Zero); } /// <summary> /// UACの盾アイコンをボタンコントロールに表示する /// </summary> /// <param name="targetButton">盾アイコンを表示するボタンコントロール</param> public static void SetShieldIcon(Button targetButton) { SetShieldIcon(targetButton, true); }
上記の方法は、ボタンコントロールにだけ使えます。ガイドラインによると、盾アイコンは、ボタンコントロール以外に、コマンドリンク、リンク、メニューに表示することができます。
ボタン以外のコントロールに盾アイコンを表示するために、盾アイコンを取得する方法を幾つか紹介します。
補足:コマンドリンクにはSendMessageで盾アイコンを表示できます。コマンドリンクを.NET Frameworkで使用する方法は、「Using Windows Vista Command Links in Managed Code」にあります。
これが正統な方法だと思われますが、Win32 APIを使わなければなりません。SHGetStockIconInfo関数は、Windows Vista以上で使用できます。
以下に、SHGetStockIconInfo関数を使って盾アイコンを取得する例を示します。
'Imports System.Windows.Forms 'Imports System.Runtime.InteropServices Private Const MAX_PATH As Integer = 260 Private Const SIID_SHIELD As UInt32 = &H4D Private Const SHGSI_ICON As UInt32 = &H100 Private Const SHGSI_LARGEICON As UInt32 = &H0 Private Const SHGSI_SMALLICON As UInt32 = &H1 <StructLayoutAttribute(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _ Private Structure SHSTOCKICONINFO Public cbSize As UInt32 Public hIcon As IntPtr Public iSysIconIndex As Integer Public iIcon As Integer <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=MAX_PATH)> _ Public szPath As String End Structure <DllImport("shell32.dll", SetLastError:=False)> _ Private Shared Function SHGetStockIconInfo(ByVal siid As UInt32, _ ByVal uFlags As UInt32, _ ByRef sii As SHSTOCKICONINFO) As Integer End Function ''' <summary> ''' UAC盾アイコンを取得する ''' </summary> ''' <param name="smallSize">小さいアイコン(16x16)を取得する時はtrue。 ''' 大きいいアイコン(32x32)を取得する時はfalse。</param> ''' <returns>UAC盾アイコンのIconオブジェクト。 ''' Windows Vista未満のOSの時はnullを返す。</returns> Public Shared Function GetShieldIcon(ByVal smallSize As Boolean) As Icon 'Windows Vista以上か確認する If Environment.OSVersion.Platform <> PlatformID.Win32NT OrElse _ Environment.OSVersion.Version.Major < 6 Then Return Nothing End If Dim sii As New SHSTOCKICONINFO() sii.cbSize = CUInt(Marshal.SizeOf(GetType(SHSTOCKICONINFO))) '盾アイコンを取得する Dim res As Integer = SHGetStockIconInfo(SIID_SHIELD, _ SHGSI_ICON Or (IIf(smallSize, SHGSI_SMALLICON, SHGSI_LARGEICON)), _ sii) '失敗した時は例外をスローする If res <> 0 Then Marshal.ThrowExceptionForHR(res) End If 'Iconオブジェクトを作成する Return Icon.FromHandle(sii.hIcon) End Function
//using System.Drawing; //using System.Runtime.InteropServices; private const int MAX_PATH = 260; private const uint SIID_SHIELD = 0x00000004D; private const uint SHGSI_ICON = 0x000000100; private const uint SHGSI_LARGEICON = 0x000000000; private const uint SHGSI_SMALLICON = 0x000000001; [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct SHSTOCKICONINFO { public uint cbSize; public IntPtr hIcon; public int iSysIconIndex; public int iIcon; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)] public string szPath; } [DllImport("shell32.dll", SetLastError = false)] private static extern int SHGetStockIconInfo(uint siid, uint uFlags, ref SHSTOCKICONINFO sii); /// <summary> /// UAC盾アイコンを取得する /// </summary> /// <param name="smallSize">小さいアイコン(16x16)を取得する時はtrue。 /// 大きいいアイコン(32x32)を取得する時はfalse。</param> /// <returns>UAC盾アイコンのIconオブジェクト。 /// Windows Vista未満のOSの時はnullを返す。</returns> public static Icon GetShieldIcon(bool smallSize) { //Windows Vista以上か確認する if (Environment.OSVersion.Platform != PlatformID.Win32NT || Environment.OSVersion.Version.Major < 6) { return null; } SHSTOCKICONINFO sii = new SHSTOCKICONINFO(); sii.cbSize = (uint)Marshal.SizeOf(typeof(SHSTOCKICONINFO)); //盾アイコンを取得する int res = SHGetStockIconInfo(SIID_SHIELD, SHGSI_ICON | (smallSize ? SHGSI_SMALLICON : SHGSI_LARGEICON), ref sii); //失敗した時は例外をスローする if (res != 0) { Marshal.ThrowExceptionForHR(res); } //Iconオブジェクトを作成する return Icon.FromHandle(sii.hIcon); }
.NET Framework 2.0 SP1以上では、SystemIcons.Shieldプロパティによって、盾アイコンを取得することができます。ただしこのアイコンはどのバージョンのWindowsで呼び出してもWindows Vistaの盾アイコン(4色)となり、Windows 7の盾アイコン(黄色と青の2色)とは色が異なります。
以下に、ボタン、メニュー、ピクチャボックス(LinkLabelコントロールの左に配置)に盾アイコンを表示する例を示します。
'Imports System.Windows.Forms 'Imports System.Runtime.InteropServices '16x16サイズの盾アイコンを取得する Dim shieldIcon As New Icon(SystemIcons.Shield, New Size(16, 16)) Dim shieldBitmap As Bitmap = shieldIcon.ToBitmap() 'ボタンに表示する '盾アイコンがテキストの左に表示されるようにする Button1.TextImageRelation = TextImageRelation.ImageBeforeText Button1.Image = shieldBitmap 'メニューに表示する ToolStripMenuItem1.Image = shieldBitmap 'ピクチャボックスに表示する PictureBox1.Image = shieldBitmap
//using System.Drawing; //using System.Windows.Forms; //16x16サイズの盾アイコンを取得する Icon shieldIcon = new Icon(SystemIcons.Shield, new Size(16, 16)); Bitmap shieldBitmap = shieldIcon.ToBitmap(); //ボタンに表示する //盾アイコンがテキストの左に表示されるようにする Button1.TextImageRelation = TextImageRelation.ImageBeforeText; Button1.Image = shieldBitmap; //メニューに表示する ToolStripMenuItem1.Image = shieldBitmap; //ピクチャボックスに表示する PictureBox1.Image = shieldBitmap;
UAC盾アイコンをリソースに埋め込んでしまうという方法もあります。そのためにはUAC盾アイコンの画像が必要ですが、これがなかなか見つかりません。Visual Studio 2008をインストールしているのであれば、「C:\Program Files\Microsoft Visual Studio 9.0\Common7\VS2008ImageLibrary\1041」フォルダにある「VS2008ImageLibrary.zip」というアーカイブの中に「UAC_shield.ico」と「UAC_shield.png」というファイル名の盾アイコンの画像ファイルがあります。ただしこれらはWindows Vistaの盾アイコンです。Visual Studio 2010の「VS2010ImageLibrary.zip」には、盾アイコンを見つけることができませんでした。
リソースを埋め込む方法は、「Visual Studioでリソースを管理する」をご覧ください。
「User Account Control」のガイドラインには、どのような場合に盾アイコンを表示する(あるいは表示しない)べきかが説明されています。これによると、UACが有効になっている時に、すぐに昇格が必要であるタスクを開始するコントロールに盾アイコンを表示します。UACが無効だったり、ビルトイン(組み込みの)Administratorアカウントであったとしても、盾アイコンを表示します。
また、複数のバージョンのWindowsに対応しているアプリケーションの場合、少なくとも1つのバージョンで昇格が必要ならば盾アイコンを表示します。しかし、無理せず(パフォーマンスを損なわずに、一貫性を維持して)できるのであれば、Windows XPでは盾アイコンを表示しないことを検討します。
盾アイコンを表示する色々なサンプルを見てみると、マイクロソフトの「Download: Windows Vista UAC Demo Sample Code」をはじめとしてほとんどが、アプリケーションを実行しているユーザーに管理者権限があるかを調べ(その方法は「現在のユーザーが管理者か調べる」で説明しています)、管理者権限がない時だけ盾アイコンを表示するようにしています。このようなやり方はガイドラインに従っていないように思えますが、もしかしたらそれでもよいということなのかもしれません。
以下の例では、ガイドラインに従って、フォームのLoadイベントハンドラでWindows Vista以上の時だけ盾アイコンを表示しています。
'Imports System.Windows.Forms 'Imports System.Runtime.InteropServices 'Form1のLoadイベントハンドラ Private Sub Form1_Load(ByVal sender As Object, _ ByVal e As EventArgs) Handles Me.Load 'Windows Vista以上か調べる If Environment.OSVersion.Platform = PlatformID.Win32NT AndAlso _ Environment.OSVersion.Version.Major >= 6 Then '16x16サイズの盾アイコンを取得する Dim shieldIcon As New Icon(SystemIcons.Shield, New Size(16, 16)) Dim shieldBitmap As Bitmap = shieldIcon.ToBitmap() 'メニューに表示する ToolStripMenuItem1.Image = shieldBitmap End If End Sub
//using System.Drawing; //using System.Windows.Forms; //Form1のLoadイベントハンドラ private void Form1_Load(object sender, EventArgs e) { //Windows Vista以上か調べる if (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version.Major >= 6) { //16x16サイズの盾アイコンを取得する Icon shieldIcon = new Icon(SystemIcons.Shield, new Size(16, 16)); Bitmap shieldBitmap = shieldIcon.ToBitmap(); //メニューに表示する ToolStripMenuItem1.Image = shieldBitmap; } }