Publikoval Michal Kočí dňa 5.1.2005 o 22:30 v kategórii .Net
Potreboval som logovať, kedy som si zamkol počítač (lock computer). Tým začalo moje hľadanie. Myšlienka, naprogramovať si to sám, ma síce napadla, na druhej strane sa mi nechcelo veriť, že neexistuje softvér, ktorý to zvládne. Potreboval som iba dátum a čas každého zamknutia počítača. Na moje obrovské počudovanie, som takýto softvér nenašiel. Takže začalo pátranie, ako odchytiť udalosť zamknutia počítača...
Celkom rýchlo som sa dopracoval k informácii, že to čo potrebujem je, spraviť Windows Notification Package. Jedná sa dll knižnicu, ktorá exportuje metódy - metódy, ktoré zavolá operačný systém (Win2000 a vyšší) a tak mi oznámi, že došlo k udalosti. Túto knižnicu treba zaregistrovať (cez registry systému) a potom proces winlogon.exe, ktorý zastrešuje prihlasovanie a odhlasovanie, bude volať metódy tejto knižnice.
Winlogon volá tie exportované metódy z tohto dll, k odberu svoje dll prihlásite - nemusíte tak vytvoriť metódu pre každú udalosť, ale iba pre tie, o ktoré máte záujem. Navyše, Vami exportované metódy môžu mať ľubovoľný názov práve preto, že pri ich registrácii do registry systému zadávate meno tejto metódy.
A k odberu akých udalostí sa môžete prihlásiť? Tak napríklad:
Plus zopár ďaľších. Ich presný zoznam nájdete tu.
Všetky metódy majú rovnakú signatúru, nevracajú žiadnu hodnotu (vracajú void) a prijímajú ukazovateľ na štruktúru WLX_NOTIFICATION_INFO s doplňujúcimi informáciami (napríklad login prihlásujúceho/odhlasujúceho sa užívateľa, ...).
Tým, že je potreba aby dll exportovalo metódy, použitie .Net Frameworku neprichádzalo v úvahu. Nie že by sa také dll nedalo vytvoriť, ale treba to obchádzať dissasmblovaním do MSIL a spätným assemblovaním, čo som nepovažoval za vhodné. Takže voľba padla na Delphi. S tým sa vynoril ďaľší problém - v MSDN je síce popísaná štruktúra WLX_NOTIFICATION_INFO, ale pre jazyk C a nie pre Delphi. Naštastie, Google ozaj nájde všetko a takisto aj prepis štruktúry WLX_NOTIFICATION_INFO do Delphi.
Takže, treba už len vytvoriť spomínané dll, exportujúce metódy, ktoré neskôr zaregistrujeme v registroch systému. Ako na registráciu je opäť popísané v MSDN. Registrované dll treba umiestniť do system32 adresára. Dll, ktoré bude odchytávať iba udalosť zamknutia počítača môže vyzerať nasledovne:
library WinLogonSpy; uses Windows, SysUtils; {$R *.res} type PFNMSGECALLBACK = function (bVerbose: BOOL; lpMessage: PWideChar): DWORD; stdcall; {$EXTERNALSYM PFNMSGECALLBACK} TFnMsgeCallback = PFNMSGECALLBACK; PWLX_NOTIFICATION_INFO = ^WLX_NOTIFICATION_INFO; {$EXTERNALSYM PWLX_NOTIFICATION_INFO} _WLX_NOTIFICATION_INFO = record Size: ULONG; Flags: ULONG; UserName: PWideChar; Domain: PWideChar; WindowStation: PWideChar; hToken: THandle; hDesktop: HDESK; pStatusCallback: PFNMSGECALLBACK; end; {$EXTERNALSYM _WLX_NOTIFICATION_INFO} WLX_NOTIFICATION_INFO = _WLX_NOTIFICATION_INFO; {$EXTERNALSYM WLX_NOTIFICATION_INFO} TWlxNotificationInfo = WLX_NOTIFICATION_INFO; PWlxNotificationInfo = PWLX_NOTIFICATION_INFO; procedure LogMsg(const Msg: string); var f: TextFile; s: string; begin s := 'c:\winlogon.log'; try AssignFile(f, 'c:\winlogon.log'); if(FileExists(s)) then begin Append(f); end else begin Rewrite(f); end; WriteLn(f, FormatDateTime('dd.mm.yyyy hh:nn:ss', Now()), ' : ', Msg); CloseFile(f); except end; end;
procedure LogEvent(const EventType: string; const PInfo: PWLX_NOTIFICATION_INFO); begin try if ((pInfo <> nil) and (pInfo^.UserName <> nil)) then begin LogMsg(EventType + ' : ' + pInfo^.UserName); end else begin LogMsg(EventType + ' : Unknown user'); end; except on Exception do LogMsg(EventType + ' : exception'); end; end;
procedure OnLock(pInfo: PWLX_NOTIFICATION_INFO); stdcall; begin LogEvent('Lock', pInfo); end;
exports OnLock;
begin end.
Dll má metódu OnLock, ktorú zaregistrujeme aby obsluhovala udalosť Lock, ktorá nastane pri zamknutí počítača. Táto zavolá metódu LogEvent (ktorá zavolá LogMsg). Ich cieľom je zapísať dátum a čas udalosti spolu s názvom udalosti a loginom prihláseného užívateľa.
Po skompilovaní kódu ho zaregistrujeme v registroch, reg súbor by mohol vyzerať nasledovne (prvé dva riadky tvoria v skutočnosti jeden riadok):
REGEDIT4 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify\winlogonspy] "Asynchronous"=dword:00000000 "Impersonation"=dword:00000000 "DllName"=hex(2):57,69,6e,4c,6f,67,6f,6e,53,70,79,2e,64,6c,6c,00 "Lock"="OnLock"
A nasleduje reštart počítača. Hneď po reštarte si proces winlogon.exe nahrá naše dll do svojho adresného priestoru a bude volať vami zaregistrované metódy pri nastaní požadovaných udalostí. Z tohto pramení jeden veľmi dôležitý fakt - ak nastane vynímka v niektorej z Vašich metód, spadne celý winlogon.exe proces a zobere so sebou celý windows. Takže rozhodne nezabudnite try/except blokmi ošetrovať vynímky.
Že je to dôležité som sa presvedčil aj ja sám. Ošetrenie som nedal a kvôli hlúpej, začiatočníckej programátorskej chybe (zabudol som kontrolovať existenciu súboru, do ktorého som chcel pridávať text), ktorú som si bohužial uvedomil až v momente, keď mi bootoval systém sa hneď po prihlásení spustila moja udalosť pre prihlásenie užívateľa, spôsobila vynímku a zhodila celý systém... V tom momente som bol ozaj vďačný Microsoftu za bootovateľné CD obsahujúce Recovery Console :)
Ak nechceš premeškať príspevky ako je tento, sleduj ma na Twitteri, alebo ak máš RSS čítačku, môžeš sledovať môj RSS kanál.