C# WinForms实现关机、重启与注销系统功能详解

Aria
Aria
发布时间:2026-10-14 | 2人浏览过
原创

在Windows系统中,程序执行关机、重启或注销需要特定的用户权限。本文将详细解析如何利用C# Windows Forms结合底层Win32 API实现这些系统级操作,包括权限提升的关键步骤及完整代码示例。

实现原理:权限提升与API调用

在Windows NT及以上版本系统中,普通进程没有直接关闭计算机的权限。必须首先提升当前进程的权限至包含"关闭系统"特权(Shutdown Privilege),然后调用 ExitWindowsEx 函数来触发相应的操作。

第1步:定义必要的常量与结构体

首先需要定义用于标识不同操作模式的常量,以及用于调整令牌权限的结构体。

  • 操作标志位:
    EWX_LOGOFF:注销当前用户。
    EWX_SHUTDOWN:关闭计算机。
    EWX_REBOOT:重新启动计算机。
    EWX_FORCE:强制终止进程,不等待应用程序响应(可选,但可能导致数据丢失)。
  • 权限相关常数:
    SE_PRIVILEGE_ENABLED:启用特权。
    TOKEN_QUERY 和 TOKEN_ADJUST_PRIVILEGES:用于访问和调整令牌权限。

第2步:导入Win32 DLL函数

使用 DllImport 特性引入 kernel32.dll 和 advapi32.dll 中的关键函数:

  1. GetCurrentProcess:获取当前进程的句柄。
  2. OpenProcessToken:打开当前进程的访问令牌。
  3. LookupPrivilegeValue:查找特定系统特权(如关机特权)对应的本地唯一标识符(LUID)。
  4. AdjustTokenPrivileges:修改访问令牌的权限列表,启用关机特权。
  5. ExitWindowsEx:最终执行注销、关机或重启操作。

完整代码实现

以下是一个完整的C# Windows Forms类示例,封装了上述逻辑并提供UI接口。

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;
using System.Threading;

namespace ShutdownSystemApp
{
    public class Form1 : System.Windows.Forms.Form
    {
        private System.Windows.Forms.GroupBox groupBox1;
        private System.Windows.Forms.TextBox textBox1;
        private System.Windows.Forms.Button button1; // 关闭系统
        private System.Windows.Forms.Button button2; // 注销系统
        private System.Windows.Forms.Button button3; // 重启系统
        private System.ComponentModel.IContainer components;
        
        // 权限结构体
        [StructLayout(LayoutKind.Sequential, Pack=1)]
        public struct TokPriv1Luid
        {
            public int Count;
            public long Luid;
            public int Attr;
        }

        // API 声明
        [DllImport("kernel32.dll", ExactSpelling=true)]
        public static extern IntPtr GetCurrentProcess();

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

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

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

        [DllImport("user32.dll", ExactSpelling=true, SetLastError=true)]
        public static extern bool ExitWindowsEx(int flg, int rea);

        // 常量定义
        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 = 0x00000000;
        private const int EWX_SHUTDOWN = 0x00000001;
        private const int EWX_REBOOT = 0x00000002;
        private const int EWX_FORCE = 0x00000004;

        private System.Threading.Timer timer;

        // 核心执行方法
        private static void RebootCommand(int flg)
        {
            bool ok;
            TokPriv1Luid tp;
            IntPtr hproc = GetCurrentProcess();
            IntPtr htok = IntPtr.Zero;

            // 1. 打开当前进程令牌
            ok = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
            if (!ok) return;

            // 2. 设置特权信息
            tp.Count = 1;
            tp.Luid = 0;
            tp.Attr = SE_PRIVILEGE_ENABLED;

            // 3. 获取关机特权的LUID
            ok = LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref tp.Luid);
            
            // 4. 调整令牌权限以启用关机特权
            ok = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
            
            // 5. 执行关机/重启/注销
            ok = ExitWindowsEx(flg, 0);
        }

        public Form1()
        {
            InitializeComponent();
        }

        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if (components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }

        #region Windows 窗体设计器生成的代码
        private void InitializeComponent()
        {
            this.groupBox1 = new System.Windows.Forms.GroupBox();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.button1 = new System.Windows.Forms.Button();
            this.button2 = new System.Windows.Forms.Button();
            this.button3 = new System.Windows.Forms.Button();
            
            // 布局设置...
            this.groupBox1.SuspendLayout();
            this.SuspendLayout();
            
            this.groupBox1.Controls.Add(this.textBox1);
            this.groupBox1.Location = new System.Drawing.Point(48, 24);
            this.groupBox1.Name = "groupBox1";
            this.groupBox1.Size = new System.Drawing.Size(192, 56);
            this.groupBox1.TabIndex = 0;
            this.groupBox1.TabStop = false;
            this.groupBox1.Text = "系统已运行时间";

            this.textBox1.Location = new System.Drawing.Point(24, 24);
            this.textBox1.Name = "textBox1";
            this.textBox1.ReadOnly = true;
            this.textBox1.Size = new System.Drawing.Size(152, 21);
            this.textBox1.TabIndex = 1;

            this.button1.Location = new System.Drawing.Point(16, 112);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(64, 32);
            this.button1.TabIndex = 1;
            this.button1.Text = "关闭系统";
            this.button1.Click += new System.EventHandler(this.button1_Click);

            this.button2.Location = new System.Drawing.Point(112, 112);
            this.button2.Name = "button2";
            this.button2.Size = new System.Drawing.Size(64, 32);
            this.button2.TabIndex = 2;
            this.button2.Text = "注销系统";
            this.button2.Click += new System.EventHandler(this.button2_Click);

            this.button3.Location = new System.Drawing.Point(208, 112);
            this.button3.Name = "button3";
            this.button3.Size = new System.Drawing.Size(64, 32);
            this.button3.TabIndex = 3;
            this.button3.Text = "重启系统";
            this.button3.Click += new System.EventHandler(this.button3_Click);

            this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
            this.ClientSize = new System.Drawing.Size(296, 173);
            this.Controls.Add(this.button3);
            this.Controls.Add(this.button2);
            this.Controls.Add(this.button1);
            this.Controls.Add(this.groupBox1);
            this.Name = "Form1";
            this.Text = "简单的关机程序";
            this.Load += new System.EventHandler(this.Form1_Load);
            
            this.groupBox1.ResumeLayout(false);
            this.ResumeLayout(false);
        }
        #endregion

        [STAThread]
        static void Main()
        {
            Application.Run(new Form1());
        }

        private void button1_Click(object sender, System.EventArgs e)
        {
            RebootCommand(EWX_SHUTDOWN + EWX_FORCE);
        }

        private void button2_Click(object sender, System.EventArgs e)
        {
            RebootCommand(EWX_LOGOFF + EWX_FORCE);
        }

        private void button3_Click(object sender, System.EventArgs e)
        {
            RebootCommand(EWX_REBOOT + EWX_FORCE);
        }

        private void Form1_Load(object sender, System.EventArgs e)
        {
            textBox1.Text = (Environment.TickCount / (1000 * 60)).ToString() + "分钟";
            timer = new System.Threading.Timer(new TimerCallback(OnTimer), null, 0, 1000);
        }

        private void OnTimer(object state)
        {
            textBox1.Text = (Environment.TickCount / (1000 * 60)).ToString() + "分钟";
            textBox1.Refresh();
        }
    }
}

关键步骤解析

第1步:构建权限调整流程

在 RebootCommand 方法中,顺序执行以下步骤至关重要:

  1. 获取进程句柄:通过 GetCurrentProcess() 获得当前程序的标识。
  2. 打开通道:调用 OpenProcessToken 并以 TOKEN_ADJUST_PRIVILEGES 模式打开令牌,这是修改权限的前提。
  3. 查询特权值:使用 LookupPrivilegeValue 将字符串 "SeShutdownPrivilege" 转换为系统内部的 LUID(长整型数值)。
  4. 应用特权:调用 AdjustTokenPrivileges 将该特权标记为“已启用”(SE_PRIVILEGE_ENABLED)。如果此步失败,后续的系统调用将被拒绝。

第2步:执行系统指令

最后调用 ExitWindowsEx 传入组合后的标志位。例如,`EWX_SHUTDOWN + EWX_FORCE` 表示强制关机。注意,如果不加 EWX_FORCE,系统会尝试保存用户文档并询问是否继续,这可能不符合自动化脚本的预期行为;但在交互式程序中,通常建议移除强制标志以避免不可逆的数据损坏风险。

第3步:辅助功能集成

示例代码中包含了一个 System.Threading.Timer,用于每秒钟更新界面上的系统运行时间文本框。这展示了如何在执行敏感系统操作的同时,保持界面的动态反馈。通过将计算结果除以1000再除以60,可以将毫秒转换为分钟数显示。

注意事项与排错

  • 管理员权限:虽然代码通过API请求提升了进程内权限,但如果应用程序本身未以管理员身份运行(UAC限制下),OpenProcessToken 可能会返回错误或被拦截。建议在开发调试时确保编译后的EXE具有足够的上下文权限。
  • 返回值检查:在生产环境中,务必检查每个DLL调用的布尔返回值。如果 AdjustTokenPrivileges 返回false,应调用 Marshal.GetLastWin32Error() 来获取具体的错误代码(如ERROR_NOT_ALL_ASSIGNED)。
  • 强制操作的后果:使用 EWX_FORCE 会立即切断所有进程,正在编辑的Word或Excel文档可能未保存。请根据实际应用场景谨慎选择是否包含此标志。

总结

通过上述C#代码,我们可以精确控制Windows系统的电源状态。核心在于理解Windows的安全机制——普通进程无权关机,必须显式申请并激活 SeShutdownPrivilege。这段代码适用于开发系统维护工具、定时任务管理器或远程管理软件。以上就是C#实现关机重启及注销功能的详细内容,更多关于C#系统编程的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表