Jak wyłączyć komputer z poziomu C #

138

Jaki jest najlepszy sposób na wyłączenie komputera z poziomu programu C #?

Znalazłem kilka metod, które działają - zamieszczę je poniżej - ale żadna z nich nie jest zbyt elegancka. Szukam czegoś, co jest prostsze i natywnie .net.

roomaroo
źródło

Odpowiedzi:

171

Działa począwszy od systemu Windows XP, niedostępne w wersji 2000 lub niższej:

To najszybszy sposób:

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

W przeciwnym razie użyj P / Invoke lub WMI, jak powiedzieli inni.

Edycja: jak uniknąć tworzenia okna

var psi = new ProcessStartInfo("shutdown","/s /t 0");
psi.CreateNoWindow = true;
psi.UseShellExecute = false;
Process.Start(psi);
Pop Catalin
źródło
2
Wydaje się, że działa to również w przypadku usług (przynajmniej w scenariuszach, które mnie interesują). Nigdy nie udało mi się uzyskać metod WMI lub ExitWindowsEx z usługi.
James
1
@James Dzieje się tak, ponieważ usługa zwykle nie ma do niej uprawnień.
AK_
Stan zużycia energii przez maszynę jest inny po użyciu tego niż po użyciu tradycyjnego okna dialogowego zamykania. Naciśnięcie przycisku zasilania, aby uruchomić kopię zapasową, pobiera około 80-85 miliamperów, zamiast standardowych 300 + ish. Wrócę tutaj, jeśli dowiem się, dlaczego. Nie powinno to mieć wpływu na większość użytkowników.
samuelesque
Działa to świetnie, z wyjątkiem faktu, że jeśli jesteś w WPF, spowoduje to pojawienie się okna konsoli na ułamek sekundy, nie do końca profesjonalnie wyglądającego.
Dustin Jensen
79

Zaczerpnięte z: post Geekpedia

Ta metoda wykorzystuje WMI do zamykania okien.

Aby z tego skorzystać, musisz dodać odwołanie do System.Management do projektu.

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
źródło
3
Korzystanie z WMI ułatwia śledzenie błędów. Co się stanie, jeśli polecenie zamknięcia systemu z jakiegoś powodu nie zadziała?
Rob Walker,
2
Używam tej metody do zamykania okien i dwa na trzy razy to mi powie, że nie mam uprawnień, ale za trzecim razem trochę się „poddaje” i i tak uruchamia ponownie komputer. O co w tym chodzi?
DTI-Matt
1
To rozwiązanie u mnie nie działa. Otrzymuję wyjątek „Brak uprawnień”, nawet jeśli uruchamiam program jako administrator.
Fanda
@roomaroo Ta metoda nie działa. Zgłasza wyjątek: wyjątek zarządzania, brak uprawnień.
coś Coś
Jeśli chcesz wymusić zamknięcie systemu, powinieneś użyć mboShutdownParams ["Flags"] = "5"; Wartość 5 oznacza wymuszone wyłączenie.
SaneDeveloper
32

Ten wątek zawiera niezbędny kod: http://bytes.com/forum/thread251367.html

ale oto odpowiedni kod:

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

Stosowanie:

DoExitWin( EWX_SHUTDOWN );

lub

DoExitWin( EWX_REBOOT );
Stephen Wrighton
źródło
Możesz przeczytać o tym, co robią inne EWX_ contstants tutaj: msdn.microsoft.com/en-us/library/windows/desktop/…
TripleAntigen
1
Podczas przenoszenia stałych numerycznych do języka C # najlepszą praktyką jest użycie wyliczenia. Do tego właśnie służy enum. Daje silne możliwości pisania wokół stałych numerycznych, opcjonalnie obsługuje flagi / maski bitowe i łatwo rzutuje tam iz powrotem do bazowego typu liczbowego.
Andrew Rondeau
26

Różne metody:

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

B. Instrumentacja zarządzania Windows (WMI)

C. System.Runtime.InteropServices Pinvoke

D. Zarządzanie systemem

Po przesłaniu, widziałem, że wielu innych również opublikowało ...

lakshmanaraj
źródło
2
B i D to ta sama metoda (WMI)
Lucas
E. Powershell wykonuje skrypt z kodu blogs.msdn.microsoft.com/kebab/2014/04/28/ ...
user1785960
14

Stara brzydka metoda. Użyj ExitWindowsExfunkcji z interfejsu 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);

W kodzie produkcyjnym powinieneś sprawdzać zwracane wartości wywołań API, ale pominąłem to, aby przykład był jaśniejszy.

