首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 透過 P/Invoke 呼叫 Windows API 實現關機、重開機、登出

     編輯:關於C#

核心原理

Windows 關機需要 SE_SHUTDOWN_PRIVILEGE 特權。流程:

  1. 取得目前進程 Token (OpenProcessToken)
  2. 查詢關機特權 LUID (LookupPrivilegeValue)
  3. 啟用 Token 中的特權 (AdjustTokenPrivileges)
  4. 呼叫 ExitWindowsEx 執行關機/重開機/登出

完整可執行程式碼

using System;
using System.Runtime.InteropServices;

public static class WindowsShutdown
{
    // --- 常數 ---
    private const int SE_PRIVILEGE_ENABLED = 0x00000002;
    private const int TOKEN_QUERY = 0x00000008;
    private const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
    private const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege";

    private const int EWX_LOGOFF = 0x00;
    private const int EWX_SHUTDOWN = 0x01;
    private const int EWX_REBOOT = 0x02;
    private const int EWX_FORCE = 0x04;
    private const int EWX_POWEROFF = 0x08;
    private const int EWX_FORCEIFHUNG = 0x10;

    // --- 結構 ---
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    private struct TOKEN_PRIVILEGES
    {
        public int PrivilegeCount;
        public long Luid;
        public int Attributes;
    }

    // --- P/Invoke 宣告 ---
    [DllImport("kernel32.dll", ExactSpelling = true)]
    private static extern IntPtr GetCurrentProcess();

    [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
    private static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle);

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref long lpLuid);

    [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
    private static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges,
        ref TOKEN_PRIVILEGES NewState, int BufferLength, IntPtr PreviousState, IntPtr ReturnLength);

    [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
    private static extern bool ExitWindowsEx(int uFlags, int dwReason);

    // --- 公開方法 ---
    public static void Shutdown(bool force = false) => ExitWin(EWX_SHUTDOWN | (force ? EWX_FORCE : 0));
    public static void Reboot(bool force = false) => ExitWin(EWX_REBOOT | (force ? EWX_FORCE : 0));
    public static void Logoff(bool force = false) => ExitWin(EWX_LOGOFF | (force ? EWX_FORCE : 0));
    public static void PowerOff(bool force = false) => ExitWin(EWX_POWEROFF | (force ? EWX_FORCE : 0));

    private static void ExitWin(int flags)
    {
        IntPtr hToken = IntPtr.Zero;
        var tp = new TOKEN_PRIVILEGES { PrivilegeCount = 1, Attributes = SE_PRIVILEGE_ENABLED };

        if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref hToken))
            ThrowWin32();

        if (!LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref tp.Luid))
            ThrowWin32();

        if (!AdjustTokenPrivileges(hToken, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero))
            ThrowWin32();

        if (!ExitWindowsEx(flags, 0))
            ThrowWin32();
    }

    private static void ThrowWin32() => throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
}

使用範例

// 一般關機
WindowsShutdown.Shutdown();

// 強制重開機 (不等待應用程式關閉)
WindowsShutdown.Reboot(force: true);

// 登出目前使用者
WindowsShutdown.Logoff();

注意事項

  • 需管理員權限:應用程式需以系統管理員身分執行,否則 AdjustTokenPrivileges 會失敗 (錯誤 1300: NOT_ALL_ASSIGNED)。
  • EWX_FORCE:強制關閉應用程式,可能導致資料遺失,謹慎使用。
  • 現代替代方案:.NET 6+ 可用 System.Diagnostics.Process.Start("shutdown", "/s /t 0") 簡單場景,但無法精確控制特權流程。
  • 測試建議:先在虛擬機或備用環境驗證,避免誤關機主力開發機。

總結

透過 P/Invoke 完整實作 Windows 關機特權啟用流程,提供 Shutdown/Reboot/Logoff/PowerOff 靜態方法,適合需精確控制關機行為的系統管理工具或嵌入式應用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved