Comment arrêter l'ordinateur à partir de C #

138

Quelle est la meilleure façon d'arrêter l'ordinateur à partir d'un programme C #?

J'ai trouvé quelques méthodes qui fonctionnent - je les posterai ci-dessous - mais aucune d'entre elles n'est très élégante. Je recherche quelque chose de plus simple et nativement .net.

salle de bain
la source

Réponses:

171

Fonctionne à partir de Windows XP, non disponible dans win 2000 ou version antérieure:

C'est le moyen le plus rapide de le faire:

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

Sinon, utilisez P / Invoke ou WMI comme d'autres l'ont dit.

Modifier: comment éviter de créer une fenêtre

var psi = new ProcessStartInfo("shutdown","/s /t 0");
psi.CreateNoWindow = true;
psi.UseShellExecute = false;
Process.Start(psi);
Pop Catalin
la source
2
Cela semble fonctionner également à partir des services (du moins dans les scénarios qui me concernent). Je n'ai jamais pu faire fonctionner les méthodes WMI ou ExitWindowsEx à partir d'un service.
James
1
@James C'est parce qu'un service n'a généralement pas les permissions pour cela.
AK_
L'état de consommation électrique de la machine est différent après utilisation, qu'après utilisation de la fenêtre de dialogue d'arrêt traditionnelle. Appuyer sur le bouton d'alimentation pour redémarrer consomme environ 80 à 85 milliampères, au lieu du 300 + ish standard. Je publierai ici si je découvre pourquoi. Cela ne devrait pas affecter la plupart des utilisateurs.
samuelesque
Cela fonctionne très bien, sauf le fait que si vous êtes dans WPF, cela créera une fenêtre de console pendant une fraction de seconde, pas exactement d'aspect professionnel.
Dustin Jensen
80

Tiré de: un article de Geekpedia

Cette méthode utilise WMI pour fermer les fenêtres.

Vous devrez ajouter une référence à System.Management à votre projet pour l'utiliser.

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);
    }
}
salle de bain
la source
3
L'utilisation de WMI facilite le suivi des erreurs. Que se passe-t-il si la commande d'arrêt ne fonctionne pas pour une raison quelconque?
Rob Walker
2
J'utilise cette méthode pour fermer les fenêtres, et deux fois sur trois, cela me dira que je n'ai pas les autorisations, mais la troisième fois, cela «abandonne» et redémarre quand même l'ordinateur. Qu'est-ce qui se passe avec ça?
DTI-Matt
1
Cette solution ne fonctionne pas pour moi. J'obtiens l'exception "Privilège non tenu" même si j'exécute le programme sous l'utilisateur administrateur.
Fanda
@roomaroo Cette méthode ne fonctionne pas. Il lève une exception: exception de gestion, privilège non détenu.
quelque chose de
Si vous souhaitez arrêter de force, vous devez utiliser mboShutdownParams ["Flags"] = "5"; La valeur 5 signifie un arrêt forcé.
SaneDeveloper
32

Ce fil fournit le code nécessaire: http://bytes.com/forum/thread251367.html

mais voici le code pertinent:

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

Usage:

DoExitWin( EWX_SHUTDOWN );

ou

DoExitWin( EWX_REBOOT );
Stephen Wrighton
la source
Vous pouvez en savoir plus sur ce que font les autres candidats EWX_ ici: msdn.microsoft.com/en-us/library/windows/desktop/…
TripleAntigen
1
Lors du portage de constantes numériques vers C #, la meilleure pratique consiste à utiliser une énumération. C'est ce à quoi une énumération est conçue. Il donne un typage fort autour des constantes numériques, prend éventuellement en charge les drapeaux / masques de bits et effectue facilement un basculement vers le type numérique sous-jacent.
Andrew Rondeau
26

Différentes méthodes:

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

B. Instrumentation de gestion Windows (WMI)

C. System.Runtime.InteropServices Pinvoke

D. Gestion du système

Après avoir soumis, j'en ai vu tellement d'autres ont également posté ...

lakshmanaraj
la source
2
B et D sont la même méthode (WMI)
Lucas
E. Powershell exécute le script à partir du code blogs.msdn.microsoft.com/kebab/2014/04/28/…
user1785960
14

La méthode laide à l'ancienne. Utilisez la ExitWindowsExfonction de l'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);

