Cómo apagar la computadora desde C #

138

¿Cuál es la mejor manera de apagar la computadora desde un programa C #?

He encontrado algunos métodos que funcionan, los publicaré a continuación, pero ninguno de ellos es muy elegante. Estoy buscando algo que sea más simple y nativo .net.

roomaroo
fuente

Respuestas:

171

Funciona a partir de Windows XP, no disponible en win 2000 o inferior:

Esta es la forma más rápida de hacerlo:

Process.Start("shutdown","/s /t 0");

De lo contrario, use P / Invoke o WMI como otros han dicho.

Editar: cómo evitar crear una ventana

var psi = new ProcessStartInfo("shutdown","/s /t 0");
psi.CreateNoWindow = true;
psi.UseShellExecute = false;
Process.Start(psi);
Pop Catalin
fuente
2
Esto también parece funcionar desde los servicios (al menos en los escenarios que me preocupan). Nunca pude hacer que los métodos WMI o ExitWindowsEx funcionen desde un servicio.
James
1
@James Es porque un servicio generalmente no tiene los permisos para ello.
AK_
El estado de consumo de energía de la máquina es diferente después de usar esto, que después de usar la ventana de diálogo de apagado tradicional. Al presionar el botón de encendido para volver a arrancar, se requieren unos 80-85 miliamperios, en lugar del estándar 300+ ish. Volveré a publicar aquí si descubro por qué. Esto no debería afectar a la mayoría de los usuarios.
samuelesque
Esto funciona muy bien, excepto el hecho de que si está en WPF, generará una ventana de consola por una fracción de segundo, no exactamente profesional.
Dustin Jensen
79

Tomado de: una publicación de Geekpedia

Este método usa WMI para cerrar ventanas.

Deberá agregar una referencia a System.Management a su proyecto para usar esto.

using System.Management;

void Shutdown()
{
    ManagementBaseObject mboShutdown = null;
    ManagementClass mcWin32 = new ManagementClass("Win32_OperatingSystem");
    mcWin32.Get();

    // You can't shutdown without security privileges
    mcWin32.Scope.Options.EnablePrivileges = true;
    ManagementBaseObject mboShutdownParams =
             mcWin32.GetMethodParameters("Win32Shutdown");

     // Flag 1 means we want to shut down the system. Use "2" to reboot.
    mboShutdownParams["Flags"] = "1";
    mboShutdownParams["Reserved"] = "0";
    foreach (ManagementObject manObj in mcWin32.GetInstances())
    {
        mboShutdown = manObj.InvokeMethod("Win32Shutdown", 
                                       mboShutdownParams, null);
    }
}
roomaroo
fuente
3
El uso de WMI facilita el seguimiento de errores. ¿Qué sucede si el comando de apagado no funciona por alguna razón?
Rob Walker
2
Estoy usando este método para cerrar Windows, y dos de cada tres veces me dirá que no tengo permisos, pero la tercera vez, "cede" y reinicia la computadora de todos modos. ¿Que pasa con eso?
DTI-Matt
1
Esta solución no me funciona. Recibo la excepción "Privilegio no retenido" incluso si ejecuto el programa bajo usuario administrador.
Fanda
@roomaroo Este método no funciona. Lanza una excepción: excepción de gestión, privilegio no retenido.
algo Algo
Si desea cerrar de forma forzada, debe usar mboShutdownParams ["Flags"] = "5"; El valor 5 significa apagado forzado.
SaneDeveloper
32

Este hilo proporciona el código necesario: http://bytes.com/forum/thread251367.html

pero aquí está el código relevante:

using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack=1)]
internal struct TokPriv1Luid
{
    public int Count;
    public long Luid;
    public int Attr;
}

[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 );

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";
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;

private void DoExitWin( int flg )
{
    bool ok;
    TokPriv1Luid tp;
    IntPtr hproc = GetCurrentProcess();
    IntPtr htok = IntPtr.Zero;
    ok = OpenProcessToken( hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok );
    tp.Count = 1;
    tp.Luid = 0;
    tp.Attr = SE_PRIVILEGE_ENABLED;
    ok = LookupPrivilegeValue( null, SE_SHUTDOWN_NAME, ref tp.Luid );
    ok = AdjustTokenPrivileges( htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero );
    ok = ExitWindowsEx( flg, 0 );
    }

Uso:

DoExitWin( EWX_SHUTDOWN );

o

DoExitWin( EWX_REBOOT );
Stephen Wrighton
fuente
Puede leer sobre lo que hacen los otros participantes de EWX_ aquí: msdn.microsoft.com/en-us/library/windows/desktop/…
TripleAntigen
1
Al portar constantes numéricas a C #, la mejor práctica es usar una enumeración. Para eso está diseñado un enum. Proporciona un tipeo fuerte alrededor de las constantes numéricas, opcionalmente admite banderas / máscaras de bits y se convierte fácilmente de un lado a otro en el tipo numérico subyacente.
Andrew Rondeau
26

