Kreslení na plochu v C++ – háky

Diskuze čtenářů k článku

Pavel Masek  |  21. 01. 2002 10:37

Prispevek je skvely! Diky
Chtel bych vedet zda lze take hakovat file I/O ?

Souhlasím  |  Nesouhlasím  |  Odpovědět
HonzaPro  |  18. 01. 2002 19:54

Super

Souhlasím  |  Nesouhlasím  |  Odpovědět
Babarik  |  17. 01. 2002 13:22

Dobry den,

podle popisu

"Jak je vidět, při zachycení zpráv WM_PAINT a WM_ACTIVATE musíme nejdříve otestovat, zda jde o okno desktopu, jehož handle máme uložená, a v tomto případě pak voláme naši vlastní kreslící funkci, kde musíme nejdříve získat kontext zařízení HDC desktopu ..."

si myslim, ze by procedura háku WH_GETMESSAGE mela vypadat takto:

LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
  LPMSG lpmsg;
  if ( nCode < 0 )
    return CallNextHookEx(hhGetMsgProc, nCode, wParam, lParam);
  lpmsg = (LPMSG)lParam;
  switch ( lpmsg->message )
  {
    case WM_ERASEBKGND: break; // nebo uplne vypustit pokud nema souvislost v nize uvedenym casem
    case WM_PAINT:
    case WM_ACTIVATE:
      if ( lpmsg->hwnd == hwndDesktop )
        DrawToDesktop();
      break;
  }
  return CallNextHookEx(hhGetMsgProc, nCode, wParam, lParam);
}

 

Souhlasím  |  Nesouhlasím  |  Odpovědět
Ladislav Zezula  |  17. 01. 2002 09:25

Omluva, nevšiml jsem si, še ten problém s minimalizací je tam už zmíněný.

Pointer na OldWndProc není možné získat proto, že podle specifikace subclassingu ve Win32 uživatel nemůže subclassovat okno, které patří jinému procesu. Proto asi funkce SetClassLongPtr(hwndDesktop, GCLP_WNDPROC,
MyDesktopProc) vrátila NULL a (a evidentně asi ani nic nenastavila, jinak by možná program spadl). Řešením by možná bylo "Napíchnout" se na process Exploreru funkcí CreateRemoteThread, se kterou jsou ale zase jiné problémy (např není podporována ve Win9x). Asi bude lepší se na to "vybodnout".

Souhlasím  |  Nesouhlasím  |  Odpovědět
Radek Chalupa  |  17. 01. 2002 09:58

Já mám dojem, že před časem jsem si s tím trochu hrál, a podařilo se subclassovat jiné okno právě z toho důvodu, že ten proces si tu DLLku natáhne do svého prostoru, takže když to subclassování voláme z dll-ky, jsme ve stejném procesu jako to cílové okno. Teď už nevím zda to chodilo ve Win98 nebo 2000. Tohle co nechodí jsem zkoušel v XP. Když bude čas, ještě si s tím trochu pohraju  a dám vědět.

Souhlasím  |  Nesouhlasím  |  Odpovědět
Ladislav Zezula  |  17. 01. 2002 10:20

Je to jenom úvaha, ale přece :

Funkci SetHooks voláte z VAŠÍ dll natáhnuté do VAŠEHO procesu
Uvnitř je volána funkce SetWindowsHookEx, a pak i funkce SetClassLong(hwndDesktop, GWL_WNDPROC, NewWndProc).
Ale toto volání je stále ve VAŠEM procesu.

Já bohužel taky nemám čas na to, abych to zkoumal, ale :
Co kdyby se ta DLL pokusila subclassovat okno plochy během volání funkce GetMsgProc, která je tuším volána
z CIZÍHO procesu (proto to ostatně musí být DLL, ne ?). Možná by stálo za to zobrazit v messageboxu ID aktuálního procesu v SetHooks a pak v GetMsgProc. Pokud by se ukázalo, že jsme skutečně v CIZÍM procesu, tak bychom měli vyhráno.