Dans le code de production, vous devriez vérifier les valeurs de retour des appels d'API, mais j'ai laissé cela de côté pour rendre l'exemple plus clair.

salle de bain
la source
12

Court et doux. Appeler un programme externe:

    using System.Diagnostics;

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

Remarque: cela appelle le programme Shutdown.exe de Windows, il ne fonctionnera donc que si ce programme est disponible. Vous pouvez rencontrer des problèmes sous Windows 2000 (où shutdown.exe n'est disponible que dans le kit de ressources) ou XP Embedded .

salle de bain
la source
9
System.Diagnostics.Process.Start("shutdown", "/s /t 0")

Devrait marcher.

Pour redémarrer, c'est / r

Cela redémarrera la boîte PC directement et proprement, sans aucune boîte de dialogue.

Micah Vertal
la source
C'est la réponse parfaite sur les systèmes modernes (2015+).
Fattie
merci, pouvez-vous expliquer ce que font le / s et le / t 0?
Vladimir verleg
1
@Peterverleg Bien sûr. L'argument "/ s" indique à l'ordinateur de s'arrêter et le "/ t" indique à l'ordinateur d'attendre x secondes avant de s'éteindre. Je sais par expérience personnelle que l'argument "/ t" ne fait rien dans Windows 8.1, mais il fonctionne dans 7 à coup sûr. Vous pouvez également utiliser ces fonctions: shutdown /s /t 0 //For shutdown shutdown /r /t 0 //For restart shutdown /h /t 0 //For hibernateEssayez également de les saisir dans CMD pour obtenir le même résultat.
Micah Vertal
6

Vous pouvez lancer le processus d'arrêt:

  • shutdown -s -t 0 - Fermer
  • shutdown -r -t 0 - Redémarrer
RichS
la source
5

J'ai eu du mal à essayer d'utiliser la méthode WMI acceptée ci-dessus parce que j'ai toujours eu des exceptions de privilège non détenues malgré l'exécution du programme en tant qu'administrateur.

La solution était que le processus demande le privilège pour lui-même. J'ai trouvé la réponse sur http://www.dotnet247.com/247reference/msgs/58/292150.aspx écrite par un type appelé Richard Hill.

J'ai collé mon utilisation de base de sa solution ci-dessous au cas où ce lien vieillirait.

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
la source
2
Cela a fonctionné, même si je n'aime pas savoir pourquoi. Je me demande honnêtement si j'aurais dû simplement utiliser la commande "shutdown" ...
Dan Bailiff
5

Juste pour ajouter à la réponse de Pop Catalin, voici une ligne unique qui arrête l'ordinateur sans afficher de fenêtre:

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

J'ai essayé la méthode WMI de roomaroo pour arrêter Windows 2003 Server, mais cela ne fonctionnait pas tant que j'aurais ajouté `[STAThread] '(c'est-à-dire le modèle de thread " Single Threaded Apartment ") à la déclaration Main ():

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

J'ai ensuite essayé de m'arrêter à partir d'un thread, et pour que cela fonctionne, je devais également définir "l'état d'appartement" du thread sur STA:

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
    }
}

Je suis un C # noob, donc je ne suis pas entièrement sûr de l'importance des threads STA en termes d'arrêt du système (même après avoir lu le lien que j'ai posté ci-dessus). Peut-être que quelqu'un d'autre peut élaborer ...?

MonsieurEd
la source
En fait, seul le thread qui appelle WMI doit être un thread STA. Si ce n'est pas le fil principal, Main()n'a pas besoin [STAThread].
SLaks
2

** Réponse élaborée ...

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
la source
1

Utilisez shutdown.exe. Pour éviter les problèmes de passage d'arguments, d'exécution complexe, d'exécution à partir de WindowForms, utilisez le script d'exécution 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 doit être installé sur le système d'exploitation et disponible dans GAC.

Désolé pour mon anglais.

user1785960
la source
0

Il n'y a pas de méthode native .net pour éteindre l'ordinateur. Vous devez P / Invoke l'appel d'API ExitWindows ou ExitWindowsEx.

Oui - ce Jake.
la source
0

Si vous souhaitez éteindre l'ordinateur à distance, vous pouvez utiliser

Using System.Diagnostics;

sur n'importe quel bouton, cliquez

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