eaglet

本博专注于基于微软技术的搜索相关技术
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

C# 重启计算机的问题

Posted on 2012-04-13 13:54  eaglet  阅读(13075)  评论(11编辑  收藏  举报

C# 程序重启计算机的方法很多,网上也有不少这方面的文章,不过很多网上提供的方法在某些情况下无法获取重启计算机的权限导致重启失败。本文对这些方法做一些简单的讨论。

作者:eaglet

 

网上介绍最多的两种方法分别是:

System.Diagnostics.Process.Start("shutdown",@"/r");

    [DllImport("user32.dll")]
        static extern bool ExitWindowsEx(ExitWindows uFlags, ShutdownReason dwReason);

        [STAThread]
        static void Main(string[] args)
        {
            ExitWindowsEx(ExitWindows.LogOff, ShutdownReason.MajorOther & ShutdownReason.MinorOther);
            //这个语句将实现计算机注销操作   
         }

 

这两种方法在通常情况下工作是没有问题的,但在某些特殊情况下,比如桌面被其它用户锁定时就无法重启计算机。本人在实际工作中遇到过当当前屏幕被远程控制软件锁定后,我做的后台守护进程试图重启计算机,结果用上述两种方法都无法成功。分析原因,应该是远程控制软件用另外的帐号锁定了屏幕(通常应该是windows service 或者 network service),这时守护进程用当前帐号重启计算机就因为没有权限而失败。

要解决这个问题,我们必须要给进程赋予足够的权限才行,于是我在调用 ExitWindowsEx 前运行了如下代码来赋予当前进程关闭计算机权限

 

 
        //give current process SeShutdownPrivilege
        TokPriv1Luid tp;
 
        IntPtr hproc = GetCurrentProcess();
 
        IntPtr htok = IntPtr.Zero;
 
        if (!OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok))
        {
            throw new Exception("Open Process Token fail");
        }
 
        tp.Count = 1;
 
        tp.Luid = 0;
 
        tp.Attr = SE_PRIVILEGE_ENABLED;
 
        if (!LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref tp.Luid))
        {
            throw new Exception("Lookup Privilege Value fail");
        }
 
        if (!AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero))
        {
            throw new Exception("Adjust Token Privileges fail");
        }

上面代码为当前进程赋予了关闭计算机的权限。这里需要注意的是上述代码要执行成功同样需要足够的权限,通常当前进程需要以拥有至少是系统管理员权限的账户运行。如果没有足够权限,需要用程序模拟系统管理员权限,模拟其它帐号权限的问题不在本文讨论范围内。

加上如上代码后,在其他用户锁定机器后,重启计算机成功。

下面给出完整代码

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
 
public class ExitWindows
{
    #region win32 api
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
 
    private struct TokPriv1Luid
    {
 
        public int Count;
 
        public long Luid;
 
        public int Attr;
 
    }
 
    [DllImport("kernel32.dll", ExactSpelling = true)]
    private static extern IntPtr GetCurrentProcess();
 
    [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
    private static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
 
    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
 
    [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
    private static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall,
        ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
 
    [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
    private static extern bool ExitWindowsEx(int flg, int rea);
 
    #endregion
 
    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";
 
    #region Exit Windows Flags
    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 const int EWX_POWEROFF = 0x00000008;
 
    private const int EWX_FORCEIFHUNG = 0x00000010;
 
    #endregion
    
    public static void DoExitWin(int flg)
    {
 
        //give current process SeShutdownPrivilege
        TokPriv1Luid tp;
 
        IntPtr hproc = GetCurrentProcess();
 
        IntPtr htok = IntPtr.Zero;
 
        if (!OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok))
        {
            throw new Exception("Open Process Token fail");
        }
 
        tp.Count = 1;
 
        tp.Luid = 0;
 
        tp.Attr = SE_PRIVILEGE_ENABLED;
 
        if (!LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, ref tp.Luid))
        {
            throw new Exception("Lookup Privilege Value fail");
        }
 
        if (!AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero))
        {
            throw new Exception("Adjust Token Privileges fail");
        }
 
        //Exit windows
        if (!ExitWindowsEx(flg, 0))
        {
            throw new Exception("Exit Windows fail");
        }
    }
 
    /// <summary>
    /// Reboot computer
    /// </summary>
    /// <param name="force">force reboot</param>
    public static void Reboot(bool force)
    {
        if (force)
        {
            DoExitWin(EWX_REBOOT | EWX_FORCE);
        }
        else
        {
            DoExitWin(EWX_REBOOT | EWX_FORCEIFHUNG);
        }
    }
 
    /// <summary>
    /// Reboot computer force if hung
    /// </summary>
    public static void Reboot()
    {
        Reboot(false);
    }
 
    /// <summary>
    /// Shut down computer
    /// </summary>
    /// <param name="force">force shut down</param>
    public static void Shutdown(bool force)
    {
        if (force)
        {
            DoExitWin(EWX_SHUTDOWN | EWX_FORCE);
        }
        else
        {
            DoExitWin(EWX_SHUTDOWN | EWX_FORCEIFHUNG);
        }
    }
 
    /// <summary>
    /// Shut down computer force if hung
    /// </summary>
    public static void Shutdown()
    {
        Shutdown(false);
    }
 
    /// <summary>
    /// Log off
    /// </summary>
    /// <param name="force">force logoff</param>
    public static void Logoff(bool force)
    {
        if (force)
        {
            DoExitWin(EWX_LOGOFF | EWX_FORCE);
        }
        else
        {
            DoExitWin(EWX_LOGOFF | EWX_FORCEIFHUNG);
        }
    }
 
    /// <summary>
    /// logoff computer force if hung
    /// </summary>
    public static void Logoff()
    {
        Logoff(false);
    }
}