前言
本文由網友(@nobody)投稿,歡迎留言技術討論。
以系統管理員身分不只是簡單的啟動一個處理程序,在實際開發過程中遇到的情況可能會複雜得多。比如使用者打開應用程式就是以系統管理員身分啟動的,那這個時候就不需要再以系統管理員身分自啟;比如使用者是在無人值守的情況下使用,就需要考慮系統管理員提權的提示行為,只有在「不提示,直接提升」的情況下才以系統管理員身分啟動;比如系統管理員啟動方式會進行傳遞,比如應用程式 A 以系統管理員身分啟動,那應用程式 A 啟動應用程式 B 通常情況下,應用程式 B 預設獲取了應用程式 A 的系統管理員權限等。
本文主要介紹在無人值守情況下,以系統管理員身分啟動的實現過程。其他情況,只要進行靈活組合就應該能夠實現。
無人值守的主要特點是應用程式開機自啟、崩潰重啟,程式自動執行。程式中不能有阻斷程式啟動或是執行的操作,比如彈出視窗提示讓使用者確認或是讓使用者輸入帳號密碼。解決無人值守的情況,主要解決思路如下圖:

需要注意的是,如果使用者提權行為非「不提示,直接提升」,而一定要以系統管理員身分啟動,就一定要更改提權行為。可以透過執行「gpedit.msc」→ 電腦設定 →Windows 設定 → 安全性設定 → 本機原則 → 安全性選項 → 使用者帳戶控制: 在管理員核准模式下管理員的提升權限提示行為 來進行更改。
實作步驟
下面為流程中設計的步驟程式碼實作方法:
- 判斷目前應用程式是否是以系統管理員身分啟動,程式碼如下:
public static bool IsRunAsAdmin()
{
WindowsIdentity windowsIdentity = WindowsIdentity.GetCurrent();
WindowsPrincipal windows = new WindowsPrincipal(windowsIdentity);
if (windows.IsInRole(WindowsBuiltInRole.Administrator))
{
return true;
}
return false;
}
注意:此程式碼是判斷使用者是否以系統管理員身分啟動,不是使用者是否是系統管理員。因為雖然使用者是系統管理員,但是啟動應用程式的時候,不一定是以系統管理員身分啟動的。網上搜尋判斷使用者是否為系統管理員,搜尋出來的大部分都是這個程式碼。
- 以系統管理員身分啟動,這是最基本的程式碼,程式碼如下:
public static void RunAsAdmin()
{
ProcessStartInfo startInfo = new ProcessStartInfo();
//設定以系統管理員身分啟動標記
startInfo.Verb = "runas";
//使用 shell 啟動處理程序
startInfo.UseShellExecute = true;
startInfo.FileName = Process.GetCurrentProcess().MainModule.FileName;
Process.Start(startInfo);
}
注意:Verb 是以系統管理員啟動的識別碼,除了設定 Verb,還需要設定 UseShellExecute=true,使用 shell 啟動處理程序,不然啟動時系統管理員權限會進行傳遞,即如果原先的應用程式不是以系統管理員身分啟動的,那麼傳遞以後也不會以系統管理員身分啟動,以系統管理員身分啟動就會失敗。啟動物件還有很多屬性可以設定,讀者可以自行研究。
- 判斷目前是否開啟 UAC(使用者帳戶控制),關閉 UAC 表示目前使用者有系統管理員權限,程式碼如下:
public static bool IsUACEnabled()
{
//登錄檔項目
RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System", false);
if (key != null)
{
//取得子機碼 EnableLUA 的值,1 表示開啟了 UAC
object value = key.GetValue("EnableLUA");
if (value != null && value.ToString() == "1")
{
return true;
}
}
return false;
}
- 判斷目前使用者是否在系統管理員權限群組,程式碼如下:
public static bool IsInAdminGroup()
{
WindowsIdentity windowsIdentity = WindowsIdentity.GetCurrent();
WindowsPrincipal windowsPrincipal = new WindowsPrincipal(windowsIdentity);
var claims = windowsPrincipal.Claims;
//宣告集合中有使用者群組的資訊,S-1-5-32-544 代表系統管理員群組
return claims.Any(c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/denyonlysid" && c.Value == "S-1-5-32-544");
}
- 判斷 UAC 系統管理員提升權限提示行為,這裡需要判斷行為是否為「不提示,直接提升」,程式碼如下:
public static bool IsUpPermissionWithOutTip()
{
//登錄檔項目
RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System", false);
if (key != null)
{
//取得子機碼 ConsentPromptBehaviorAdmin 的值,0 表示開啟了不提示直接提升,就不會造成阻斷
object value = key.GetValue("ConsentPromptBehaviorAdmin");
if (value != null && value.ToString() == "0")
{
return true;
}
}
return false;
}
上述是根據主要流程提取出的 5 個方法。在實際開發過程中,可能還要考慮以系統管理員身分啟動失敗後無限重啟的問題。方法中也沒考慮例外情況,使用者需要根據自己的需求,做例外處理。
總結
本文主要是根據作者的開發經驗整理的無人值守情況下的實作方式,讀者在開發過程中要根據實際需求進行靈活組裝,或是添加上述沒有的功能或是邏輯。作者相關知識也可能存在盲區,或是邏輯考慮不周的,歡迎指正。