コンピュータの電源を切ったり、システムをシャットダウン、再起動、ログオフさせることは、.NET Frameworkの機能だけではできません。ここでは、Win32 API、WMI、shutdown.exeによる方法を紹介します。
ExitWindowsEx関数を使用するために、まずは次のようなメソッドを記述します。
Public Enum ExitWindows EWX_LOGOFF = &H0 EWX_SHUTDOWN = &H1 EWX_REBOOT = &H2 EWX_POWEROFF = &H8 EWX_RESTARTAPPS = &H40 EWX_FORCE = &H4 EWX_FORCEIFHUNG = &H10 End Enum <System.Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True)> _ Public Shared Function ExitWindowsEx(ByVal uFlags As ExitWindows, _ ByVal dwReason As Integer) As Boolean End Function
public enum ExitWindows : uint { EWX_LOGOFF = 0x00, EWX_SHUTDOWN = 0x01, EWX_REBOOT = 0x02, EWX_POWEROFF = 0x08, EWX_RESTARTAPPS = 0x40, EWX_FORCE = 0x04, EWX_FORCEIFHUNG = 0x10, } [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)] public static extern bool ExitWindowsEx(ExitWindows uFlags, int dwReason);
さらにWindows NT系のOSでは、AdjustTokenPrivileges関数によりSE_SHUTDOWN_NAMEセキュリティ特権を有効にする必要があります。これは、例えば次のようなコードになります。
<System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError:=True)> _ Private Shared Function GetCurrentProcess() As IntPtr End Function <System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError:=True)> _ Private Shared Function OpenProcessToken(ByVal ProcessHandle As IntPtr, _ ByVal DesiredAccess As Integer, _ ByRef TokenHandle As IntPtr) As Boolean End Function <System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError:=True)> _ Private Shared Function CloseHandle(ByVal hHandle As IntPtr) As Boolean End Function <System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError:=True, _ CharSet:=System.Runtime.InteropServices.CharSet.Auto)> _ Private Shared Function LookupPrivilegeValue(ByVal lpSystemName As String, _ ByVal lpName As String, _ ByRef lpLuid As Long) As Boolean End Function <System.Runtime.InteropServices.StructLayout( _ System.Runtime.InteropServices.LayoutKind.Sequential, Pack:=1)> _ Private Structure TOKEN_PRIVILEGES Public PrivilegeCount As Integer Public Luid As Long Public Attributes As Integer End Structure <System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError:=True)> _ Private Shared Function AdjustTokenPrivileges(ByVal TokenHandle As IntPtr, _ ByVal DisableAllPrivileges As Boolean, _ ByRef NewState As TOKEN_PRIVILEGES, _ ByVal BufferLength As Integer, _ ByVal PreviousState As IntPtr, _ ByVal ReturnLength As IntPtr) As Boolean End Function 'シャットダウンするためのセキュリティ特権を有効にする Public Shared Sub AdjustToken() Const TOKEN_ADJUST_PRIVILEGES As Integer = &H20 Const TOKEN_QUERY As Integer = &H8 Const SE_PRIVILEGE_ENABLED As Integer = &H2 Const SE_SHUTDOWN_NAME As String = "SeShutdownPrivilege" If Environment.OSVersion.Platform <> PlatformID.Win32NT Then Return End If Dim procHandle As IntPtr = GetCurrentProcess() 'トークンを取得する Dim tokenHandle As IntPtr OpenProcessToken(procHandle, _ TOKEN_ADJUST_PRIVILEGES Or TOKEN_QUERY, tokenHandle) 'LUIDを取得する Dim tp As New TOKEN_PRIVILEGES() tp.Attributes = SE_PRIVILEGE_ENABLED tp.PrivilegeCount = 1 LookupPrivilegeValue(Nothing, SE_SHUTDOWN_NAME, tp.Luid) '特権を有効にする AdjustTokenPrivileges(tokenHandle, False, tp, 0, IntPtr.Zero, IntPtr.Zero) '閉じる CloseHandle(tokenHandle) End Sub
[System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr GetCurrentProcess(); [System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError = true)] private static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle); [System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)] private static extern bool CloseHandle(IntPtr hObject); [System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] private static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out long lpLuid); [System.Runtime.InteropServices.StructLayout( System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 1)] private struct TOKEN_PRIVILEGES { public int PrivilegeCount; public long Luid; public int Attributes; } [System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError = true)] private static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, int BufferLength, IntPtr PreviousState, IntPtr ReturnLength); //シャットダウンするためのセキュリティ特権を有効にする public static void AdjustToken() { const uint TOKEN_ADJUST_PRIVILEGES = 0x20; const uint TOKEN_QUERY = 0x8; const int SE_PRIVILEGE_ENABLED = 0x2; const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege"; if (Environment.OSVersion.Platform != PlatformID.Win32NT) return; IntPtr procHandle = GetCurrentProcess(); //トークンを取得する IntPtr tokenHandle; OpenProcessToken(procHandle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out tokenHandle); //LUIDを取得する TOKEN_PRIVILEGES tp = new TOKEN_PRIVILEGES(); tp.Attributes = SE_PRIVILEGE_ENABLED; tp.PrivilegeCount = 1; LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, out tp.Luid); //特権を有効にする AdjustTokenPrivileges( tokenHandle, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero); //閉じる CloseHandle(tokenHandle); }
システムをシャットダウンして電源を切るには、次のようにEWX_POWEROFFを指定してExitWindowsExメソッドを呼び出します。
'シャットダウンする
AdjustToken()
ExitWindowsEx(ExitWindows.EWX_POWEROFF, 0)
//シャットダウンする
AdjustToken();
ExitWindowsEx(ExitWindows.EWX_POWEROFF, 0);
上記のようにExitWindowsExを呼び出したとき、例えば、アプリケーションで文章を作成中であれば、文章を保存するか破棄するかキャンセルするかを問うダイアログが表示されることがあります。もしここでユーザーがキャンセルを選択すれば、通常シャットダウンもキャンセルされます。
シャットダウンを確実に実行するために、開いているアプリケーションを強制的に終了させるには、次のようにEWX_FORCEを付加します。
'強制的にシャットダウンする AdjustToken() ExitWindowsEx(ExitWindows.EWX_POWEROFF Or ExitWindows.EWX_FORCE, 0)
//強制的にシャットダウンする
AdjustToken();
ExitWindowsEx(ExitWindows.EWX_POWEROFF | ExitWindows.EWX_FORCE, 0);
上記のようにすると、現在実行されているアプリケーションにWM_QUERYENDSESSIONやWM_ENDSESSIONメッセージを送信しないで終了されます。よって、アプリケーションで作成中のデータが失われてしまう可能性があります。
EWX_FORCEの代わりにEWX_FORCEIFHUNGを指定すると、Windows 2000以降では、WM_QUERYENDSESSION、WM_ENDSESSIONメッセージに応答しないアプリケーションが強制的に終了されます。
EWX_POWEROFFの代わりにEWX_SHUTDOWNを指定すると、シャットダウン後も電源が切られなくなります。ただし、Windows XP SP1およびWindows Server 2003では、EWX_SHUTDOWNでも電源が切られます(私の環境では、XP SP2でも電源が切られました)。詳しくは、以下のリンク先をご覧ください。
システムを再起動されるには、EWX_POWEROFFの代わりにEWX_REBOOTを指定します。
ログオフするには、EWX_POWEROFFの代わりにEWX_LOGOFFを指定します。ログオフではセキュリティ特権は必要ありませんので、上記のAdjustTokenメソッドを呼び出す必要はありません。
WMIのWin32_OperatingSystemクラスにより、シャットダウン、再起動、ログオフすることもできます。この方法については、「How to Shutdown My Computer using C#?」で説明されています。この方法はWindows NT系でのみ使えます。
以下に、Win32_OperatingSystemクラスのWin32Shutdownメソッドにより、システムをシャットダウンするコードを示します。なお、System.Management.dllを参照設定に追加しておく必要があります。
'ユーザー特権を有効にするための設定を作成 Dim co As New System.Management.ConnectionOptions() co.Impersonation = System.Management.ImpersonationLevel.Impersonate co.EnablePrivileges = True 'ManagementScopeを作成 Dim sc As New System.Management.ManagementScope("\ROOT\CIMV2", co) 'リモートコンピュータをシャットダウンするには次のようにする 'co.Username = "username" 'co.Password = "password" 'Dim sc As New System.Management.ManagementScope("\\MachineName\ROOT\CIMV2", co) '接続 sc.Connect() Dim oq As New System.Management.ObjectQuery( _ "select * from Win32_OperatingSystem") Dim mos As New System.Management.ManagementObjectSearcher(sc, oq) 'Shutdownメソッドを呼び出す Dim mo As System.Management.ManagementObject For Each mo In mos.Get() 'パラメータを指定 Dim inParams As System.Management.ManagementBaseObject = _ mo.GetMethodParameters("Win32Shutdown") inParams("Flags") = 1 inParams("Reserved") = 0 'Win32Shutdownメソッドを呼び出す Dim outParams As System.Management.ManagementBaseObject = _ mo.InvokeMethod("Win32Shutdown", inParams, Nothing) mo.Dispose() Next mo mos.Dispose()
//ユーザー特権を有効にするための設定を作成 System.Management.ConnectionOptions co = new System.Management.ConnectionOptions(); co.Impersonation = System.Management.ImpersonationLevel.Impersonate; co.EnablePrivileges = true; //ManagementScopeを作成 System.Management.ManagementScope sc = new System.Management.ManagementScope("\\ROOT\\CIMV2", co); //リモートコンピュータをシャットダウンするには次のようにする //co.Username = "username"; //co.Password = "password"; //System.Management.ManagementScope sc = // new System.Management.ManagementScope("\\\\MachineName\\ROOT\\CIMV2", co); //接続 sc.Connect(); System.Management.ObjectQuery oq = new System.Management.ObjectQuery("select * from Win32_OperatingSystem"); System.Management.ManagementObjectSearcher mos = new System.Management.ManagementObjectSearcher(sc, oq); //Shutdownメソッドを呼び出す foreach (System.Management.ManagementObject mo in mos.Get()) { //パラメータを指定 System.Management.ManagementBaseObject inParams = mo.GetMethodParameters("Win32Shutdown"); inParams["Flags"] = 1; inParams["Reserved"] = 0; //Win32Shutdownメソッドを呼び出す System.Management.ManagementBaseObject outParams = mo.InvokeMethod("Win32Shutdown", inParams, null); mo.Dispose(); } mos.Dispose();
上記の例ではWin32ShutdownのFlagsパラメータに1を指定していますが、この値は、先に紹介したExitWindowsEx関数のuFlagsパラメータに指定する値と同じ意味となります。つまり、電源を切るには8、シャットダウンするには1、再起動するには2、ログオフするには0を指定します。さらに、この値に4を足すことにより、EWX_FORCEと同じように強制することもできます。
補足:私が試した限りでは、Flagsパラメータに8を指定すると、強制的にシャットダウンと電源オフが行われるようでした。
また、Win32Shutdownメソッドの代わりにShutdownメソッドを使うこともできます(先に紹介したリンク先では、Shutdownメソッドを使用しています)。Shutdownメソッドは、Win32ShutdownのFlagsパラメータに1を指定したのと同じではないかと思われます。
shutdown.exeというツールを使用して行うこともできます。このツールはWindows XP以上では標準で用意されており、それ以下(Windows 2000、NT4.0)ではResource Kitを導入することにより使えるようになります。
shutdown.exeを使用してシャットダウンする例を示します。Process.Startメソッドで呼び出すだけなので、簡単です。なおProcess.Startメソッドについて詳しくは、「DOSコマンドを実行し出力データを取得する」をご覧ください。
Dim psi As New System.Diagnostics.ProcessStartInfo() psi.FileName = "shutdown.exe" 'コマンドラインを指定 psi.Arguments = "/s" 'ウィンドウを表示しないようにする psi.UseShellExecute = False psi.CreateNoWindow = True '起動 Dim p As System.Diagnostics.Process = System.Diagnostics.Process.Start(psi)
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(); psi.FileName = "shutdown.exe"; //コマンドラインを指定 psi.Arguments = "/s"; //ウィンドウを表示しないようにする psi.UseShellExecute = false; psi.CreateNoWindow = true; //起動 System.Diagnostics.Process p = System.Diagnostics.Process.Start(psi);
Windows XPで「shutdown /?」を実行したときに表示されるshutdown.exeの使用法は、以下の通りです。
使用法: shutdown [/i | /l | /s | /r | /a] [/f] [/m \\コンピュータ名] [/t xx] [/c "コメント"] [/d up:xx:yy] 引数なし このメッセージを表示します (/? と同じです) /i GUI インターフェイスを表示します。このオプ ションは最初に指定する必要があります /l ログオフ (/m オプションとは併用できません) /s コンピュータをシャットダウンします /r コンピュータをシャットダウンして再起動します /a システム シャットダウンを中止します /m \\コンピュータ名 シャットダウン/再起動/中止するリモート コン ピュータの名前です /t xx シャットダウンのタイムアウトを xx 秒に設定 します /c "コメント" シャットダウンのコメントです (127 文字まで) /f 実行中のアプリケーションを警告なしに閉じます /d [u][p]:xx:yy シャットダウンの理由コードです u = ユーザー コード p = 計画されたシャットダウンのコード xx = 重大な理由コード (255 以下の正の整数) yy = 重大ではない理由コード (65535 以下の正の 整数)
このように、シャットダウンするときは引数に"/s"を、再起動するときは"/r"を、ログオフするときは"/l"を指定します。強制的にシャットダウンするには、"/f"パラメータも付けます。
さらに、"/m"引数により、リモートコンピュータをシャットダウンすることもできます。
補足:「c# - Process of Shutdown.exe with multiple arguments, not working」によると、shutdown.exeの引数に「/」ではなく「-」を使うと、複数の引数を指定した時に正常に動作しなくなるということです。