C# 调用 Windows API 实现关机、重启与注销
在 C# 开发中,有时需要通过程序直接控制操作系统电源状态。本文将演示如何利用 Platform Invocation Services (P/Invoke) 调用 Windows advapi32.dll 和 user32.dll 中的 API,以管理员权限安全地执行关机、重启或注销命令。
实现原理与准备工作
C# 无法直接调用底层 C/C++ API,必须通过声明外部方法(DllImport)进行桥接。实现关机的关键逻辑分为两步:
- 权限提升:当前进程默认没有关机权限,需要通过
AdjustTokenPrivileges开启SE_SHUTDOWN_NAME特权。 - 执行指令:使用
ExitWindowsEx发送具体的关机或重启标志位。
注意:以下代码包含高危系统操作。在生产环境或用户电脑上运行前,请务必确保已保存未编辑的数据,因为关机指令可能导致不可逆的后果。
第1步:定义结构体与常量
首先定义用于令牌调整的结构体以及 API 调用所需的标志位常量。原文代码中存在拼写错误(如 shoutdown),此处已修正为标准命名规范。
1. 导入动态链接库并定义辅助结构
入口:System.Runtime.InteropServices
动作:声明内存布局及外部函数签名。
using System;
using System.Runtime.InteropServices;
class PowerControl
{
// 定义令牌权限结构,Pack=1 确保内存对齐方式正确
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct TokPriv1Luid
{
public int Count;
public long Luid;
public int Attr;
}
// 权限属性常量
internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
// 令牌访问权限
internal const int TOKEN_QUERY = 0x00000008;
internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
// 关机特权名称
internal const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege";
// ExitWindowsEx 的标志位定义
internal const int EWX_LOGOFF = 0x00000000; // 注销
internal const int EWX_SHUTDOWN = 0x00000001; // 关机
internal const int EWX_REBOOT = 0x00000002; // 重启
internal const int EWX_FORCE = 0x00000004; // 强制关闭(不等待应用退出)
internal const int EWX_POWEROFF = 0x00000008; // 断电(部分系统支持)
internal const int EWX_FORCEIFHUNG = 0x00000010; // 如果应用无响应则强制关闭
}
2. 声明外部 API 函数
入口:kernel32.dll, advapi32.dll, user32.dll
动作:映射 Windows 底层函数到 C#。
重要参数:SetLastError=true 允许获取精确的错误代码。
[DllImport("kernel32.dll", ExactSpelling = true)]
internal static extern IntPtr GetCurrentProcess();
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall,
ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool ExitWindowsEx(int flg, int rea);
第2步:封装关机逻辑
入口:PowerControl.DoExitWin
动作:组合上述 API 调用,完成权限赋予与系统指令发送。
private static void DoExitWin(int flg)
{
bool ok;
TokPriv1Luid tp;
// 1. 获取当前进程句柄
IntPtr hproc = GetCurrentProcess();
IntPtr htok = IntPtr.Zero;
// 2. 打开进程令牌,请求调整特权和查询权限
ok = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
if (!ok) return; // 若失败可在此处理错误
// 3. 准备特权数据结构
tp.Count = 1;
tp.Luid = 0;
tp.Attr = SE_PRIVILEGE_ENABLED;
// 4. 查找并获取关机特权的本地唯一标识符 (LUID)
ok = LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref tp.Luid);
// 5. 调整令牌特权,启用 SeShutdownPrivilege
ok = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
// 6. 执行指定的 Windows 操作(关机/重启/注销等)
ok = ExitWindowsEx(flg, 0);
}
第3步:调用执行
在主程序中调用 DoExitWin 并传入对应的标志位。例如,若要重启计算机,将参数改为 EWX_REBOOT;若要强制注销,使用 EWX_LOGOFF。
public static void Main()
{
Console.WriteLine("正在关闭计算机……");
// 示例:执行普通关机
DoExitWin(EWX_SHUTDOWN);
// 其他示例调用(取消注释即可使用):
// DoExitWin(EWX_REBOOT); // 重启
// DoExitWin(EWX_LOGOFF); // 注销当前用户
// DoExitWin(EWX_FORCE | EWX_SHUTDOWN); // 强制关机
}
完成判断与排错
- 成功判断:
ExitWindowsEx返回true,且屏幕变黑进入关机流程。 - 常见错误:若返回
false,请检查是否以管理员权限运行该程序。非管理员账户可能无法获得SeShutdownPrivilege,导致AdjustTokenPrivileges失败。 - 版本兼容:该 API 在 Windows NT/2000/XP/Vista/7/8/10/11 均有效。
简要总结
通过 C# 调用 Windows API 实现关机是一个涉及权限管理和底层通信的典型场景。关键在于正确配置 TokPriv1Luid 结构并使用 AdjustTokenPrivileges 提权。以上就是 C# 调用 Windows API 实现关机的详细内容,更多关于 C# 系统控制方面的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