Diferentes métodos:

A. System.Diagnostics.Process.Start("Shutdown", "-s -t 10");

B. Instrumental de administración de Windows (WMI)

C. System.Runtime.InteropServices Pinvoke

D. Gestión del sistema

Después de enviar, he visto que muchos otros también han publicado ...

lakshmanaraj
fuente
2
B y D son el mismo método (WMI)
Lucas
E. Powershell ejecuta el script del código blogs.msdn.microsoft.com/kebab/2014/04/28/…
user1785960
14

El método feo de la vieja escuela. Utilizar elExitWindowsEx función de la API Win32.

using System.Runtime.InteropServices;

void Shutdown2()
{
    const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege";
    const short SE_PRIVILEGE_ENABLED = 2;
    const uint EWX_SHUTDOWN = 1;
    const short TOKEN_ADJUST_PRIVILEGES = 32;
    const short TOKEN_QUERY = 8;
    IntPtr hToken;
    TOKEN_PRIVILEGES tkp;

    // Get shutdown privileges...
    OpenProcessToken(Process.GetCurrentProcess().Handle, 
          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out hToken);
    tkp.PrivilegeCount = 1;
    tkp.Privileges.Attributes = SE_PRIVILEGE_ENABLED;
    LookupPrivilegeValue("", SE_SHUTDOWN_NAME, out tkp.Privileges.pLuid);
    AdjustTokenPrivileges(hToken, false, ref tkp, 0U, IntPtr.Zero, 
          IntPtr.Zero);

    // Now we have the privileges, shutdown Windows
    ExitWindowsEx(EWX_SHUTDOWN, 0);
}

// Structures needed for the API calls
private struct LUID
{
    public int LowPart;
    public int HighPart;
}
private struct LUID_AND_ATTRIBUTES
{
    public LUID pLuid;
    public int Attributes;
}
private struct TOKEN_PRIVILEGES
{
    public int PrivilegeCount;
    public LUID_AND_ATTRIBUTES Privileges;
}

[DllImport("advapi32.dll")]
static extern int OpenProcessToken(IntPtr ProcessHandle, 
                     int DesiredAccess, out IntPtr TokenHandle);

