DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

コントロールにUAC盾アイコンを表示する

User Account Control」のガイドラインによると、UACが有効になっている時に昇格が必要になるタスクを開始するボタン(あるいは他のコントロール)には、盾アイコンを表示することが推奨されています。ここではコントロールに盾アイコンを表示する方法を幾つか紹介します。

ボタンコントロールに盾アイコンを表示する

ボタンコントロールに盾アイコンを表示するには、SendMessageを使ってBCM_SETSHIELDメッセージを送信します。

以下に、この方法でボタンコントロールに盾アイコンを表示するメソッドの例を示します。

ボタンコントロールに盾アイコンを表示する

VB.NET
コードを隠すコードを選択
'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
C#
コードを隠すコードを選択
//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」にあります。

SHGetStockIconInfo関数を使う

これが正統な方法だと思われますが、Win32 APIを使わなければなりません。SHGetStockIconInfo関数は、Windows Vista以上で使用できます。

以下に、SHGetStockIconInfo関数を使って盾アイコンを取得する例を示します。

VB.NET
コードを隠すコードを選択
'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
C#
コードを隠すコードを選択
//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);
}

SystemIcons.Shieldプロパティを使う

.NET Framework 2.0 SP1以上では、SystemIcons.Shieldプロパティによって、盾アイコンを取得することができます。ただしこのアイコンはどのバージョンのWindowsで呼び出してもWindows Vistaの盾アイコン(4色)となり、Windows 7の盾アイコン(黄色と青の2色)とは色が異なります。

以下に、ボタン、メニュー、ピクチャボックス(LinkLabelコントロールの左に配置)に盾アイコンを表示する例を示します。

SystemIcons.Shieldプロパティで盾アイコンを表示

VB.NET
コードを隠すコードを選択
'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
C#
コードを隠すコードを選択
//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以上の時だけ盾アイコンを表示しています。

VB.NET
コードを隠すコードを選択
'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
C#
コードを隠すコードを選択
//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;
    }
}

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。
  • コードの先頭に記述されている「Imports ??? がソースファイルの一番上に書かれているものとする」(C#では、「using ???; がソースファイルの一番上に書かれているものとする」)の意味が分からないという方は、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。