最近在設計網站後台管理系統的時候,想到了是否可以透過頁面重啟Windows伺服器
到Google搜尋了一下,找到了一段似乎很普遍的程式碼
事實證明,這段程式碼在寫桌面應用程式例如Console或Windows Form程式的時候可以正常運行,但是透過ASP.NET呼叫則無法通過
但是我還是把這段程式碼貼出來,因為其中除了個別兩行外,其他的還是重啟伺服器的必須程式碼
新建一個類,在裡面填入如下程式碼:
首先是命名空間,呼叫Win API的時候,InteropServices不可少:
using System;
using System.Runtime.InteropServices;
然後是一系列的常數聲明: protected const int SE_PRIVILEGE_ENABLED = 0x2;
protected const int TOKEN_QUERY = 0x8;
protected const int TOKEN_ADJUST_PRIVILEGES = 0x20;
protected const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege";
protected const int EWX_LOGOFF = 0x0;
protected const int EWX_SHUTDOWN = 0x1;
protected const int EWX_REBOOT = 0x2;
protected const int EWX_FORCE = 0x4;
protected const int EWX_POWEROFF = 0x8;
protected const int EWX_FORCEIFHUNG = 0x10;
定義Luid結構,注意屬性: [StructLayout(LayoutKind.Sequential, Pack=1)]
protected struct LuidStruct {
public int Count;
public long Luid;
public int Attr;
}
外部非託管DLL的聲明: [DllImport("kernel32.dll", ExactSpelling=true)]
protected static extern IntPtr GetCurrentProcess();
[DllImport("advapi32.dll", SetLastError=true)]
protected static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
[DllImport("advapi32.dll", SetLastError=true)]
protected static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
[DllImport("advapi32.dll", SetLastError=true, ExactSpelling=true)]
protected static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref LuidStruct newst, int len, IntPtr prev, IntPtr relen);
[DllImport(""32.dll", SetLastError,true
protected static extern bool ExitWindowsEx(int flg, int rea);
在NT級的作業系統上,需要先通知Windows系統即將關機,並且要取得關機的權限
以下就是關機、重啟以及註銷的實作: protected static void DoExitWindows(int flg) {
LuidStruct tp;
IntPtr hproc = GetCurrentProcess();
IntPtr htok = IntPtr.Zero;
OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
tp.Count = 1;
tp.Luid = 0;
tp.Attr = SE_PRIVILEGE_ENABLED;
LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref tp.Luid);
AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
ExitWindowsEx(flg, 0);
}
public static void Shutdown() {
DoExitWindows(EWX_SHUTDOWN);
}
public static void Reboot() {
DoExitWindows(EWX_REBOOT | EWX_FORCE);
}
public static void Logoff() {
DoExitWindows(EWX_LOGOFF);
}
至此,重新啟動程式碼結束,這段程式碼可以很好地工作在互動環境下,也就是在使用者已經登入到Windows的情況下
但是ASP.NET是運行在非交互環境下的,查閱MSDN,在ExitWindowsEx函數定義下面發現這樣一段話:
The ExitWindowsEx function returns as soon as it has initiated the shutdown process. The shutdown or logoff then proceeds asynchronously. The function is designed to stop all processes in the caller's logon session the not, unction the succ, unction the succn logon session the not, uncif. without actually shutting down the computer. If you are not the interactive user, use the InitiateSystemShutdown or InitiateSystemShutdownEx function.於是得到啟發,發現非交互下重啟伺服器不可以
用ExitWindowsEx,需要將其替換成
Shystm
.dll", SetLastError=true, ExactSpelling=false)]
protected static extern bool InitiateSystemShutdown(string name, string msg, int timeout, bool force, bool reboot);
參數解釋:
name:機器名,用於重啟區域網路內的其它機器,如果為null 則是本機
msg:重新啟動訊息,會顯示在重新啟動訊息框上,在Windows 2003和XP中也會作為訊息日誌儲存
timeout:如果不是0,那麼會顯示一個重新訊息框,倒數timeout秒後重啟
force:強制重啟,不等待應用程式提示是否儲存工作,對於伺服器來說,應該是true
reboot:是否是重啟,如果是false,那麼做關機處理,對於伺服器,應該是true
先按照文章一開始的方法呼叫AdjustTokenPrivileges 得到Privilege,然後在ASP.NET頁裡面執行: InitiateSystemShutdown(null,null,0, true,true);
系統就重啟了
這裡有一點說下,如果重啟本機,那麼多半會回傳Service Unavailable錯誤,這是因為在ASP.NET執行結束之前系統已經開始結束各個進程了,當然包括ASP.NET進程,算是正常表現,雖然看起來有些不太舒服
另外由於目前我只在自己機器上測試通過,所以沒有詳細研究權限問題,所以無法確定在一般伺服器上是否可以正常運行
初步只想到可以用權限模擬解決,即在web.config檔system.web節寫上<identity impersonate="true" userName="Administrator" password="pass">,但沒有經過確認,有時間會試試看。 web.config不是很安全,所以這裡可能要利用DPAPI,有點扯遠了,就先到這裡吧。