WPF で UAC の盾アイコンをメニューに表示する
Windows のセキュリティ機能 UAC を呼び出すボタン類には盾アイコンを表示することが望ましいわけですが、WPF で Windows アプリを作っていて迷ったのでメモしておきます。
開発環境・ターゲット
- JetBrains Rider 2020.2.4
- .NET Framework 4.7.1
- WPF
取得方法
Icon → Bitmap → BitmapImage → Image とキャストすることで MenuItem.Icon に入れられる盾アイコンが手に入ります。もっと簡単な方法ありそうだがわからん。
using System;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
namespace YourNamespace
{
public class UAC
{
const int MAX_PATH = 260;
const uint SIID_SHIELD = 0x00000004D;
const uint SHGSI_ICON = 0x000000100;
const uint SHGSI_LARGEICON = 0x000000000;
const uint SHGSI_SMALLICON = 0x000000001;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
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)]
static extern int SHGetStockIconInfo(uint siid, uint uFlags, ref SHSTOCKICONINFO sii);
public static Image GetShieldImage(bool small)
{
var sii = new SHSTOCKICONINFO();
sii.cbSize = (uint)Marshal.SizeOf<SHSTOCKICONINFO>();
var sizeFlag = small ? SHGSI_SMALLICON : SHGSI_LARGEICON;
var res = SHGetStockIconInfo(SIID_SHIELD, SHGSI_ICON | sizeFlag, ref sii);
if (res != 0) Marshal.ThrowExceptionForHR(res);
var bitmap = System.Drawing.Icon.FromHandle(sii.hIcon).ToBitmap();
using (var memory = new MemoryStream())
{
bitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
return new Image { Source = bitmapImage };
}
}
}
}
余談:苦労のわけ
- MenuItem.Icon は object 型だが、Image 型を入れないと文字列にされてしまうことが一見わからない。
- システムから取得できる盾アイコンは Icon 型。Image 型に直接キャストできない。
2020年11月4日