Recently, when I was designing a website backend management system, I thought about whether I could restart the Windows server through the page.
I searched on Google and found a piece of code that seemed to be very common
. It turns out that this code is used when writing desktop applications such as Console or Windows Form programs. It can run normally, but the call through ASP.NET cannot pass
. But I still posted this code, because except for a few lines, the other codes are necessary to restart the server. Create
a new class and fill in the following code. :
The first is the namespace. When calling Win API, InteropServices is indispensable:
using System;
using System.Runtime.InteropServices;
Then there is a series of constant declarations: 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;
Define the Luid structure, pay attention to the attributes: [StructLayout(LayoutKind.Sequential, Pack=1)]
protected struct LuidStruct {
public int Count;
public long Luid;
public int Attr;
}
Declaration of external unmanaged 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("user32.dll", SetLastError=true, ExactSpelling=true)]
protected static extern bool ExitWindowsEx(int flg, int rea);
On NT-level operating systems, you need to first notify Windows that the system is about to shut down, and obtain permission to shut down.
The following is the implementation of shutdown, restart, and logout: protected static void DoExitWindows(int flg) {
LuidStructtp;
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);
}
At this point, the restart code ends. This code can work well in an interactive environment, that is, when the user has logged in to Windows,
but ASP.NET is running in a non-interactive environment. Check MSDN and find the ExitWindowsEx function definition. I found this passage below:
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. Therefore, if you are not the interactive user, the function can succeed without actually shutting down the computer. If you are not the interactive user, use the InitiateSystemShutdown or InitiateSystemShutdownEx function.
So I was inspired and found that ExitWindowsEx cannot be used to restart the server non-interactively. You need to replace it with InitiateSystemShutdown:
[DllImport("advapi32. dll", SetLastError=true, ExactSpelling=false)]
protected static extern bool InitiateSystemShutdown(string name, string msg, int timeout, bool force, bool reboot);
Parameter explanation:
name: machine name, used to restart other machines in the LAN. If it is null, it is the local machine.
msg: Restart message, will be displayed on the restart message box, and will also be saved as a message log in Windows 2003 and XP
timeout: If it is not 0, a restart message box will be displayed and the countdown will restart after timeout seconds.
force: force restart, do not wait for the application to prompt whether to save the work, for the server, it should be true
reboot: Is it a reboot? If it is false, then do the shutdown process. For the server, it should be true.
First call AdjustTokenPrivileges according to the method at the beginning of the article to get the Privilege, and then execute it in the ASP.NET page: InitiateSystemShutdown(null,null,0, true,true);
The system will restart.
One thing to mention here is that if you restart the machine, it will most likely return a Service Unavailable error. This is because the system has begun to end various processes before the execution of ASP.NET is completed, including of course the ASP.NET process. This is normal. Although the performance seems a bit uncomfortable,
in addition, since I have only passed the test on my own machine, I have not studied the permission issue in detail, so I cannot be sure whether it can run normally on a general server.
At first, I only thought that it could be solved by permission simulation, that is, in In the system.web section of the web.config file, write <identity impersonate="true" userName="Administrator" password="pass">, but it has not been confirmed. I will try it when I have time. web.config is not very secure, so you may need to use DPAPI here, which is a bit far-fetched, so let’s stop here first.