Zamykanie systemu w Delphi

michalkopacz
<style>p {text-align: justify; text-indent: 15px; margin: 2px}</style>

Chociaż tytuł artykułu sugeruje, że mowa będzie jedynie o zamykaniu systemu, to tak naprawdę opiszę tu również sposoby restartowania, hibernacji systemu, przejścia w stan wstrzymania oraz wylogowywania użytkownika.

Aby zamknąć, zrestartować system lub wylogować użytkownika trzeba użyć funkcji WinAPI ExitWindowsEx. Jednak poprawne wywołanie tej funkcji pod Windows NT nie jest takie proste, gdyż systemem musi jeszcze martwić się o prawa do tej operacji. Zanim jednak przedstawię sposób zamykania systemu (restartowania i wylogowywania) w systemie z linii NT, opiszę najpierw funkcję ExitWindowsEx.

Funkcja ExitWindowsEx ma następującą postać:

function ExitWindowsEx(wFlags: Cardinal; dwReserved: Cardinal): Boolean;

Poniżej opisuję poszczególne parametry tej funkcji:

wFlags
Określa sposób przeładowania (zamknięcia itp.) systemu. Parametr ten może być kombinacją flagi EWX_FORCE oraz jednej z pozostałych flag.

EWX_FORCE
Wymusza zamknięcie wszystkich procesów. Jeżeli ta flaga jest ustawiona, system nie wysyła komunikatów WM_QUERYENDSESSION i WM_ENDSESSION do aktualnie uruchomionych aplikacji, czyli nie pyta ich o ewentualne zapisanie danych. Dlatego powinieneś używać tej flagi w nagłych wypadkach.
EWX_FORCEIFHUNG
Wymusza zamknięcie wszystkich procesów jeśli nie odpowiadają one na komunikaty WM_QUERYENDSESSION i WM_ENDSESSION. Flaga ta jest ignorowana, jeśli zostanie użyta flaga EWX_FORCE.
EWX_LOGOFF
Zamyka wszystkie uruchomione aplikacje i wylogowywuje użytkownika.
EWX_POWEROFF
Zamyka system i wyłącza komputer.
EWX_REBOOT
Zamyka a następnie restartuje system.
EWX_SHUTDOWN
Zamyka system do momentu kiedy będzie można bezpiecznie wyłączyć komputer, wszystkie tymczasowe pliki (bufory, pliki wymiany) zostają wyczyszczone (ewentualnie usunięte) a uruchomione procesy zakończone.

Aby połączyć flagi używamy operatora or (lub +). Na przykład:

ExitWindowsEx(EWX_FORCE or EWX_REBOOT, 0);

dwReserved
Parametr ten jest ignorowany (należy przypisać mu wartość zero).

Tak jak wspomniałem, w Windows z linii NT potrzebujemy przejąć prawa umożliwiające nam zamknięcie systemu. Działająca pod obydwoma systemami funkcja wygląda następująco:

Wydruk 1. Funkcja DelphiExitsWindows.

function DelphiExitsWindows(Flags: Word): Boolean;
var
  iVersionInfo : TOSVersionInfo;
  iToken    : THandle;
  iPriveleg : TTokenPrivileges;
  iaresult  : DWord;
begin
  Result:=False;
  FillChar (iPriveleg, SizeOf (iPriveleg), #0);
  iVersionInfo.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
  GetVersionEx(iVersionInfo);
  if iVersionInfo.dwPlatformId <> VER_PLATFORM_WIN32_NT then
    Result:=ExitWindowsEx(Flags, 0)
  else
    if OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES 
       or TOKEN_QUERY, iToken) then
      if LookupPrivilegeValue (NIL,'SeShutdownPrivilege',  
         iPriveleg.Privileges[0].Luid) then 
      begin
        iPriveleg.PrivilegeCount := 1;
        iPriveleg.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
        if AdjustTokenPrivileges (iToken, False, iPriveleg,  
           Sizeof(iPriveleg), iPriveleg, iaresult) then
          Result:=ExitWindowsEx (Flags, 0);
      end;
end;

Funkcja DelphiExitsWindows jako parametr przyjmuje flagi funkcji ExitWindowsEx. W zależności od systemu operacyjnego, funkcja od razu wywołuje funkcję ExitWindowsEx, jeśli był to system z linii 9x, lub też przejmuje prawa (ang. privileges ? przywileje) jeśli był to system z linii NT. Powierzchowny opis funkcji zamieszczam poniżej:

W liniach od 3 do 6 deklarujemy potrzebne nam zmienne. I tak, zmienna iVersionInfo to struktura zawierająca informacje o systemie. Dalej deklarujemy zmienną iToken będącą uchwytem do identyfikatora praw. Następna zmienna to struktura zawierająca zestaw praw. W linii 9 i 10 inicjujemy strukturę danych. Następnie, za pomocą funkcji GetVersionEx pobieramy informację o systemie (konkretnie, czy jest to Windows 9x czy też Windows NT). Instrukcja warunkowa w linii 12 sprawdza, czy system nie pochodzi z linii NT, jeśli tak jest możemy przeładować system. Jeśli jednak jest to system z linii NT, musimy przejąć najpierw prawa umożliwiające dokonanie operacji przeładowania systemu. W linii 15 otwieramy identyfikator praw związany z procesem. Jeśli operacja się powiedzie, pobieramy identyfikator Luid reprezentujący prawo SeShutdownPrivilege (linia 17). Jeśli udało nam się pobrać identyfikator, ustawiamy flagę, dzięki której prawo reprezentowane przez identyfikator Luid zostaje uaktywnione (linie 20 i 21). Na koniec próbujemy nadać prawo umożliwiające przeładowanie sytemu. Jeśli nam się uda, zostanie wywołana funkcja ExitWindowsEx.