roomaroo
źródło
12

Krótkie i słodkie. Zadzwoń do programu zewnętrznego:

    using System.Diagnostics;

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

Uwaga: wywołuje to program Shutdown.exe systemu Windows, więc będzie działać tylko wtedy, gdy ten program jest dostępny. Mogą wystąpić problemy w systemie Windows 2000 (gdzie shutdown.exe jest dostępny tylko w zestawie zasobów) lub w systemie XP Embedded .

roomaroo
źródło
9
System.Diagnostics.Process.Start("shutdown", "/s /t 0")

Powinno działać.

W przypadku ponownego uruchomienia to / r

Spowoduje to bezpośrednie i czyste ponowne uruchomienie komputera bez okien dialogowych.

Micah Vertal
źródło
To doskonała odpowiedź na nowoczesne systemy (2015+).
Fattie
dzięki, czy mógłbyś wyjaśnić, co robią / s i / t 0?
Vladimir Verleg
1
@Peterverleg Jasne. Argument „/ s” mówi komputerowi, aby się wyłączył, a „/ t” mówi komputerowi, aby odczekał x sekund przed wyłączeniem. Z własnego doświadczenia wiem, że argument „/ t” nic nie robi w Windows 8.1, ale na pewno działa w 7. Możesz również użyć tych funkcji: shutdown /s /t 0 //For shutdown shutdown /r /t 0 //For restart shutdown /h /t 0 //For hibernateSpróbuj także wpisać je do CMD, aby uzyskać ten sam wynik.
Micah Vertal
6

Możesz uruchomić proces zamykania:

  • shutdown -s -t 0 - Zamknąć
  • shutdown -r -t 0 - Uruchom ponownie
RichS
źródło
5

Miałem problem z użyciem metody WMI zaakceptowanej powyżej, ponieważ zawsze miałem uprawnienia nie posiadane wyjątki pomimo uruchamiania programu jako administrator.

Rozwiązanie polegało na tym, że proces zażądał uprawnienia dla siebie. Odpowiedź znalazłem pod adresem http://www.dotnet247.com/247reference/msgs/58/292150.aspx, napisaną przez gościa imieniem Richard Hill.

Poniżej wkleiłem moje podstawowe użycie jego rozwiązania na wypadek, gdyby ten link się zestarzał.

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
źródło
2
To zadziałało, chociaż nie lubię nie wiedzieć dlaczego. Szczerze się zastanawiam, czy nie powinienem był po prostu wyjść z poleceniem „zamknij” ...
Dan Bailiff,
5

Aby dodać do odpowiedzi Pop Catalina, oto jedna linijka, która wyłącza komputer bez wyświetlania żadnych okien:

Process.Start(new ProcessStartInfo("shutdown", "/s /t 0") {
  CreateNoWindow = true, UseShellExecute = false
});
człowiek
źródło
2

Wypróbowałem metodę WMI roomaroo, aby zamknąć Windows 2003 Server, ale nie zadziała, dopóki nie dodam `[STAThread] '(tj. Model wątków " Single Threaded Apartment ") do deklaracji Main ():

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

Następnie próbowałem zamknąć system z wątku i aby to zadziałało, musiałem również ustawić „Stan mieszkania” wątku na 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
    }
}

Jestem noobem C #, więc nie jestem do końca pewien, jakie znaczenie mają wątki STA w kontekście zamykania systemu (nawet po przeczytaniu linku, który zamieściłem powyżej). Może ktoś inny rozwiąże…?

MisterEd
źródło
W rzeczywistości tylko wątek, który wywołuje WMI, musi być wątkiem STA. Jeśli to nie jest główny wątek, Main()nie trzeba [STAThread].
Slaks
2

** Rozszerzona odpowiedź ...

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
źródło
1

Użyj shutdown.exe. Aby uniknąć problemu z przekazywaniem argumentów, złożonym wykonywaniem, wykonywaniem z WindowForms użyj skryptu wykonywania 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 powinien być zainstalowany w systemie operacyjnym i dostępny w GAC.

Przepraszam za mój angielski.

user1785960
źródło
0

Nie ma macierzystej metody .net do wyłączania komputera. Musisz P / Wywołać wywołanie API ExitWindows lub ExitWindowsEx.

Tak - ten Jake.
źródło
0

Jeśli chcesz zdalnie wyłączyć komputer, możesz użyć

Using System.Diagnostics;

po kliknięciu dowolnego przycisku

{
    Process.Start("Shutdown","-i");
}
Jason Plank
źródło