管理後台常需提供「重啟伺服器」功能。桌面應用程式可直接呼叫 Win32 API 重啟,但 ASP.NET 執行於 `w3wp.exe` 背景進程,預設帳戶(`ApplicationPoolIdentity` 或 `NetworkService`)不具備關機權限,直接呼叫會失敗。
using System;
using System.Runtime.InteropServices;
public class ServerReboot
{
// 權限常數
const int SE_PRIVILEGE_ENABLED = 0x2;
const int TOKEN_QUERY = 0x8;
const int TOKEN_ADJUST_PRIVILEGES = 0x20;
const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege";
const int EWX_LOGOFF = 0x0;
const int EWX_SHUTDOWN = 0x1;
const int EWX_REBOOT = 0x2;
const int EWX_FORCE = 0x4;
const int EWX_POWEROFF = 0x8;
const int EWX_FORCEIFHUNG = 0x10;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct LUID_AND_ATTRIBUTES
{
public int Count;
public long Luid;
public int Attributes;
}
[DllImport("kernel32.dll", ExactSpelling = true)]
static extern IntPtr GetCurrentProcess();
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
[DllImport("advapi32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref LUID_AND_ATTRIBUTES newst, int len, IntPtr prev, IntPtr relen);
[DllImport("user32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool ExitWindowsEx(int uFlags, int dwReason);
public static bool Reboot(bool force = true)
{
IntPtr hToken = IntPtr.Zero;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, ref hToken))
return false;
long luid = 0;
if (!LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref luid))
return false;
LUID_AND_ATTRIBUTES tp = new LUID_AND_ATTRIBUTES { Count = 1, Luid = luid, Attributes = SE_PRIVILEGE_ENABLED };
if (!AdjustTokenPrivileges(hToken, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero))
return false;
int flags = EWX_REBOOT | (force ? EWX_FORCE : 0);
return ExitWindowsEx(flags, 0);
}
}
| 限制 | 說明 | 解決方案 |
|---|---|---|
| 權限不足 | 應用程式池身分無 `SeShutdownPrivilege` | 1. 將應用程式池身分設為具備關機權限的帳戶(如 Administrators 群組) 2. 或建立 Windows Service 接收指令執行重啟,網頁僅呼叫 Service |
| Session 0 隔離 | Vista/Server 2008+ 服務運行於 Session 0,互動式重啟對話框不顯示 | 使用 `EWX_FORCE` 強制重啟,或透過 Service Control Manager (SCM) 發送重啟指令 |
| 安全風險 | 網頁直接重啟伺服器極度危險 | 多重驗證:IP 白名單、雙因子認證、操作日誌、確認對話框 |
若僅需重啟網站而非整台伺服器:
// 使用 Microsoft.Web.Administration (需 IIS 7+)
using (ServerManager sm = new ServerManager())
{
ApplicationPool pool = sm.ApplicationPools["MyAppPool"];
pool.Recycle(); // 或 pool.Stop(); pool.Start();
}
此方式權限需求較低,風險可控,適合大多數維護情境。
ASP.NET 直接重啟伺服器技術上可行但阻礙重重,生產環境強烈建議採用「網頁下指令 → Windows Service 執行」的間接架構,並嚴格管控存取權限。