[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool AdjustTokenPrivileges(IntPtr TokenHandle,
    [MarshalAs(UnmanagedType.Bool)]bool DisableAllPrivileges,
    ref TOKEN_PRIVILEGES NewState,
    UInt32 BufferLength,
    IntPtr PreviousState,
    IntPtr ReturnLength);

[DllImport("advapi32.dll")]
static extern int LookupPrivilegeValue(string lpSystemName, 
                       string lpName, out LUID lpLuid);

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

En el código de producción, debería verificar los valores de retorno de las llamadas a la API, pero lo dejé fuera para aclarar el ejemplo.

roomaroo
fuente
12

Corto y dulce. Llamar a un programa externo:

    using System.Diagnostics;

    void Shutdown()
    {
        Process.Start("shutdown.exe", "-s -t 00");
    }

Nota: Esto llama al programa Shutdown.exe de Windows, por lo que solo funcionará si ese programa está disponible. Es posible que tenga problemas en Windows 2000 (donde shutdown.exe solo está disponible en el kit de recursos) o XP Embedded .

roomaroo
fuente
9
System.Diagnostics.Process.Start("shutdown", "/s /t 0")

Deberia trabajar.

Para reiniciar, es / r

Esto reiniciará la caja de la PC de forma directa y limpia, sin cuadros de diálogo.

Micah Vertal
fuente
Esta es la respuesta perfecta en los sistemas modernos (2015+).
Fattie
gracias, ¿podrías explicar qué hacen / s y / t 0?
Vladimir verleg
1
@Peterverleg Claro. El argumento "/ s" le dice a la computadora que se apague y el "/ t" le dice a la computadora que espere x segundos antes de apagarse. Sé por experiencia personal que el argumento "/ t" no hace nada en Windows 8.1, pero sí funciona en 7 con seguridad. También puede usar estas funciones: shutdown /s /t 0 //For shutdown shutdown /r /t 0 //For restart shutdown /h /t 0 //For hibernateAdemás, intente escribirlas en CMD para obtener el mismo resultado.
Micah Vertal
6

Puede iniciar el proceso de apagado:

  • shutdown -s -t 0 - Apagar
  • shutdown -r -t 0 - Reiniciar
RichS
fuente
6

Tenga en cuenta que shutdown.exees solo una envoltura InitiateSystemShutdownEx, que proporciona algunas sutilezas que faltan enExitWindowsEx

deshacer
fuente
5

Tuve problemas para tratar de usar el método WMI aceptado anteriormente porque siempre recibí excepciones de privilegios a pesar de ejecutar el programa como administrador.

La solución fue que el proceso solicitara el privilegio por sí mismo. Encontré la respuesta en http://www.dotnet247.com/247reference/msgs/58/292150.aspx escrita por un tipo llamado Richard Hill.

He pegado mi uso básico de su solución a continuación en caso de que ese enlace se vuelva viejo.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management;
using System.Runtime.InteropServices;
using System.Security;
using System.Diagnostics;

namespace PowerControl
{
    public class PowerControl_Main
    {


        public void Shutdown()
        {
            ManagementBaseObject mboShutdown = null;
            ManagementClass mcWin32 = new ManagementClass("Win32_OperatingSystem");
            mcWin32.Get();

            if (!TokenAdjuster.EnablePrivilege("SeShutdownPrivilege", true))
            {
                Console.WriteLine("Could not enable SeShutdownPrivilege");
            }
            else
            {
                Console.WriteLine("Enabled SeShutdownPrivilege");
            }

            // You can't shutdown without security privileges
            mcWin32.Scope.Options.EnablePrivileges = true;
            ManagementBaseObject mboShutdownParams = mcWin32.GetMethodParameters("Win32Shutdown");

            // Flag 1 means we want to shut down the system
            mboShutdownParams["Flags"] = "1";
            mboShutdownParams["Reserved"] = "0";

            foreach (ManagementObject manObj in mcWin32.GetInstances())
            {
                try
                {
                    mboShutdown = manObj.InvokeMethod("Win32Shutdown",
                                                   mboShutdownParams, null);
                }
                catch (ManagementException mex)
                {
                    Console.WriteLine(mex.ToString());
                    Console.ReadKey();
                }
            }
        }


    }


    public sealed class TokenAdjuster
    {
        // PInvoke stuff required to set/enable security privileges
        [DllImport("advapi32", SetLastError = true),
        SuppressUnmanagedCodeSecurityAttribute]
        static extern int OpenProcessToken(
        System.IntPtr ProcessHandle, // handle to process
        int DesiredAccess, // desired access to process
        ref IntPtr TokenHandle // handle to open access token
        );

        [DllImport("kernel32", SetLastError = true),
        SuppressUnmanagedCodeSecurityAttribute]
        static extern bool CloseHandle(IntPtr handle);

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

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

        [StructLayout(LayoutKind.Sequential)]
        internal struct LUID
        {
            internal int LowPart;
            internal int HighPart;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct LUID_AND_ATTRIBUTES
        {
            LUID Luid;
            int Attributes;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct _PRIVILEGE_SET
        {
            int PrivilegeCount;
            int Control;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] // ANYSIZE_ARRAY = 1
            LUID_AND_ATTRIBUTES[] Privileges;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct TOKEN_PRIVILEGES
        {
            internal int PrivilegeCount;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
            internal int[] Privileges;
        }
        const int SE_PRIVILEGE_ENABLED = 0x00000002;
        const int TOKEN_ADJUST_PRIVILEGES = 0X00000020;
        const int TOKEN_QUERY = 0X00000008;
        const int TOKEN_ALL_ACCESS = 0X001f01ff;
        const int PROCESS_QUERY_INFORMATION = 0X00000400;

        public static bool EnablePrivilege(string lpszPrivilege, bool
        bEnablePrivilege)
        {
            bool retval = false;
            int ltkpOld = 0;
            IntPtr hToken = IntPtr.Zero;
            TOKEN_PRIVILEGES tkp = new TOKEN_PRIVILEGES();
            tkp.Privileges = new int[3];
            TOKEN_PRIVILEGES tkpOld = new TOKEN_PRIVILEGES();
            tkpOld.Privileges = new int[3];
            LUID tLUID = new LUID();
            tkp.PrivilegeCount = 1;
            if (bEnablePrivilege)
                tkp.Privileges[2] = SE_PRIVILEGE_ENABLED;
            else
                tkp.Privileges[2] = 0;
            if (LookupPrivilegeValue(null, lpszPrivilege, ref tLUID))
            {
                Process proc = Process.GetCurrentProcess();
                if (proc.Handle != IntPtr.Zero)
                {
                    if (OpenProcessToken(proc.Handle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                    ref hToken) != 0)
                    {
                        tkp.PrivilegeCount = 1;
                        tkp.Privileges[2] = SE_PRIVILEGE_ENABLED;
                        tkp.Privileges[1] = tLUID.HighPart;
                        tkp.Privileges[0] = tLUID.LowPart;
                        const int bufLength = 256;
                        IntPtr tu = Marshal.AllocHGlobal(bufLength);
                        Marshal.StructureToPtr(tkp, tu, true);
                        if (AdjustTokenPrivileges(hToken, 0, tu, bufLength, IntPtr.Zero, ref ltkpOld) != 0)
                        {
                            // successful AdjustTokenPrivileges doesn't mean privilege could be changed
                            if (Marshal.GetLastWin32Error() == 0)
                            {
                                retval = true; // Token changed
                            }
                        }
                        TOKEN_PRIVILEGES tokp = (TOKEN_PRIVILEGES)Marshal.PtrToStructure(tu,
                        typeof(TOKEN_PRIVILEGES));
                        Marshal.FreeHGlobal(tu);
                    }
                }
            }
            if (hToken != IntPtr.Zero)
            {
                CloseHandle(hToken);
            }
            return retval;
        }

    }
}
m3z
fuente
2
Esto funcionó, aunque no me gusta no saber por qué. Sinceramente, me pregunto si debería haber seguido el comando de "apagado" ...
Dan Bailiff
5

Solo para agregar a la respuesta de Pop Catalin, aquí hay un revestimiento que apaga la computadora sin mostrar ninguna ventana:

Process.Start(new ProcessStartInfo("shutdown", "/s /t 0") {
  CreateNoWindow = true, UseShellExecute = false
});
hombre
fuente
2

Intenté el método WMI de roomaroo para cerrar Windows 2003 Server, pero no funcionaría hasta que agregue `[STAThread] '(es decir, el modelo de subprocesos " Single Threaded Apartment ") a la declaración Main ():

[STAThread]
public static void Main(string[] args) {
    Shutdown();
}

Luego intenté cerrar desde un subproceso, y para que eso funcione, tuve que establecer el "Estado del apartamento" del subproceso en STA también:

using System.Management;
using System.Threading;

public static class Program {

    [STAThread]
    public static void Main(string[] args) {
        Thread t = new Thread(new ThreadStart(Program.Shutdown));
        t.SetApartmentState(ApartmentState.STA);
        t.Start();
        ...
    }

    public static void Shutdown() {
        // roomaroo's code
    }
}

Soy un novato de C #, por lo que no estoy completamente seguro de la importancia de los subprocesos de STA en términos de apagar el sistema (incluso después de leer el enlace que publiqué anteriormente). Quizás alguien más pueda elaborar ...?

MisterEd
fuente
En realidad, solo el hilo que llama a WMI debe ser hilo STA. Si ese no es el hilo principal, Main()no es necesario [STAThread].
SLaks el
2

** Respuesta elaborada ...

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
// Remember to add a reference to the System.Management assembly
using System.Management;
using System.Diagnostics;

namespace ShutDown
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnShutDown_Click(object sender, EventArgs e)
        {
            ManagementBaseObject mboShutdown = null;
            ManagementClass mcWin32 = new ManagementClass("Win32_OperatingSystem");
            mcWin32.Get();

            // You can't shutdown without security privileges
            mcWin32.Scope.Options.EnablePrivileges = true;
            ManagementBaseObject mboShutdownParams = mcWin32.GetMethodParameters("Win32Shutdown");

            // Flag 1 means we want to shut down the system
            mboShutdownParams["Flags"] = "1";
            mboShutdownParams["Reserved"] = "0";

            foreach (ManagementObject manObj in mcWin32.GetInstances())
            {
                mboShutdown = manObj.InvokeMethod("Win32Shutdown", mboShutdownParams, null);
            }
        }
    }
}
Fazil Mir
fuente
1

Utilice shutdown.exe. Para evitar problemas al pasar argumentos, ejecución compleja, ejecución desde WindowForms, use el script de ejecución de PowerShell:

using System.Management.Automation;
...
using (PowerShell PowerShellInstance = PowerShell.Create())
{
    PowerShellInstance.AddScript("shutdown -a; shutdown -r -t 100;");
    // invoke execution on the pipeline (collecting output)
    Collection<PSObject> PSOutput = PowerShellInstance.Invoke();
} 

System.Management.Automation.dll debe instalarse en el sistema operativo y estar disponible en GAC.

Lo siento por mi ingles.

usuario1785960
fuente
0

No existe un método nativo .net para apagar la computadora. Debe P / Invocar la llamada API ExitWindows o ExitWindowsEx.

Sí, ese Jake.
fuente
0

Si desea apagar la computadora de forma remota, puede usar

Using System.Diagnostics;

en cualquier botón haga clic

{
    Process.Start("Shutdown","-i");
}
Jason Plank
fuente