Živě.cz o počítačích a internetu

Tipy a triky v Delphi, díl 5. - Restart a ukončení Windows; Detekce ukončení systému; Spuštění a ukončení cizí aplikace; Klávesy NumLock,CapsLock a Scroll Lock

Jan Šindelář - 22.8.2001

Restart a ukončení Windows

Nejprve si ukážeme, jak celkem snadno restartovat systém. Tato funkce se nám může hodit při různých příležitostech, např. pokud vaše aplikace provede určité změny v konfiguraci Windows, obvykle se projeví až po restartu systému (záleží samozřejmě na "závažnosti" provedených změn). Nejčastěji se s požadavkem na restart setkáte pravděpodobně při instalaci nového software a nutno poznamenat, že neustálé restarty nemají uživatelé příliš rádi. Proto je třeba s touto funkcí nakládat s rozumem, a co je nejdůležitější, vždy byste měli před restartem uživatele upozornit a dát mu možnost restartu zabránit.

Pod Windows 9x je situace poměrně jednoduchá. Stačí nám k tomu jeden řádek kódu:

procedure Restart;

begin

ExitWindowsEx(EWX_REBOOT, 0);

end;

Tento způsob je ten šetrnější, kdy je (zjednodušeně řečeno) ostatním aplikacím poslán dotaz, zda je možné systém ukončit, a ty nám to buď povolí nebo ne (viz další kapitola článku). Pokud chcete ukončit systém "násilím", stačí drobně upravit parametry na:

ExitWindowsEx(EWX_FORCE or EWX_REBOOT, 0);

V tomto případě by mělo dojít k restartu systému bez ohledu na to, zda nám to ostatní aplikace dovolí či ne. Osobně bych se přikláněl spíše k první variantě, protože dáte ostatním aplikacím možnost se na ukončení systému "připravit".

Tato funkce má však i další možnosti parametrů kromě EWX_REBOOT. Jsou to:

Názvy jsou myslím dostatečně jasné a netřeba dalšího vysvětlování. Snad jen drobnou poznámku: ačkoliv popis parametrů a použití této funkce pochází přímo od Borlandu, občas se mi stane, že v určitých případech nefungují, jak mají (jedná se hlavně o poslední dvě varianty). Pokud snad někdo ze čtenářů ví proč, neváhejte se prosím pochlubit.

U Windows 2000 je situace poněkud složitější. Pojďme si rovnou ukázat zdrojový kód:

procedure Restart2000;

var

hToken, hProcess: THandle;

tp, prev_tp: TTokenPrivileges;

Len, Flags: DWORD;

begin

if Win32Platform = VER_PLATFORM_WIN32_NT then

begin

hProcess := OpenProcess(PROCESS_ALL_ACCESS, True, GetCurrentProcessID);

try

if not OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY,hToken) then Exit;

finally

CloseHandle(hProcess);

end;

try

if not LookupPrivilegeValue(``, `SeShutdownPrivilege`,tp.Privileges[0].Luid) then Exit;

tp.PrivilegeCount := 1;

tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;

if not AdjustTokenPrivileges(hToken, False, tp, SizeOf(prev_tp),prev_tp, Len) then Exit;

finally

CloseHandle(hToken);

end;

end;

ExitWindowsEx(EWX_REBOOT, 0);

end;

Vysvětlování, jak to celé funguje, by asi zabralo více času. Zjednodušeně řečeno je nutné projít všechny běžící procesy, zjistit, zda je možné je ukončit (protože pod Windows NT či 2000 jak jistě víte není z důvodů bezpečnosti dovoleno, jen tak se "hrabat" do systému, ke všemu potřebujete ta správná oprávnění), a nakonec se systém restartuje již stejným způsobem jako u výše uvedeného příkladu pro Windows 9x. Opět můžete použít již zmiňované parametry, které vám umožní systém nejen restartovat, ale i kompletně vypnout apod. Příklad byl testován v Delphi 5 a nejsem si jist, zda bude v této podobě pracovat ve všech verzích Delphi. Proto budu rád, když se ozvete, jak příklad pracuje i ve verzích starších či novějších.

Detekce ukončení systému