Souhlasím  |  Nesouhlasím  |  Odpovědět
Petr  |  17. 01. 2002 14:10

Doporučuji si přečíst kapitolu která se tomu věnuje v knize J.Richter: Advanced Windows. Tam je to celé podrobně popsáno.

Souhlasím  |  Nesouhlasím  |  Odpovědět
Petr  |  17. 01. 2002 14:14

Ta kniha vyšla i v českém překladu kde se daná kapitola jmenuje "Naočkování knihovny pomocí závěsů oken". Zkuste tuhle větu říct někomu, kdo nemá o programování ani ponětí

Souhlasím  |  Nesouhlasím  |  Odpovědět
Ladislav Zezula  |  17. 01. 2002 09:08

Musím pochválit autora za tento seriál. Vyskytují se v něm věci, které by v případě potřeby člověk dlouho a těžce hledal.
Jinak v programu je drobný nedostatek - pokud dojde k minimalizaci okna jiné aplikace, tak nedojde k překreslení "uživatelských blbůstek" na ploše. Je to zvláštní protože bych čekal, že Windows zašlou WM_PAINT do plochy i v připadě, že je minimalizováno okno "nad" plochou.

Otázka na p. Chalupu : Není možné ty hákovací funkce uložit v EXE a pak jako parametr hInstance u volání SetWindowHookEx uvést handle spuštěného procesu ? Proč je k tomu třeba DLL ?

Souhlasím  |  Nesouhlasím  |  Odpovědět
Radek Chalupa  |  17. 01. 2002 09:48

Já myslím, že WM_PAINT tam jde, ale ta hákovací funkce GetMsgProc ji zpracuje dřív než je poslána ke zpracování oknu (tedy oknu plochy), no a plocha se překreslí celá najednou "po svém" a to naše kreslení smaže. V oststních případech se ten problém neprojeví, protože třeba při tažení nějakého okna po ploše jde těch zpráv WM_PAINT "mraky", tedy po každém posunutí o pixel. Takže pak jediná "chyba" by byla že tam může zůstat mezera šířky 1 pixelu, což není vidět a zdá se, že vše chodí jak má. Ale při miminalizaci všech oken jde zpráva WM_PAINt jen jednou.

Pokud budete mít hákovací funkce v exe, budete chtytat všehcny zprávy všech oken vaší aplikace, ale nikoli systému. Jde o to, že tu dll-ku s háky si proces, který máme hákovat musí natáhnout do svého paměťového prostoru, což z vašeho exe nemůže. Nakonec i MSDN to přímo tak praví, že háky přes celý systém musí být v externí DLLce.

P.S. děkuji za pochvalu i přes to, že v redakci opět trochu pozměnili nadpis a ten "úvodní text". Jádrem článku je použití háků a nikoli kteslení tlačítka start, to je použito pouze jako demonstrace. Také výsledek není nijak cool, s graficku podobou už si musí pohrát každý sám, to není téma článku. No a to C++ v názvu článku je také přidáno redakcí, již několikrát se na toto téma zde objevil rozhorlený příspěvek "co je to za blbost ikony v c++ (například))".

Souhlasím  |  Nesouhlasím  |  Odpovědět
Petr  |  17. 01. 2002 14:08

Hookovaci (některé termíny by se asi neměli překládat ) funkce by měla být v DLL, ve Win9x to sice za jistých okolností bude náhodou fungovat i v EXE ale to je pouze vedlejší efekt jiné implementace kernelu.

Souhlasím  |  Nesouhlasím  |  Odpovědět
Tamo  |  20. 01. 2002 21:50

Ja jsem to sice nezkusil, ale co takhle v procedure GetMsgProc pri WM_PAINT nejdrive volat CallNextHookEx a az pak kreslit? Takhle destkop dostane svoji zpravu driv, a my muzeme prekreslovat.

 Taky se mi zda, ze z hlediska vykonnosti by se ve funkci GetMsgProc nejdrive melo testovat, jestli lpmsg->hwnd == hwndDesktop, switch by mel byt az za tim. Tou funkci totiz prochazeji vsechny zpravy, tak by mela byt co nejrychlejsi.