Poniżej zamieszczam przykład wywołania funkcji DelphiExitsWindows (w tym przypadku próbujemy zamknąć system, z jednoczesnym wymuszeniem zamknięcia wszystkich procesów).

DelphiExitsWindows(EWX_FORCE or EWX_POWEROFF);

Wspomniałem również, że istnieje możliwość przejścia w stan wstrzymania lub hibernacji systemu. Umożliwia nam to funkcja WinApi: SetSystemPowerState. Ma ona następującą postać:

function SetSystemPowerState(fSuspend: LongBool; fForce: LongBool);

fSuspend
Parametr ten określa sposób usypiana systemu. Jeśli parametr ma wartość True, system przechodzi w stan wstrzymania. W przeciwnym wypadku, gdy parametr ma wartość False, następuje hibernacja systemu.

fForce

Operator określa, czy system ma zostać uśpiony natychmiast po wywołaniu funkcji SetSystemPowerState (wartość True), czy też ma wysłać do każdego procesu prośbę o możliwość uśpienia systemu.

Win32Check(SetSystemPowerState(True, False));

Powyżej przedstawiony jest sposób wywołania opisanej funkcji. Zauważ, że użyłem tu funkcji Win32Check (moduł SysUtils). Funkcja ta sprawdza, jaką wartość (typu Boolean) zwróciła przekazana w parametrze funkcja WindowsApi i w przypadku, gdy zwrócona zostaje wartość False, wywołuje ona procedurę RaiseLastWin32Error w celu wygenerowania wyjątku.

W przypadku próby uśpienia systemu WindowsNT, musimy również przejąć odpowiednie prawa. Procedura, która umożliwia nam wykonanie takiej czynności, została zamieszczona na poniższym wydruku. Działa ona zarówno w systemie z linii 9x, jak i NT.

Wydruk 2. Procedura SuspendOrHibernate

{$WARN SYMBOL_PLATFORM OFF}
procedure SuspendOrHibernate(ASuspend, AForce: Boolean);
var
  iVersionInfo : TOSVersionInfo;
  iToken    : Cardinal;
  iPriveleg : TTokenPrivileges;
  iaResult  : Cardinal;
begin
  FillChar (iPriveleg, SizeOf (iPriveleg), #0);
  iVersionInfo.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
   GetVersionEx(iVersionInfo);

  if iVersionInfo.dwPlatformId = VER_PLATFORM_WIN32_NT then 
  begin
    Win32Check(OpenProcessToken (GetCurrentProcess,
               TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, iToken));
    Win32Check(LookupPrivilegeValue (NIL, 'SeShutdownPrivilege',  
               iPriveleg.Privileges[0].Luid));

    iPriveleg.PrivilegeCount := 1;
    iPriveleg.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;

    Win32Check(AdjustTokenPrivileges(iToken, False, iPriveleg,
    Sizeof(iPriveleg), iPriveleg, iaResult));
  end;
     Win32Check(SetSystemPowerState(ASuspend, AForce));
end;

17 komentarzy

Niezły artykuł. A jak zamknąć windows vista/7?

Super artykuł, przyda mi się

fajny tekst :-)

Tego było mi trzeba :-)))

do: michalkopacz
hmm zainstalowałem nowszą wersje delphi i działa ale niedokońca. jak włańczam program(EXE) to wyskakuje Form 1 a nic nierobi??
?
?
co jest?

Jest ok ale jak biore run to kupa. Hmm kursor ten do pisania spada mi na dól o jedną linie co jest?? moze ktoś by mi wysłał projekt ten na E-mail ??
Prosze o pomoc

Hmm robie to na innym kompie do którego tymczsowo niemam dostempu i Ci niepowiem pisze to na 98 i czy to ma jakieś znaczenie?? a jak bede mógł to Ci napisze.

do physikus: Jaka jest treść tego błędu? U mnie jest wszystko ok, więc nie wiem jak Ci pomóc, bo nie wiem co jest źle u Ciebie.

Wyskołyczł mi błąd w linijce gdzie jest:

iVersionInfo : TOSVersionInfo;

prosze o pomooc

Ang:
Windows 2000/XP: Forces processes to terminate if they do not respond to the WM_QUERYENDSESSION or WM_ENDSESSION message. This flag is ignored if EWX_FORCE is used.

Zapomniałeś wspomnieć o fladze: EWX_FORCEIFHUNG
Nie poprawiam tego samodzielnie, gdyż dziwnym dla mnie się wydaje ingerowanie w cudze artykuły.

tego mi było trzeba!

Nawet niezłe

spoko, przyda sie :)

Do Format: byłbym Ci bardzo wdzięczny, gdybyś opisał tą flage. Artykuł pisałem dawno, więc nie jestem w temacie :).

Świetny art, jednakże mógłbyś tekst wrzucić pomiędzy tagi justowania tekstu, łatwiej by sie go czytało.

wkleiłem ten kod i mi wywala bład w lini

iVersionInfo : TOSVersionInfo;