C#でUACに対応する
Windows Vistaで一部の処理で管理者権限が必要だが、起動時に管理者権限を要求するまでもないようなアプリがある。タスクマネージャやProcess Explorerはその典型例だが、こういうアプリにはシールドアイコンが付加されたボタンやメニューがある。
このアイコン自体は、Visual Studio 2008がインストールされていれば、
%ProgramFiles%\Microsoft Visual Studio 9.0\Common7\VS2008ImageLibrary\1033
というディレクトリに存在するVS2008ImageLibrary.zipをshieldで検索すればすぐに出てくる。そのため、この画像をそのままアプリで利用しても良いが、ボタンで利用する場合には、OSからもらってきた方が手っ取り早い。
const uint BCM_SETSHIELD = 0x160CU; SendMessage(buttonHwnd, BCM_SETSHIELD, IntPtr.Zero, new IntPtr(1 /* TRUE */)); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)] static extern int SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
また、ボタン以外の場合には、アイコンとして取得してから設定する必要があるが、.NETのイメージとして取得するには、次のようなコードを書けばよい。
Image GetShieldIcon() { try { const int SIID_SHIELD = 77; const uint SHGFI_ICON = 0x000000100U; const uint SHGFI_SMALLICON = 0x000000001U; // 16x16 SHSTOCKICONINFO sii = new SHSTOCKICONINFO(); sii.cbSize = Marshal.SizeOf(typeof(SHSTOCKICONINFO)); sii.hIcon = IntPtr.Zero; SHGetStockIconInfo(SIID_SHIELD, SHGFI_ICON | SHGFI_SMALLICON, ref sii); return Bitmap.FromHicon(sii.hIcon); // hIconの所有権はBitmapに移る } catch { // なんか問題が起きたらnullを返す return null; } } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] struct SHSTOCKICONINFO { public int cbSize; public IntPtr hIcon; public int iSysImageIndex; public int iIcon; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szPath; } [DllImport("shell32.dll")] static extern int SHGetStockIconInfo(int siid, uint uFlags, ref SHSTOCKICONINFO psii); [DllImport("shell32.dll")] static extern bool DestroyIcon(IntPtr hIcon);
のようなコードを使えば良い(コードはC#)。とはいえ、XPや2000においては、この処理は無意味だし、既に昇格済みならば、このシールドを表示する必要もない。そのため、できれば、Vistaなのかどうかと昇格済みなのかどうかを調べたい。これについては、かなり他力本願ではあるけど、
"C# code to detect UAC elevation on Vista" - Tim Anderson’s ITWriting
Tech writing blogにあるコードを利用するのが手っ取り早い。これを使えば、シールド表示のコードは、
if (VistaTools.IsReallyVista() && !VistaTools.IsElevated()) { // シールド表示が必要 menuItem.Image = GetShieldIcon(); }
のように書くことが出来る。
そして、タスクマネージャなどのように自分自身を昇格して起動し直したいならば、次のようにShellExecuteを"runas"というverb(動詞)で実行する
void RunElevated() { SHELLEXECUTEINFO info = new SHELLEXECUTEINFO(); info.cbSize = Marshal.SizeOf(info); info.hwnd = IntPtr.Zero; info.lpVerb = "runas"; info.lpFile = Assembly.GetExecutingAssembly().Location; info.lpParameters = "-evaluated"; // パラメータは適宜編集してくれ info.lpDirectory = null; info.nShow = SW.SHOWNORMAL; info.hInstApp = IntPtr.Zero; info.fMask = SEE_MASK.WAITFORINPUTIDLE; ShellExecuteEx(ref info); } [DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Auto)] static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO lpExecInfo); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] struct SHELLEXECUTEINFO { public int cbSize; public SEE_MASK fMask; public IntPtr hwnd; [MarshalAs(UnmanagedType.LPTStr)] public string lpVerb; [MarshalAs(UnmanagedType.LPTStr)] public string lpFile; [MarshalAs(UnmanagedType.LPTStr)] public string lpParameters; [MarshalAs(UnmanagedType.LPTStr)] public string lpDirectory; public SW nShow; public IntPtr hInstApp; public IntPtr lpIDList; [MarshalAs(UnmanagedType.LPTStr)] public string lpClass; public UIntPtr hKeyClass; public uint dwHotKey; public IntPtr hIcon; public IntPtr hProcess; } enum SW : int { HIDE = 0, SHOWNORMAL = 1, NORMAL = 1, SHOWMINIMIZED = 2, SHOWMAXIMIZED = 3, MAXIMIZE = 3, SHOWNOACTIVATE = 4, SHOW = 5, MINIMIZE = 6, SHOWMINNOACTIVE = 7, SHOWNA = 8, RESTORE = 9, SHOWDEFAULT = 10, FORCEMINIMIZE = 11, MAX = 11 } [Flags] enum SEE_MASK { NOCLOSEPROCESS = 0x00000040, NOASYNC = 0x00000100, WAITFORINPUTIDLE = 0x02000000, }