S předchozí kapitolou souvisí i následující tip. Ukážeme si, jak zjistit, že se cizí aplikace (např. instalátor) nebo prostě samotný systém snaží ukončit Windows. Pokud naše aplikace zrovna provádí nějakou velmi důležitou činnost, můžeme vypnutí systému i zabránit a nebo včas zajistit případné uložení všech důležitých dat. Je to vlastně velice snadné. Když se Windows ukončují, odešlou všem běžícím aplikacím zprávu WM_QUERYENDSESSION, kterou "zachytíme" událostí OnCloseQuery hlavního formuláře aplikace. Parametr CanClose nastavíme na True, pokud chceme systému dovolit ukončení, a nebo False pro opačný efekt. Jednoduchý zdrojový kód může vypadat kupříkladu takto:

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);

begin

if MessageDlg(`Opravdu ukočit ?`, mtConfirmation, mbYesNoCancel, 0) = mrYes

then CanClose := true

else CanClose := false;

end;

Při pokusu o ukončení se zobrazí dialogové okno s potvrzením. To je samozřejmě pouze ukázkové řešení pro názornost a v reálných aplikacích použijeme určitě jiné řešení. Zkušenější z vás jistě vědí, že takto napsaný kód ovšem zobrazí onen dialog při každém ukončení aplikace, tedy ne jen pouze při pokusu o ukončení z "vnějšku", ale i při uzavření aplikace běžným způsobem. Proto by bylo vhodné doplnit proceduru o test, který nám určí, "kdo" aplikaci ukončuje. To nechám čtenářům za domácí úkol.

Spuštění a ukončení cizí aplikace

Další užitečnou věcí, kterou se teď naučíme, je spouštění cizích aplikací. To se nám může hodit například tehdy, když potřebujeme "spustit" nějaký typ dokumentu v externí aplikaci, protože s ním náš program neumí pracovat, a podobně.

Budeme potřebovat knihovnu ShellAPI, proto ji nezapomeňte přidat mezi ostatní do Uses. Vytvoříme si jednoduchou proceduru s několika málo parametry, která nám spustí libovolný soubor.

.

.

.

uses ShellApi;

.

.

.

procedure Spust(soubor,param,defaultdir:string);

begin

ShellExecute(0,nil,soubor,param,defaultdir, SW_SHOWNORMAL);

end;

Parametry jsou myslím jasné – aplikace včetně cesty, případné parametry (můžete nechat prázdné) a "pracovní" adresář (obvykle taktéž můžete ponechat prázdné). Kvůli zjednodušení není prováděna žádná kontrola úspěšnosti pokusu spustit aplikaci (tj. jestli vůbec aplikace existuje).

Pokud již aplikace splnila náš požadavek nebo ji už z jiného důvodu dále nepotřebujeme, můžeme ji jednoduše ukončit. Jeden z jednoduchých způsobů si teď ukážeme. Jediné, co potřebujeme vědět, je titulek okna aplikace, kterou chceme ukončit. Poté jí "pošleme zprávu" o našem požadavku na ukončení. Jednoduchá procedura může vypadat například takto:

procedure Ukonci(titulek:string);

begin

PostMessage(FindWindow(Nil, titulek), WM_QUIT, 0, 0);

end;

Klávesy NumLock, CapsLock a Scroll Lock

Na závěr si trošku pohrajeme s výše uvedenými klávesami. Ukážeme si, jak je programově "stisknout", tj. aktivovat či deaktivovat jejich funkci, což je – jak jistě víte – doprovázeno rozsvícením či zhasnutím příslušných diod, takže pokud si dáte záležet, můžete vytvořit docela zajímavý efekt. Následující příklad aktivuje nebo deaktivuje (podle jejich aktuálního stavu) uvedené klávesy.

procedure TForm1.Button1Click(Sender: TObject);

var KeyState : TKeyboardState;

begin

GetKeyboardState(KeyState);

if (KeyState[VK_NUMLOCK] = 0) then KeyState[VK_NUMLOCK] := 1

else KeyState[VK_NUMLOCK] := 0;

if (KeyState[VK_CAPITAL] = 0) then KeyState[VK_CAPITAL] := 1

else KeyState[VK_CAPITAL] := 0;

if (KeyState[VK_SCROLL] = 0) then KeyState[VK_SCROLL] := 1

else KeyState[VK_SCROLL] := 0;

SetKeyboardState(KeyState);

end;

Pokud máte dostatečnou fantazii, jistě dokážete blikání jednotlivých diod synchronizovat v zajímavé světelné efekty.