【題外話】
從Vista開始,由于增加了UAC(用戶賬戶控制,User Account Control)功能,使得管理員用戶平時不再擁有能控制所有功能的管理員權限了,所以在調用很多比較重要的功能時需要提升權限來實現。有時候寫的程序需要調用這種權限,那么大概就是分為運行前就提升以及運行后再提升兩種,在這里整理如下。
【文章索引】
如果整個程序都需要使用管理員權限的話(甚至主界面上顯示的內容都需要管理員權限才行),那么可以讓程序一運行時就提升管理員權限,就如同大部分的安裝程序一樣。程序運行時提高權限通常采用設置manifest文件的方式,可以在項目中添加“應用程序清單文件”,添加完成后會生成如下圖所示的一個文件。除此之外,也可以通過選擇項目屬性,然后進入“安全性”選項卡,然后選擇“啟用 ClickOnce 安全設置”后也會在項目的“Properties”目錄下生成app.manifest文件。
在注釋中很明確的說明了如果要在程序中如果需要更高的權限需要修改哪部分,不過非常好奇,這段注釋并沒有說明應該修改成哪種方式。
在http://blogs.msdn.com/b/winsdk/archive/2010/05/31/dealing-with-administrator-and-standard-user-s-context.aspx搜索到了這兩者的區別,區別如下:
Possible requested execution level values
Value |
Description |
Comment |
asInvoker |
The application runs with the same access token as the parent process. |
Recommended for standard user applications. Do refractoring with internal elevation points, as per the guidance provided earlier in this document. |
highestAvailable |
The application runs with the highest privileges the current user can obtain. |
Recommended for mixed-mode applications. Plan to refractor the application in a future release. |
requireAdministrator |
The application runs only for administrators and requires that the application be launched with the full access token of an administrator. |
Recommended for administrator only applications. Internal elevation points are not needed. The application is already running elevated. |
區別即是,highestAvailable按當前賬號能獲取到的權限執行,而requireAdministrator則是以具有完整權限的管理員運行。如果當前賬戶是管理員賬戶的話,那么兩者都是可以的通過提升權限來獲取到管理員權限的;而如果當前賬戶是Guest的話,那么highestAvailable則放棄提升權限而直接運行,而requireAdministrator則允許輸入其他管理員賬戶的密碼來提升權限。
其中App1使用的是highestAvailable,而App2則使用的是requireAdministrator,可以看出在Administrator用戶下都需要提升權限來運行,在關閉UAC的時候都不需要提升權限。而比如在Guest下highestAvailable放棄了提升權限,同時如果使用requireAdministrator的話則會提示類似下圖的輸入其他管理員賬戶密碼的對話框:
所以,如果一個程序必須要求管理員權限才能執行或者才能執行得有意義(比如主界面上的信息需要管理員權限才能顯示之類的),那么不妨設置為requireAdministrator,即使使用Guest登陸的話也需要提升管理員權限;否則也可設置為highestAvaliable。
【二、程序運行后提升權限】
如果程序默認不需要權限就能運行大部分功能,只是在個別功能上需要管理員權限的話,那么可以采用程序運行后,當用戶需要提升權限的時候再提升權限重新運行程序。由于權限是按進程來的,所以如果需要提升整個程序的權限,只能以管理員權限創建進程以后再結束本程序,或者以管理員權限運行其他程序或者程序通過不同參數來執行不同功能。以管理員權限執行程序其實非常簡單,只要將ProcessStartInfo對象的Verb屬性設置為“runas”即可,例如如下的代碼即可以管理員權限重啟本程序。
1 ProcessStartInfo psi = new ProcessStartInfo(); 2 psi.FileName = Application.ExecutablePath; 3 psi.Verb = "runas"; 4 5 try 6 { 7 Process.Start(psi); 8 Application.Exit(); 9 } 10 catch (Exception eee) 11 { 12 MessageBox.Show(eee.Message); 13 }
當然,運行其他程序也是一樣的。
除此之外,我們可能還需要在這個按鈕或菜單上繪制UAC盾牌的圖標,其實系統已經提供了這樣的方法。
1 [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 2 public static extern int SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, IntPtr lParam); 3 4 public const UInt32 BCM_SETSHIELD = 0x160C;
調用的時候只要將按鈕的FlatStyle設置為System,然后采用如下的代碼就可以了,最后一項如果設為0的話則會取消顯示UAC的盾牌圖標。
1 SendMessage(button1.Handle, BCM_SETSHIELD, 0, (IntPtr)1);
不過如果要往菜單上或者WPF的Button上繪制UAC盾牌的圖標就沒法這樣去做了,不過好在我們還可以獲取到系統圖標,不嫌棄的話可以用.NET自帶的System.Drawing.SystemIcons.Shield,其實好多軟件用的就是這個圖標,原圖如下(32×32):
當然,也可以通過DllImport的方式從系統中獲取系統內置的圖標,其中UAC盾牌的圖標的ID是77,代碼如下。
1 [DllImport("shell32.dll", SetLastError = false)] 2 public static extern Int32 SHGetStockIconInfo(SHSTOCKICONID siid, SHGSI uFlags, ref SHSTOCKICONINFO psii); 3 4 public enum SHSTOCKICONID : uint 5 { 6 SIID_SHIELD = 77 7 } 8 9 [Flags] 10 public enum SHGSI : uint 11 { 12 SHGSI_ICON = 0x000000100, 13 SHGSI_SMALLICON = 0x000000001 14 } 15 16 [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 17 public struct SHSTOCKICONINFO 18 { 19 public UInt32 cbSize; 20 public IntPtr hIcon; 21 public Int32 iSysIconIndex; 22 public Int32 iIcon; 23 24 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] 25 public string szPath; 26 }
然后如下調用就可以將UAC盾牌的圖標設置到菜單上了:
1 SHSTOCKICONINFO iconInfo = new SHSTOCKICONINFO(); 2 iconInfo.cbSize = (UInt32)System.Runtime.InteropServices.Marshal.SizeOf(iconInfo); 3 SHGetStockIconInfo(SHSTOCKICONID.SIID_SHIELD, SHGSI.SHGSI_ICON | SHGSI.SHGSI_SMALLICON, ref iconInfo); 4 Icon icon = Icon.FromHandle(iconInfo.hIcon); 5 6 menu.Image = icon.ToBitmap();
圖中menu1是使用的System.Drawing.SystemIcons.Shield,menu2使用的通過shell32.dll獲取到的圖標,button1是使用的SendMessage直接顯示的UAC的圖標。
當然,還是應該判斷一下系統的版本的,確保系統是Vista及以后的版本,否則就不需要提升權限了。判斷是否是Vista只需要判斷系統主版本號是否大于等于6就可以了,例如以下的代碼。
1 Boolean afterVista = (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version.Major >= 6);
如果要判斷當前是否以管理員身份運行,只需引用“System.Security.Principal”這個命名空間,然后就可以通過如下的代碼獲取當前是否以管理員在運行。
1 WindowsIdentity identity = WindowsIdentity.GetCurrent(); 2 WindowsPrincipal principal = new WindowsPrincipal(identity); 3 Boolean isRunasAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator);
除了獲取當前是否是以管理員運行,還可以通過DllImport的方式獲取到當前用戶是否是管理員用戶以及當前進程是否提升了權限(僅限Vista及以上的版本)等等,詳情可以見相關鏈接2中的代碼。
【相關鏈接】
- 編寫C#程序讓其在Win7 下以管理員權限運行:http://www.cr173.com/html/11557_1.html
- UAC self-elevation (CSUACSelfElevation):http://code.msdn.microsoft.com/windowsdesktop/CSUACSelfElevation-644673d3
- How to add an uac shield icon to a MenuItem:http://www.peschuster.de/2011/12/how-to-add-an-uac-shield-icon-to-a-menuitem/
文章列表