A posledni pripominka: na volani funkci z nasi DLL knihovny nepotrebujeme LoadLibrary a vsechno kolem. Jednodussi je k programu prilinkovat "import library", ktera se vygeneruje pri sestaveni DLL knihovny (ma stejny nazev a priponu lib), a funkce deklarovat napr.:

extern "C" __declspec(dllimport) BOOL SetHooks();

Termin prilinkovat neni uplne presny, mam na mysli uvest tu knihovnu v Project Settings na karte Link v Object/Library modules...

Jeste me napada k tomu podtrzitku v C++ Builderu: muzete nekdo zkusit deklarovat funkce s volaci konvenci stdcall misto cdecl? Zase strilim od boku, ale melo by to prece jit nejak odstranit...

Tamo.

Souhlasím  |  Nesouhlasím  |  Odpovědět
Radek Chalupa  |  20. 01. 2002 22:27

To volání CallNextHookEx "před" nepomůže - jde o to, že jednak tato funkce nevolá defaultní zpracování (jako DefWindowProc v proceduře okna), ale prostě pustí zprávu dalšímu případnému háku, který může být nainstalován. A zpráva se do procedury háku GetrMsgProc dostane až PO jejím vybráním z fronty zpráv (a tedy zpracováním příslušnou aplikací), a v tom je ten problém.

S tím importem se obávám, že by to nefungovalo. Jde o to že ty hákovací procedury musí být v extení DLL proto, že ostatní procesy si tu DLL-ku musí natáhnout do svého paměťového prostoru, jinak by háky fungovaly pouze pro naší aplikaci. Zjistíte to také z toho, když si dáte nějaký signál do DllMain, že tudy projde vícekrát a ne jen při připojení našeho procesu.

Radek Chalupa

Souhlasím  |  Nesouhlasím  |  Odpovědět
Tamo  |  21. 01. 2002 12:13

Ano, to je pravda, ja jsem z nejakeho duvodu mel za to, ze po navratu z CallNextHookEx aplikace uz tu zpravu zpracovala... To by bylo hezke...

Ten "import" a 100% funguje, zkuste se na to podivat. Asi jsem to nevysvetlil dost jasne, nejde o staticky link, "import library" obsahuje seznam funkci vyexportovanych z dll, ale zadny kod - ten je v dll. Import library slouzi pouze k tomu, aby byl linker uspokojen (ma totiz deklarovany funkce, ale definice nikde), a vedel, ve ktere dll knihovne ty funkce jsou.

Souhlasím  |  Nesouhlasím  |  Odpovědět
Ladislav Zezula  |  21. 01. 2002 12:35

Samozřejmě, že ten import funguje, MUSÍ fungovat, tedy ovšem pokud tu DLL/LIB neuděláte v jiném překladači,
tam bych za to moc nedal. Ale nehaňte dynamický import, někdy je opravdu nezbytný.

Souhlasím  |  Nesouhlasím  |  Odpovědět
Tamo  |  21. 01. 2002 13:27

Nehanim pouze jsem toho nazoru, ze zrovna v tomto pripade je staticky import jednodussi. Jinak rekl bych, ze na kompilatoru nezalezi, pokud docilim toho, aby jmena funkci nechal bez dekoraci. Tedy abych rekl pravdu, zkusenosti mam pouze s kompilatory od Borlandu (C++ 5.0) a MS (VC++ 5.0, 6.0)...

Souhlasím  |  Nesouhlasím  |  Odpovědět
Ladislav Zezula  |  21. 01. 2002 13:37

Jo, já mám zkušenosti pouze s MSVC. Problémy s ostatními kompilátory jsou pouze s objektovými metodami, jinak by to mělo být jedno.

Souhlasím  |  Nesouhlasím  |  Odpovědět
Zasílat názory e-mailem: Zasílat názory Můj názor