Základné služby systému Windows a funkcie rozhrania API. Čo je Windows API. Dedenie objektov pre výstup a aritmy. operácií

  • 31.10.2019

Vylúčenie zodpovednosti

Zdalo by sa, že WinAPI je minulosťou. Už dlhú dobu existuje obrovské množstvo crossplatformových frameworkov, Windows nielen na desktopoch a samotný Microsoft vo svojom obchode neuprednostňuje aplikácie, ktoré toto monštrum využívajú. Okrem toho existujú tisíce článkov o tom, ako vytvoriť okná na WinAPI, nielen tu, ale na celom internete, od predškolákov a vyššie. Celý tento proces už rozobrali ani nie atómy, ale subatomárne častice. Čo môže byť jednoduchšie a prehľadnejšie? A potom som ešte...

Ale nie všetko je také jednoduché, ako sa zdá.

Prečo teraz WinAPI?

V jednej krásnej chvíli, keď som študoval útroby jednej z hier vo veľmi dobrej hre, som si pomyslel: Vyzerá to ako dobrá emulzia, ale v debuggeri nie je taká jednoduchá vec ako navigácia po tlačidlách klávesnice, čo je v každom normálnom debugger.

o čom to hovorím? A tu je kúsok kódu:

Prípad WM_KEYDOWN: MessageBox (hwndDlg, "Zomri!", "Som mŕtvy!", MB_YESNO | MB_ICONINFORMATION); break;
Autori teda chceli pridať podporu pre klávesnicu, no tvrdá realita útrob architektúry dialógových okien vo Windows takýto amatérizmus tvrdo potlačila. Videli niekedy túto správu tí, ktorí používali emulátor a debugger?
Aký je problém?

Odpoveď je: nemôžete to urobiť!

A vráťme sa k pôvodnej otázke o WinAPI: veľa populárnych a nie veľmi projektov ho v súčasnosti naďalej používa, pretože lepšie ako na čistom API sa veľa vecí nedá urobiť (tu môžete donekonečna dávať analógie ako porovnávanie jazykov na vysokej úrovni a assembleru, ale o tom to teraz nie je). A nikdy nevieš prečo? Jednoducho to použijú a hotovo.

O probléme

Dialógové okná uľahčujú používanie GUI a zároveň nám znemožňujú niečo urobiť sami. Napríklad správy WM_KEYDOWN / WM_KEYUP, ktoré prichádzajú v procedúre okna, sú „zjedené“ v hĺbke DefDlgProc, pričom preberajú také veci, ako sú: navigácia pomocou kariet, spracovanie kláves Esc, kláves Enter atď. Dialógy navyše nie je potrebné vytvárať manuálne: jednoduchšie je predsa načrtnúť tlačidlá, zoznamy, v editore zdrojov zavolať CreateDialog / DialogBox vo WinMain a hotovo.

Je ľahké obísť takéto menšie problémy. Existujú minimálne dva úplne legálne spôsoby:

  1. Vytvorte si vlastnú triedu cez RegisterClassEx a uchopte WM_KEYDOWN v procedúre spracovania triedy, presmerujte ju na procedúru spracovania dialógu. Áno áno! Môžete vytvárať dialógové okná s vlastnou triedou a vstavaný editor VS vám dokonca umožňuje zadať názov triedy pre dialógové okno. Ale kto o tom vie a používa to?
    Nevýhoda je zrejmá: Treba si zaregistrovať ešte jednu triedu, mať ešte 1 procedúru CALLBACK, ktorej podstata bude len v odvysielaní niekoľkých správ. Navyše sa to nedozvieme kde vysielajte ich a budete musieť oplotiť barly.
  2. Použite vstavaný akceleračný mechanizmus. A nemusíme ani meniť kód dialógovej procedúry! No, možno, pridajte jeden riadok do prepínača / prípadu, ale viac o tom nižšie.

Návody?

Nebojím sa to povedať všetky návody na vytváranie okien cez WinAPI začínajú takýmto jednoduchým kódom, označujúcim ho ako „slučka správ“ (vynechám detaily prípravy triedy okna a ďalšie viazanie):

Kým (GetMessage (& msg, nullptr, 0, 0)) (TranslateMessage (& msg); DispatchMessage (& msg);)
Všetko je tu naozaj jednoduché:

  1. GetMessage () zachytí ďalšiu správu z frontu a kľúčový moment: blokuje vlákno, ak je front prázdny.
  2. TranslateMessage () z WM_KEYDOWN / WM_KEYUP generuje správy WM_CHAR / WM_SYSCHAR (sú potrebné, ak si niekto chce vytvoriť vlastný textový editor).
  3. DispatchMessage () odošle správu do procedúry okna (ak taká existuje).
Na začiatok je použitie tohto kódu nebezpečné a tu je dôvod. Vezmite prosím na vedomie poznámku pod čiarou:
Pretože návratová hodnota môže byť nenulová, nulová alebo -1, vyhnite sa kódu ako je tento:
while (GetMessage (lpMsg, hWnd, 0, 0)) ...
A nižšie je príklad správnej slučky.

Stojí za to povedať, že vo VS šablónach pre aplikácie Win32 je to presne napísané nesprávne cyklu. A toto je veľmi smutné. Málokto sa totiž bude vŕtať v tom, čo urobili samotní autori, pretože je to a priori správne. A nesprávny kód sa množí spolu s chybami, ktoré je veľmi ťažké zachytiť.

Po tomto kúsku kódu je spravidla príbeh o urýchľovačoch a pridáva sa niekoľko nových riadkov (berúc do úvahy poznámku v MSDN, navrhujem ihneď napísať správny cyklus):

HACCEL hAccel = LoadAccelerators (hInstance, MAKEINTRESOURCE (IDR_ACCELERATOR)); BOOL bRet = 0; while (bRet = GetMessage (& msg, nullptr, 0, 0)) (if (-1 == bRet) break; if (! TranslateAccelerator (msg.hwnd, hAccel, & msg)) (TranslateMessage (& msg); DispatchMessage (a správa) ;))
Toto je ten, ktorý som videl najčastejšie. A on ( ta-dam) sa opäť mýli!

Najprv o tom, čo sa zmenilo (a potom o problémoch tohto kódu):

V prvom riadku sa zo zdrojov načíta tabuľka kľúčov, po stlačení sa vygeneruje správa WM_COMMAND s príslušným ID príkazu.

TranslateAccelerator v skutočnosti robí toto: ak vidí WM_KEYDOWN a kód kľúča, ktorý je v tomto zozname, potom (opäť kľúčový moment) vygeneruje správu WM_COMMAND (MAKEWPARAM (id, 1)) a odošle ju zodpovedajúcej pre deskriptor okna špecifikovaný v prvom argumente, postup spracovania.

Myslím, že z poslednej frázy bolo jasné, aký je problém s predchádzajúcim kódom.
Dovoľte mi vysvetliť: GetMessage zachytáva správy pre VŠETKY objekty typu „okno“ (ktoré zahŕňajú deti: tlačidlá, zoznamy atď.) a TranslateAccelerator odošle vygenerovaný príkaz WM_COMMAND kam? Správne: späť na tlačidlo / zoznam atď. WM_COMMAND ale spracovávame v našom postupe, čo znamená, že máme záujem ho doň dostať.

Je jasné, že v našom vytvorenom okne je potrebné zavolať TranslateAccelerator:

HWND hMainWnd = CreateWindow (...); HACCEL hAccel = LoadAccelerators (hInstance, MAKEINTRESOURCE (IDR_ACCELERATOR)); BOOL bRet = 0; while (bRet = GetMessage (& msg, nullptr, 0, 0)) (if (-1 == bRet) break; if (! TranslateAccelerator (hMainWnd, hAccel, & msg)) (TranslateMessage (& msg); DispatchMessage (& správa);))
A všetko sa teraz zdá byť dobré a úžasné: všetko sme podrobne analyzovali a všetko by malo fungovať perfektne.

A opäť nie. :-) Toto bude fungovať správne, pokiaľ budeme mať presne jedno okno - naše. Hneď ako sa objaví nové okno (dialógové okno), všetky klávesy, ktoré sa v ňom stlačia, sa preložia do WM_COMMAND a odošlú sa kam? A opäť, správne: do nášho hlavného okna.

V tejto fáze navrhujem, aby sa táto slepá ulička nevyriešila o barlách, ale navrhujem zvážiť veci, ktoré sú v tutoriáloch menej bežné (alebo sa takmer nikdy nevyskytujú).

IsDialogMessage

Podľa názvu tejto funkcie by si niekto mohol myslieť, že z nejakého dôvodu určuje, či táto správa súvisí s dialógom alebo nie. Ale v prvom rade, prečo to potrebujeme vedieť? A po druhé, čo by sme mali s týmito informáciami ďalej robiť?

V skutočnosti robí o niečo viac, ako naznačuje názov. menovite:

  • Prechádza cez detské ovládacie prvky pomocou tlačidiel Tab / Shift + Tab / nahor / nadol / doprava / doľava. Plus ešte jedna vec, ale to nám stačí
  • Stlačením ESC vytvoríte WM_COMMAND (IDCANCEL)
  • Stlačením klávesu Enter vygenerujete WM_COMMAND (IDOK) alebo stlačením aktuálneho tlačidla predvolene
  • Predvolene prepína tlačidlá (okraj týchto tlačidiel je o niečo svetlejší ako ostatné)
  • No existujú aj rôzne veci, ktoré používateľovi uľahčia prácu s dialógom.
čo nám to dáva? Po prvé, nemusíme myslieť na navigáciu v okne. Urobia nám všetko. Mimochodom, navigáciu pomocou kariet je možné vykonať pridaním štýlu WS_EX_CONTROLPARENT do nášho hlavného okna, ale je to nemotorné a nie také funkčné.

Po druhé, uľahčí nám to život vo všetkých ostatných bodoch uvedených v zozname (a ešte o niečo viac).

Vo všeobecnosti sa používa niekde v útrobách Windowsu na zabezpečenie práce modálne dialógové okná a dáva sa programátorom, aby ho nazývali pre nemodálne dialógy. Môžeme ho však použiť kdekoľvek:

Hoci je funkcia IsDialogMessage určená pre nemodálne dialógové okná, môžete ju použiť s ľubovoľným oknom, ktoré obsahuje ovládacie prvky, čo umožňuje oknám poskytovať rovnaký výber klávesnice, aký sa používa v dialógovom okne.
Tie. ak teraz naštylizujeme slučku takto:

HWND hMainWnd = CreateWindow (...); HACCEL hAccel = LoadAccelerators (hInstance, MAKEINTRESOURCE (IDR_ACCELERATOR)); BOOL bRet = 0; while (bRet = GetMessage (& msg, nullptr, 0, 0)) (if (-1 == bRet) break; if (! TranslateAccelerator (hMainWnd, hAccel, & msg)) (if (! IsDialogMessage (hMainWnd, & msg )) ( TranslateMessage (& msg); DispatchMessage (& msg);)))
Potom bude mať naše okno navigáciu ako v natívnom dialógovom okne Windows. Teraz však máme dve nevýhody:

  1. Tento kód bude tiež fungovať iba dobre s jedným (nemodálnym) oknom;
  2. Po získaní všetkých výhod dialógovej navigácie stratíme kúzla vo forme správ WM_KEYDOWN / WM_KEYUP (iba pre samotné okno, nie pre podradené ovládacie prvky);
A v tejto fáze vo všeobecnosti všetky tutoriály končia a začínajú sa otázky: Ako zvládnuť udalosti klávesnice v štandardnom dialógu winapi?
Toto je prvý odkaz na Google, ale verte mi: sú ich tisíce. O navrhovaných riešeniach (najlepšie z nich je vytvorenie vlastnej triedy dialógov, o ktorej som písal vyššie, pred podtriedou a RegisterHotKey. Niekde som dokonca videl „najlepší“ spôsob: pomocou Windows Hooks).

Je čas hovoriť o tom, čo nie je v tutoriáloch a odpovediach.

Spravidla (spravidla! Ak niekto chce viac, potom si môžete zaregistrovať svoju triedu na dialógy a pracovať takto. A ak má niekto záujem, môžem pridať tento článok) WM_KEYDOWN sa chce, keď chce zvládnuť klikanie na kláves, ktorý bude vykonávať funkciu bez ohľadu na zvolený ovládací prvok v okne - tzn nejakú všeobecnú funkciu pre celý tento konkrétny dialóg. A ak áno, prečo nevyužiť bohaté možnosti, ktoré nám ponúka samotné WinAPI: TranslateAccelerator.

Využitie všade presne jeden tabuľku akcelerátora a iba pre hlavné okno. No, naozaj: slučka GetMessage je jedna, čo znamená, že existuje iba jedna tabuľka. Čo iné s nimi robiť?

V skutočnosti môžu byť slučky GetMessage vnorené... Pozrime sa ešte raz na popis PostQuitMessage:

Funkcia PostQuitMessage odošle správu WM_QUIT do frontu správ vlákna a okamžite sa vráti; funkcia jednoducho indikuje systému, že vlákno niekedy v budúcnosti požaduje ukončenie.
A získať správu:
Ak funkcia načíta správu WM_QUIT, návratová hodnota je nula.
Slučka GetMessage sa teda ukončí, ak v procedúre okna zavoláme PostQuitMessage. Čo to znamená?

Môžeme za z každého nemodálne okná v našom programe vytvárajú vlastnú podobnú slučku. V tomto prípade nám DialogBoxParam nevyhovuje, keďže otáča svoj vlastný kolobeh a my to nevieme ovplyvniť. Ak však vytvoríme dialóg cez CreateDialogBoxParam alebo okno cez CreateWindow, tak môžeme roztočiť ešte jednu slučku. Navyše v každý V takomto okne a dialógovom okne musíme zavolať PostQuitMessage:

HWND hMainWnd = CreateWindow (...); HACCEL hAccel = LoadAccelerators (hInstance, MAKEINTRESOURCE (IDR_ACCELERATOR)); BOOL bRet = 0; while (bRet = GetMessage (& msg, nullptr, 0, 0)) (if (-1 == bRet) break; if (! TranslateAccelerator (hMainWnd, hAccel, & msg)) (if (! IsDialogMessage (hMainWnd, & msg )) ( TranslateMessage (& msg); DispatchMessage (& msg);))) // .... LRESULT CALLBACK WndProc (HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam) (prepínač (umsg) (prípad WM_MYMESSAGE: ( HWND hDlg = Vytvoriť hInstance, MAKEINTRESOURCE (IDD_MYDIALOG), hwnd, MyDialogBoxProc); HACCEL hAccel = LoadAccelerators (hInstance, MAKEINTRESOURCE (IDR_ACCELERATOR_FOR_MY_DIALOG) while,BOOL f, modlgRetE je okno 0, 0)) (if (-1 == bRet) break; if (! TranslateAccelerator (hDlg, hAccel, & msg)) (if (! IsDialogMessage (hDlg, & msg)) (TranslateMessage (& msg); DispatchMessage ( & msg);))) EnableWindow (hwnd, fSavedEnabledState); // povolenie nadradeného okna. Dialógové okno bolo zatvorené prestávka;) )) INT_PTR CALLBACK MyDlgProc (HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam) (prepínač (umsg) (prípad WM_CLOSE: (// EndDialog (hwnd, 0); - NEROBIŤ TO! // EndDialog je platný LEN pre modálne dialógy vytvorené pomocou DialogBox (Param) DestroyWindow (hwnd); prestávka; ) case WM_DESTROY: (PostQuitMessage (0); break;) // ....) return 0; )
Poznámka: teraz pre každé nové okno v našom programe môžeme pridať k spracovaniu vlastné akceleračná tabuľka. WM_QUIT vytrhne GetMessage z dialógovej slučky a vonkajšia slučka ju ani neuvidí. Prečo sa to deje?

Faktom je, že vonkajšia slučka „vstala“ pri volaní na DispatchMessage, ktorá zavolala našu procedúru, ktorá roztočila svoje vlastné interiéru Slučka GetMessage s rovnakou správou DispatchMessage. Klasický vnorený hovor (v tomto prípade DispatchMessage). Vonkajšia slučka preto nedostane WM_QUIT a v tomto bode sa neskončí. Všetko bude fungovať harmonicky.

Ale sú tu aj nevýhody:
Každá takáto slučka bude spracovávať správy len pre "vlastné" okno... O ostatných tu nevieme. To znamená, že ak je niekde deklarovaný ďalší cyklus, tak všetky ostatné okná nedostanú potrebné spracovanie svojich správ pomocou dvojice TranslateAccelerator / IsDialogMessage.

No, je čas vziať do úvahy všetky tieto poznámky a konečne napísať správne spracovanie všetkých správ zo všetkých okien nášho programu. Chcem poznamenať, že nižšie uvažujeme o prípade jedného vlákna. Pretože každé vlákno má svoj vlastný front správ, potom si každé vlákno bude musieť vytvoriť svoje vlastné štruktúry. To sa deje veľmi triviálnymi zmenami v kóde.

Robíme to krásne

Pretože správna formulácia úlohy je polovica riešenia, potom je potrebné najskôr správne nastaviť práve túto úlohu.

Po prvé, bolo by logické, že iba aktívny okno prijíma správy. Tie. pre neaktívne okno nebudeme vysielať urýchľovače a posielať správy do IsDialogMessage.

Po druhé, ak pre okno nie je špecifikovaná tabuľka akcelerátora, potom nie je čo vysielať, jednoducho pošleme správu do IsDialogMessage.

Vytvorme jednoduchú std :: mapu, ktorá bude mapovať deskriptor okna na deskriptor tabuľky akcelerátora. Páči sa ti to:

Std :: mapa l_mAccelTable;
A ako vytvárame okná, budeme k nej pridávať nové okná pomocou kľučky do našej obľúbenej tabuľky (alebo nuly, ak takéto spracovanie nie je potrebné).

BOOL AddAccelerators (HWND hWnd, HACCEL hAccel) (if (IsWindow (hWnd)) (l_mAccelTable [hWnd] = hAccel; return TRUE;) return FALSE;) BOOL AddAccelerators (HWND hWnd, LPCTS hAccel)), areturn) ) BOOL AddAccelerators (HWND hWnd, int accel) (vrátiť AddAccelerators (hWnd, MAKEINTRESOURCE (accel));) BOOL AddAccelerators (HWND hWnd) (vrátiť AddAccelerators (hWnd, HACCEL) (N
No po zatvorení okna vymažte. Páči sa ti to:

Void DelAccel (HWND hWnd) (std :: mapa :: iterátor me = l_mAccelTable.find (hWnd); if (ja! = l_mAccelTable.end ()) (if (ja-> sekunda) (DestroyAcceleratorTable (ja-> sekunda);) l_mAccelTable.erase (ja);))
Teraz, keď vytvárame nové dialógové okno / okno, zavolajte AddAccelerators (hNewDialog, IDR_MY_ACCEL_TABLE). Ako zatvoriť: DelAccel (hNewDialog).

Máme zoznam okien s potrebnými deskriptormi. Poďme trochu upraviť našu hlavnú slučku správ:

// ... HWND hMainWnd = CreateWindow (...); AddAccelerators (hMainWnd, IDR_ACCELERATOR); BOOL bRet = 0; while (bRet = GetMessage (& msg, nullptr, 0, 0)) (if (-1 == bRet) break; if (! HandleAccelArray (GetActiveWindow (), msg)) (TranslateMessage (& msg); DispatchMessage (& msg ;))) // ...
Oveľa lepšie! Čo je v HandleAccelArray a prečo je tam GetActiveWindow ()?

Trochu teórie:

Existujú dve funkcie, ktoré vracajú rukoväť do aktívneho okna, GetForegroundWindow a GetActiveWindow. Rozdiel medzi prvým a druhým je celkom prehľadne opísaný v popise druhého:

Návratová hodnota je popisovač aktívneho okna pripojený k frontu správ volajúceho vlákna. V opačnom prípade je návratová hodnota NULL.
Ak prvý vráti kľučku do ktoréhokoľvek okna v systéme, potom druhý vráti iba to jedno ktorý používa front správ nášho vlákna... Pretože Nás zaujímajú iba okná nášho vlákna (to znamená tie, ktoré spadnú do nášho frontu správ), potom vezmeme to druhé.

Takže HandleAccelArray, vedený rukoväťou, ktorá mu bola odovzdaná pre aktívne okno, hľadá práve toto okno na našej mape, a ak tam je, odošle túto správu na preklad v TranslateAccelerator a potom (ak prvé okno nevidelo potrebné) v IsDialogMessage. Ak ani tento nespracoval správu, vrátime FALSE, aby sme prešli štandardným postupom TranslateMessage / DispatchMessage.

Vyzerá to takto:

BOOL HandleAccelWindow (std :: map :: const_iterator mh, MSG & msg) (const HWND & hWnd = mh-> prvý; const HACCEL & hAccel = mh-> druhý; if (! TranslateAccelerator (hWnd, hAccel, & msg)) (// správa nie pre TranslateAccelerator . Skúste to s IsDialogMessage if (! IsDialogMessage (hWnd, & msg)) (// takže predvolená hodnota vráti FALSE;)) // ok, správa preložená. Povedzte to message-loop, aby sa ďalšia správa vrátila TRUE;) BOOL HandleAccelArray (HWND hActive, MSG & msg) (ak (! HActive) vráti FALSE; // žiadne aktívne okno. Nie je potrebné nič robiť std :: map :: const_iterator mh = l_mAccelTable.find (hActive); if (mh! = l_mAccelTable.end ()) (// Dobre! Skúste preložiť túto správu pre aktívne okno return HandleAccelWindow (mh, msg);) return FALSE; )
Teraz má každé detské okno právo pridať svoju obľúbenú tabuľku akcelerátora a pokojne chytiť a spracovať WM_COMMAND s potrebným kódom.

Čo ešte obsahuje jeden riadok v kóde obslužného programu WM_COMMAND?

Popis v TranslateAccelerator znie:
Na odlíšenie správy, ktorú táto funkcia odosiela, od správ odoslaných ponukami alebo ovládacími prvkami, obsahuje najvyššie slovo parametra wParam správy WM_COMMAND alebo WM_SYSCOMMAND hodnotu 1.
Kód na spracovanie WM_COMMAND zvyčajne vyzerá takto:

Prepínač (HIWORD (wParam)) (veľké písmená BN_CLICKED: // príkaz z tlačidiel / ponúk (prepínač (LOWORD (wParam)) (veľké písmená IDC_BUTTON1: DoButton1Stuff (); break; case IDC_BUTTON2: DoButton2Stuff (); break; // ... ) prestávka;))
Teraz môžete písať takto:

Prepínač (HIWORD (wParam)) (1. prípad: // prípad akcelerátora BN_CLICKED: // príkaz z tlačidiel / ponúk (prepínač (LOWORD (wParam)) (prípad IDC_BUTTON1: DoButton1Stuff (); prerušenie; prípad IDC_BUTTON2: DoButton2Stuff (); ; // ...) prestávka;))
A teraz sa vráťte k rovnakému fceux pridaním len jeden riadok do kódu na spracovanie príkazov z tlačidiel dostaneme to, čo chceme: ovládať debugger z klávesnice. Stačí pridať malý obal okolo hlavnej slučky správ a novú tabuľku akcelerátora s potrebnými korešpondenciami VK_KEY => IDC_DEBUGGER_BUTTON.

P.S.: Málokto vie, ale môžete si vytvoriť svoju vlastnú tabuľku akcelerátora a teraz ju používať priamo za behu.

P.P.S.: Pretože DialogBox / DialogBoxParam roztočí svoj vlastný cyklus, potom keď sa cez ne vyvolá dialóg, akcelerátory nebudú fungovať a náš cyklus (alebo cykly) budú „nečinné“.

P.P.P.S.: Po zavolaní HandleAccelWindow sa mapa l_mAccelTable môže zmeniť, pretože TranslateAccelerator alebo IsDialogMessage volajú DispatchMessage a v našich obslužných nástrojoch môžu byť AddAccelerators alebo DelAccel! Preto sa ho po tejto funkcii radšej nedotýkajte.

Môžete cítiť kód. Ako základ bol vzatý kód vygenerovaný zo štandardnej šablóny MS VS 2017.

Štítky: Pridať štítky

FindWindow
GetWindow
GetWindowText
SetWindowText
IsWindow
MoveWindow
IsWindowVisible
EnableWindow
IsWindowEnabled
WindowFromPoint
ShowWindow
CloseWindow
SetWindowPos
GetClassLong
SetClassLong
GetWindowLong
SetWindowLong
GetDesktopWindow
Získajte rodiča

Funkcia FindWindow

funkcia FindWindow (className, WindowName: PChar): HWND;
Funkcia vráti popisovač okna, ktorý spĺňa požiadavku (0, ak sa také okno nenašlo).

Názov triedy Názov triedy, podľa ktorej sa vyhľadáva vo VŠETKÝCH oknách v systéme. Názov okna Názov okna

Jeden z parametrov môže byť nula, v takom prípade sa vyhľadávanie vykoná pomocou druhého parametra.
Príklad:

Funkcia GetWindow

funkcia GetWindow (Wnd: HWND; Param): HWND
Funkcia vráti popisovač okna, ktorý spĺňa požiadavku.

Wnd Rukoväť do ľubovoľného štartovacieho okna Param Akceptuje jednu z nasledujúcich konštantných hodnôt: gw_Owner Rukoväť nadradeného okna sa vráti (0, ak nie je nadradený). gwHWNDFirst Vráti popisovač do prvého okna (vzhľadom na Wnd). gw_HWNDĎalej Vráti popisovač nasledujúceho okna (okná sa opakujú bez opakovania, t.j. ak ste nezmenili parameter Wnd funkcie, deskriptory sa znova nevracajú) gw_Child Vráti rukoväť do prvého podriadeného okna.

Príklad:

Funkcia GetWindowText

function GetWindowText (hWnd: HWND; lpString: PChar; nMaxCount: Integer): Integer;
Funkcia vráti text okna. Pre formulár to bude názov, pre tlačidlo označenie na tlačidle.

hWnd Rukoväť okna, do ktorej sa dostane text. lpString Premenná, do ktorej sa umiestni výsledok nMaxCount

Maximálna dĺžka textu, ak je text dlhší, potom je skrátený.


Príklad:

Funkcia IsWindow

funkcia IsWindow (hWnd: HWND): BOOL; Vráti hodnotu True, ak okno s daným popisovačom existuje, v opačnom prípade vráti hodnotu False.

Hwnd Špecifikátor požadovaného okna

Funkcia MoveWindow

MoveWindow (hWnd: HWND; X, Y, nWidth, nHeight: Integer; bRepaint: BOOL): BOOL; Presunie okno do novej polohy.

hWnd Kľučka k oknu na pohyb. X, Y, nŠírka, nVýška Podľa toho: nové súradnice X, Y; nová šírka, výška. bPrefarbiť Booleovská hodnota označujúca, či sa okno prekreslí.

Funkcia IsWindowVisible

funkcia IsWindowVisible (hWnd: HWND): BOOL;
Ak je dané okno viditeľné, vráti hodnotu True.

hWnd Okenná kľučka.

Povoliť funkciu okna

funkcia EnableWindow (hWnd: HWND; bEnable: BOOL): BOOL;
Nastavuje prístupnosť okna (okno je neprístupné, ak nereaguje na udalosti myši, klávesnice atď.). Analóg Delphi je vlastnosťou komponentov Enabled. EnableWindow vráti True, ak všetko prebehlo dobre, a False inak.

hWnd Okenná kľučka. bPovoliť Boolovská hodnota, ktorá určuje dostupnosť okna.


Príklad:

Funkcia IsWindowEnabled

function IsWindowEnabled (hWnd: HWND): BOOL;
Vráti pre zadané okno: True, ak je okno dostupné, a False v opačnom prípade.

hWnd Okenná kľučka.

Funkcia WindowFromPoint

WindowFromPoint (Bod: TPoint): HWND;
Vráti rukoväť do okna v tomto bode obrazovky.

Bod Typ súradníc bodu obrazovky TPoint(definíciu typu nájdete nižšie)

Funkcia

typu TPoint = Záznam x: Longint; y: Longint; koniec;

Funkcia ShowWindow

funkcia ShowWindow (hWnd: HWND; nCmdShow: Integer): BOOL; Zobrazí alebo skryje okno.

hWnd Špecifikátor požadovaného okna nCmdShow Konštanta, ktorá určuje, čo sa s oknom urobí: SW_HIDE SW_SHOWNORMAL SW_NORMAL SW_SHOWMINIMIZED SW_SHOWMAXIMIZED SW_MAXIMIZE SW_SHOWNOACTIVATE SW_SHOW SW_MINIMIZE SW_SHOWMINNOACTIVE SW_SHOWNA SW_RESTORE SW_SHOWDEFAULT SW_MAX


Príklad:

Funkcia CloseWindow

funkcia CloseWindow (hWnd: HWND): BOOL; stdcall;
Zatvorí okno.

hWnd Kľučka k oknu na zatváranie.

SetWindowPos

funkcia SetWindowPos (hWnd: HWND; hWndInsertAfter: HWND; X, Y, cx, cy: Integer; uFlags: UINT): BOOL; stdcall;
Nastaví okno do novej polohy

hWnd Okenná optika hWndInsertAfter Kľučka okna, pred ktorou je zoznam Z-poradie vloží sa okno hWnd alebo jednu z nasledujúcich konštánt: HWND_BOTTOM Umiestnite okno na spodok zoznamu Z-Order HWND_TOP Presuňte okno na začiatok zoznamu Z-Order X, Y, cx, cy

V súlade s tým nový horizont. , vert. poloha okna ( X, Y), ako aj novú šírku
a výška ( cx, cy)

uFlags Jeden alebo viac (oddelených ALEBO) z nasledujúcich konštánt: SWP_NOSIZE Po presunutí nemeňte veľkosť okna ( cx, cy ignorované) SWP_NOZORDER Nemeňte polohu okna v zozname Z-Order SWP_SHOWWINDOW Po presunutí urobte okno viditeľné SWP_HIDEWINDOW Skryť okno po premiestnení SWP_NOACTIVATE Po presunutí sa nezaostrite na okno SWP_NOMOVE Nepresúvajte okno (ignorujte X, Y)

Funkcia GetClassLong

function GetClassLong (hWnd: HWND; nIndex: Integer): Integer;
Táto funkcia vráti 32-bitové celé číslo prevzaté zo špecifického poľa v zázname TWndClassEx zadané okno.

hWnd Okenná kľučka nIndex Konštanta, ktorá určuje, čo sa vráti. Musí to byť jedna z nasledujúcich možností: GCL_MENUNAME Vráti ukazovateľ na reťazec obsahujúci názov ponuky triedy definovanej v zdrojovom súbore spojenom s nejakým programom. GCL_HBRBACKGROUND Vráti rukoväť (HBRUSH) štetca pozadia priradeného k triede GCL_HCURSOR Vráti rukoväť (HCURSOR) kurzora priradeného k triede GCL_HICON Vráti rukoväť (HICON) pre ikonu priradenú k triede GCL_HMODULE Získa popisovač procesu (HMODULE), ktorý zaregistroval triedu. GCL_CBWNDEXTRA Vráti veľkosť pamäte (v bajtoch) pridelenej na ukladanie ďalších údajov pre toto OKNO. Popis použitia tejto pamäte nájdete v popise funkcie GCL_CBCLSEXTRA Vráti veľkosť pamäte (v bajtoch) vyhradenej na ukladanie ďalšej DATA CLASS GCL_WNDPROC Získa adresu procedúry okna spojenej s triedou. GCL_STYLE Vráti štýl triedy (prítomnosť konkrétneho štýlu je kontrolovaná bitovou operáciou A pomocou konštánt ako cs_XXX) GCL_HICONSM

Poznámka: v prípade, že funkcia vracia ukazovateľ, je potrebná konverzia typu (Integer ->

Funkcia SetClassLong

function SetClassLong (hWnd: HWND; nIndex: Integer; dwNewLong: Longint): Integer; Funkcia spárovaných funkcií. Nastaví požadované pole na zodpovedajúcu hodnotu.
Funkcia vráti starú hodnotu poľa, aby sa mohla neskôr opraviť, alebo sa vráti nula, ak sa niečo pokazilo.

hWnd Okenná kľučka nIndex Jedna zo stálic GCL_XXX z funkcie. V závislosti od hodnoty tohto poľa sa požadované pole zmení.

Poznámka Ukazovateľ písať Celé číslo.

Funkcia GetWindowLong

funkcia GetWindowLong (hWnd: HWND; nIndex: Integer): Longint; Vráti informácie o určitom okne ako 32-bitové celé číslo.

hWnd Okenná kľučka nIndex Konštanta, ktorá určuje, čo sa vráti. Musí existovať jedna z nasledujúcich možností:
GWL_WNDPROC Vráti adresu procedúry okna priradenej k tomuto oknu. Prijatú adresu (po príslušných typových konverziách) možno použiť vo funkcii CallWindowProc... Táto hodnota sa zvyčajne používa, ak chcú nahradiť existujúcu procedúru okna vlastnou, pričom, aby nestratili výkon okna, zvyčajne používajú CallWindowProc. GWL_HINSTANCE Vráti deskriptor aplikácie zadaný pri vytvorení okna funkciou CreateWindowEx. GWL_HWNDPARENT Vráti popisovač (HWND) nadradeného okna GWL_STYLE Vráti štýl okna. Špecifické hodnoty štýlu sa rozpoznávajú pomocou bitovej operácie A a konštanty WS_XXX GWL_EXSTYLE Vráti rozšírený štýl okna. Špecifické hodnoty štýlu sa rozpoznávajú pomocou bitovej operácie A a konštanty WS_EX_XXX GWL_USERDATA Vráti 32-bitové celé číslo spojené s oknom (toto je posledný parameter vo volaní CreateWindow alebo CreateWindowEx) GWL_ID Vráti identifikátor okna (nemá nič spoločné s rukoväťou okna!) Nastavený parametrom hMenu pre podradené okná pri volaní CreateWindow alebo CreateWindowEx

Poznámka: v prípade, že funkcia vracia ukazovateľ, je potrebná konverzia typu (Integer -> Pointer). Môžete to urobiť takto:

Funkcia SetWindowLong

funkcia SetWindowLong (hWnd: HWND; nIndex: Integer; dwNewLong: Longint): Longint;
Spárované, aby fungovalo. Zmení atribúty konkrétneho okna.
Funkcia vráti starú hodnotu vlastnosti, ak je volanie úspešné, alebo inak null.

hWnd Okenná kľučka nIndex Konštanta, ktorá určuje, ktorá vlastnosť sa zmení. Musí to byť jedna z konštánt GWL_XXX z popisu funkcie dwNewLong Nová hodnota vlastnosti zadanej konštantou nIndex

Poznámka: pri nastavovaní polí ukazovateľa sa vyžaduje pretypovanie Ukazovateľ písať Celé číslo.

Funkcia GetDesktopWindow

funkcia GetDesktopWindow: HWND
Funkcia vráti rukoväť do okna Pracovná plocha. Žiadne parametre.

Získajte rodiča

funkcia GetParent (hWnd: HWND): HWND;
Vráti rukoväť nadradeného okna pre okno hWnd.

hWnd Okenná kľučka

Skratka pre API, Application Programming Interface (API) je len nejaký hotový súbor funkcií, ktoré môžu vývojári aplikácií používať. Vo všeobecnosti je tento koncept ekvivalentný tomu, čo sa nazývalo knižnica rutín. Najčastejšie však API odkazuje na nejakú špeciálnu kategóriu takýchto knižníc.

Pri vývoji takmer každej dostatočne komplexnej aplikácie (MyApplication) pre koncového užívateľa vzniká súbor špecifických interných funkcií slúžiacich na implementáciu tohto konkrétneho programu, ktorý sa nazýva MyApplication API. Často sa ukazuje, že tieto funkcie môžu byť efektívne použité aj na vytváranie iných aplikácií, vrátane iných programátorov. V tomto prípade sa musia autori na základe stratégie propagácie svojho produktu rozhodnúť pre otázku - otvárajú prístup k tejto zostave pre externých používateľov alebo nie? S kladnou odpoveďou na to v popise softvérového balíka, ako jeho zásluha, sa zdá, že „súprava obsahuje otvorenú sadu funkcií API“.

API teda najčastejšie znamená súbor funkcií, ktoré sú súčasťou jednej aplikácie, no zároveň sú dostupné na použitie v iných programoch. Napríklad Excel má okrem rozhrania pre koncového používateľa aj sadu funkcií Excel API, ktoré je možné použiť najmä pri vytváraní aplikácií pomocou VB.

V súlade s tým je Windows API súborom funkcií, ktoré sú súčasťou samotného operačného systému a zároveň sú dostupné pre akúkoľvek inú aplikáciu. A v tomto ohľade je analógia so sadou prerušení systému BIOS / DOS, čo je vlastne DOS API, celkom opodstatnená.

Rozdiel je v tom, že skladba funkcií Windows API je na jednej strane oveľa širšia ako v DOSe, na druhej strane neobsahuje veľa nástrojov na priame ovládanie počítačových zdrojov, ktoré mali k dispozícii. programátorov v predchádzajúcom OS. Okrem toho sa k API Windows pristupuje pomocou bežných procedurálnych volaní a volania funkcií DOS sa uskutočňujú prostredníctvom špeciálneho príkazu procesorového stroja s názvom Interrupt.

Win16 API a Win32 API

Ako viete, zmena z Windows 3.x na Windows 95 znamenala prechod zo 16-bitovej architektúry operačného systému na 32-bitovú. Zároveň bolo 16-bitové Windows API (Win16 API) nahradené novou 32-bitovou verziou (Win32 API). V tomto prípade je len potrebné mať na pamäti, že až na pár výnimiek je sada Win32 API rovnaká pre rodiny Windows 9x a Windows NT.

Po oboznámení sa s Win API človek zistí, že mnohé vstavané funkcie nie sú ničím iným ako volaním zodpovedajúcich systémových procedúr, ale implementovaných len vo forme syntaxe daného jazyka. Vzhľadom na to je potreba použiť rozhranie API určená nasledujúcimi možnosťami:

API funkcie, ktoré sú plne implementované ako vstavané funkcie. Niekedy je však v tomto prípade užitočné prejsť na používanie rozhrania API, pretože to niekedy umožňuje výrazne zvýšiť výkon (najmä kvôli absencii zbytočných transformácií odovzdaných parametrov).

Vstavané funkcie implementujú iba špeciálny prípad zodpovedajúcej funkcie API. Toto je pomerne bežná možnosť.

Obrovské množstvo funkcií API nemá v súčasnej verzii kompilátorov vôbec žiadne analógy. Napríklad nemôžete odstrániť adresár pomocou VB - musíte na to použiť funkciu DeleteDirectory.

Treba tiež zdôrazniť, že niektoré funkcie API (ich podiel na Win API je veľmi zanedbateľný) nie je možné volať z programov pre množstvo jazykových obmedzení, napríklad nemožnosť práce s pamäťovými adresami. V niektorých prípadoch však môžu pomôcť netriviálne programovacie techniky (najmä v prípade rovnakých adries).

Win APIaDynamic Link Library (DLL)

Sada Win API je implementovaná ako dynamické knižnice DLL.

V tomto prípade pod pojmom DLL máme na mysli tradičnú verziu binárnych dynamických knižníc, ktoré poskytujú priame volania aplikácií na potrebné procedúry - podprogramy alebo funkcie (v podstate rovnakým spôsobom, ako sa to deje pri volaní procedúr v rámci projektu). Takéto knižnice je možné vytvárať pomocou rôznych nástrojov - VC ++, Delphi, Fortran, Assembler.

Súbory DLL majú zvyčajne príponu .DLL, ale to sa vôbec nevyžaduje. Pre Win16 sa často používala prípona .EXE a ovládače externých zariadení sú označené .DRV.

Určenie presného počtu Windows API a súborov, ktoré ich obsahujú, je náročné (ale všetky sú v systémovom adresári). V tomto smere je lepšie vyzdvihnúť zloženie knižníc, ktoré tvoria jadro operačného systému a hlavné knižnice s kľúčovými doplnkovými funkciami.

Knižnice Win32 API jadra operačného systému Windows 95/98:

KERNEL32.DLL: nízkoúrovňové funkcie na správu pamäte, úloh a iných systémových prostriedkov;

USER32.DLL: Tu sa nachádzajú hlavne funkcie správy používateľského rozhrania;

GDI32.DLL: Knižnica Graphics Device Interface - rôzne funkcie pre výstup na externé zariadenia;

COMDLG32.DLL: Funkcie súvisiace s používaním všeobecných dialógových okien.

Základné knižnice s rozširujúcimi funkciami:

COMCTL32.DLL: sada ďalších ovládacích prvkov systému Windows vrátane zoznamu stromov a formátovaného textu;

MAPI32.DLL: funkcie pre prácu s e-mailom;

NETAPI32.DLL: sieťové ovládacie prvky a funkcie;

ODBC32.DLL: funkcie tejto knižnice sú potrebné na prácu s rôznymi databázami prostredníctvom protokolu ODBC;

WINMM.DLL: Operácie prístupu k systémovým médiám.

Windows API - súbor funkcií operačného systému

Skratka API sa mnohým začínajúcim programátorom zdá byť veľmi tajomná až odstrašujúca. V skutočnosti je aplikačné programové rozhranie (API) len nejaký hotový súbor funkcií, ktoré môžu vývojári aplikácií používať. Vo všeobecnosti je tento koncept ekvivalentný tomu, čo sa nazývalo knižnica rutín. API však zvyčajne odkazuje na špeciálnu kategóriu takýchto knižníc.

Pri vývoji takmer každej dostatočne komplexnej aplikácie (MyApplication) pre koncového užívateľa vzniká súbor špecifických interných funkcií slúžiacich na implementáciu tohto konkrétneho programu, ktorý sa nazýva MyApplication API. Často sa však ukazuje, že tieto funkcie môžu byť efektívne použité na vytváranie iných aplikácií, vrátane iných programátorov. V tomto prípade sa musia autori na základe stratégie propagácie svojho produktu rozhodnúť pre otázku: otvárajú prístup k tejto zostave pre externých používateľov alebo nie? Ak je v popise softvérového balíka odpoveď áno, ako pozitívna charakteristika sa javí veta: „Súprava obsahuje otvorený súbor funkcií API“ (niekedy však za ďalšie peniaze).

API teda najčastejšie znamená súbor funkcií, ktoré sú súčasťou jednej aplikácie, no zároveň sú dostupné na použitie v iných programoch. Napríklad Excel má okrem rozhrania pre koncového používateľa aj sadu funkcií Excel API, ktoré je možné použiť najmä pri vytváraní aplikácií pomocou VB.

V súlade s tým je Windows API súborom funkcií, ktoré sú súčasťou samotného operačného systému a zároveň sú dostupné pre akúkoľvek inú aplikáciu, vrátane tých, ktoré sú napísané pomocou VB. V tomto ohľade je analógia so sadou prerušení systému BIOS / DOS, čo je vlastne DOS API, celkom opodstatnená.

Rozdiel je v tom, že skladba funkcií Windows API je na jednej strane oveľa širšia v porovnaní s DOS, na druhej strane neobsahuje veľa nástrojov na priame ovládanie počítačových zdrojov, ktoré mali k dispozícii. programátorov v predchádzajúcom OS. Okrem toho sa k API Windows pristupuje pomocou bežných procedurálnych volaní a volania funkcií DOS sa uskutočňujú prostredníctvom špeciálneho príkazu procesorového stroja s názvom Interrupt.

Prečo potrebujete Win API pre programátorov VB

Napriek tomu, že VB má obrovské množstvo funkcií, v procese viac či menej seriózneho vývoja sa ukazuje, že ich schopnosti často nestačia na riešenie potrebných úloh. Zároveň sa začínajúci programátori často začínajú sťažovať na nedostatky VB a premýšľajú o zmene nástroja, pričom nemajú podozrenie, že na ich počítači je obrovská sada nástrojov a musíte ich len vedieť používať.

Po oboznámení sa s Win API človek zistí, že mnohé vstavané funkcie VB nie sú ničím iným ako volaním zodpovedajúcich systémových procedúr, ale implementovaných len vo forme syntaxe tohto jazyka. Vzhľadom na to je potreba použiť rozhranie API určená nasledujúcimi možnosťami:

  1. Funkcie API, ktoré sú plne implementované ako vstavané funkcie VB. Niekedy je však v tomto prípade užitočné prejsť na používanie API, pretože to môže niekedy výrazne zvýšiť výkon (najmä kvôli absencii zbytočných transformácií odovzdávaných parametrov).
  2. Vstavané funkcie VB implementujú iba špeciálny prípad zodpovedajúcej funkcie API. Toto je pomerne bežná možnosť. Napríklad rozhranie CreateDirectory API je výkonnejšie ako vstavaný operátor VB MkDir.
  3. Obrovské množstvo funkcií API nemá v súčasnej verzii jazyka VB vôbec žiadne analógy. Napríklad nemôžete odstrániť adresár pomocou VB - musíte na to použiť funkciu DeleteDirectory.

Je tiež potrebné zdôrazniť, že niektoré funkcie API (ich podiel na Win API je veľmi malý) nie je možné volať z programov VB z dôvodu množstva jazykových obmedzení, napríklad z dôvodu nedostatočnej možnosti práce s pamäťovými adresami. V niektorých prípadoch však môžu pomôcť netriviálne programovacie techniky (najmä v prípade rovnakých adries).

Osobný názor autora je taký, že namiesto rozšírenia z verzie na verziu vstavaných funkcií VB by mal byť uvedený dobrý popis najbežnejších funkcií API. Zároveň by som chcel vývojárom poradiť, aby nečakali na novú verziu nástroja s pokročilými funkciami, ale dôkladnejšie si preštudovali existujúce Win API – je pravdepodobné, že schopnosti, ktoré potrebujete, mohli byť implementované už v Verzia VB 1.0 z roku 1991.

Ako sa naučiť Win API

To nie je až taká jednoduchá otázka, keď uvážime, že počet funkcií Win32 API sa odhaduje na cca 10 tisíc (presný údaj nepozná nikto, ani Microsoft).

Súčasťou VB (verzie 4-6) je súbor s popisom deklarácií Win API - WIN32API.TXT (o jeho použití si povieme neskôr). Po prvé ho však možno použiť na získanie informácií o účele konkrétnej funkcie a jej parametroch iba podľa použitých mnemotechnických názvov a po druhé, zoznam funkcií v tomto súbore nie je ani zďaleka úplný. Kedysi (pred siedmimi rokmi) vo VB 3.0 existovali špeciálne súbory pomocníka popisujúce funkcie Win16 API. Už vo verzii 4.0 však tieto užitočné informácie s užívateľsky prívetivým rozhraním zmizli.

Komplexné informácie o Win32 API možno nájsť v Pomocníkovi Platform Software Development Kit, ktorý sa nachádza najmä na CD MSDN Library, ktoré sú súčasťou VB 5.0 a 6.0 Enterprise Edition a Office 2000 Developer Edition. Nájsť tam potrebné informácie a porozumieť im však nie je vôbec jednoduché. Nehovoriac o tom, že všetky tam uvedené popisy sú uvedené vo vzťahu k jazyku C.

Knihy známeho amerického odborníka Daniela Applemana sú vo svete všeobecne uznávané ako návod na učenie sa programovania API v prostredí VB. Jeho séria Dan Appleman's Visual Basic Programmer's Guide to Windows API (pre Win16, Win32, aplikovaná na rôzne verzie VB) je od roku 1993 trvalo jedným z bestsellerov pre VB programátorov. VB 5.0 Programmer’s Guide to the Win32 API od Dana Applemana, vydaný v roku 1997, priniesol autorovi zo Spojených štátov kamarát, ktorý ho našiel v prvom kníhkupectve v malom provinčnom meste.

Táto kniha na viac ako 1500 stranách obsahuje všeobecnú metodológiu programovania API v prostredí VB, ako aj viac ako 900 funkcií. Sprievodný CD-ROM obsahuje kompletný text knihy a všetky programové ukážky, ako aj niekoľko dodatočných kapitol, ktoré neboli zahrnuté v tlačenej verzii. V roku 1999 vydal Dan Appleman knihu Dan Appleman's Win32 API Puzzle Book and Tutorial for Visual Basic Programmers, ktorá obsahuje informácie o ďalších 7 600 funkciách (aj keď menej dôkladných).

Win API a dynamická knižnica (DLL)

Sada Win API je implementovaná ako dynamické knižnice DLL. Ďalej budeme skutočne hovoriť o technológii používania DLL v prostredí VB na príklade knižníc, ktoré sú súčasťou Win API. Avšak, keď hovoríme o DLL, je potrebné urobiť niekoľko dôležitých bodov.

V tomto prípade pod pojmom DLL máme na mysli tradičnú verziu binárnych dynamických knižníc, ktoré poskytujú priame volania aplikácií na potrebné procedúry - podprogramy alebo funkcie (v podstate rovnakým spôsobom, ako sa to deje pri volaní procedúr vo vnútri projektu VB). Takéto knižnice je možné vytvoriť pomocou rôznych nástrojov: VC ++, Delphi, Fortran, s výnimkou VB (pozrime sa, čo sa objaví vo verzii 7.0) - tá môže robiť iba ActiveX DLL, ku ktorým sa pristupuje cez rozhranie OLE Automation.

Súbory DLL majú zvyčajne príponu .DLL, ale to nie je potrebné (pre Win16 sa často používala prípona .EXE); ovládače externých zariadení sú označené ako .DRV.

Ako sme uviedli, je ťažké určiť presný počet funkcií Windows API a súborov, ktoré ich obsahujú, ale všetky sú umiestnené v systémovom adresári. V tomto ohľade je lepšie zdôrazniť zloženie knižníc zahrnutých v jadre operačného systému a hlavné knižnice s kľúčovými doplnkovými funkciami.

Teraz pár tipov.

Tip 1. Uistite sa, že vaša reklama DL je správne naformátovaná L-postupy

Skutočné volanie procedúr DLL v programe vyzerá presne rovnako ako pri „bežných“ procedúrach jazyka Visual Basic, napríklad:

Volať DllName ([zoznam argumentov])

Ak však chcete použiť externé funkcie DLL (vrátane Win API), musia byť deklarované v programe pomocou príkazu Declare, ktorý vyzerá takto:

Deklarujte Sub ProcedureName Lib _ “LibraryName” _ [([ArgumentList])]

Deklarujte funkciu FunctionName _ Lib “LibraryName” _ [([ArgumentList])]

Tu sú v hranatých zátvorkách uvedené voliteľné prvky operátora, premenné výrazy sú kurzívou, zvyšok slov sú kľúčové slová. Systém nápovedy poskytuje pomerne dobrý popis syntaxe operátora, takže zatiaľ si všimneme len niekoľko bodov.

Externé deklarácie funkcií by mali byť umiestnené v sekcii Všeobecné deklarácie modulu. Ak ho umiestnite do modulu formulára, musíte zadať kľúčové slovo Súkromné ​​(táto deklarácia bude dostupná iba v rámci tohto modulu) - toto je obmedzenie pre všetky procedúry modulu formulára.

Sada Win32 API je implementovaná iba ako funkcie (v Win16 API bolo veľa Subs). Väčšinou ide o funkcie typu Long, ktoré najčastejšie vracajú kód dokončenia operácie.

Príkaz Declare sa objavil v MS Basic ešte v časoch DOSu a používal sa aj na deklarovanie interných postupov projektu. V jazyku Visual Basic to nie je potrebné, pretože deklarácia interných procedúr je automaticky ich popisom Sub alebo Function. Oproti Basic / DOS je v novom popise povinné uvádzať názov súboru knižnice, kde sa nachádza požadovaná procedúra. Knižnice Wip API sa nachádzajú v systémovom adresári Windows, takže stačí len názov súboru. Ak odkazujete na knižnicu DLL umiestnenú na ľubovoľnom mieste, musíte si zapísať úplnú cestu k tomuto súboru.

Popis príkazu Declare zvyčajne zaberá veľa miesta a nezmestí sa na jeden riadok v okne kódu. Preto vám odporúčame, aby ste sa pri písaní aplikácií držali určitej schémy zalamovania riadkov, napríklad:

Deklarovať funkciu GetTempPath _ Lib „kernel32“ Alias ​​​​„GetTempPathA“ _ (ByVal nBufferLength ako dlhý, _ ByVal lpBuffer ako reťazec) ako dlhý

V tomto prípade sú všetky hlavné prvky opisu rozmiestnené na rôznych riadkoch, a preto sú ľahko čitateľné.

Tip 2. Buďte obzvlášť opatrní pri práci s funkciami DLL

Použitie Win API a rôznych funkcií DLL výrazne rozširuje funkčnosť VB a často zlepšuje výkon programov. Trestom za to je však riziko zníženia spoľahlivosti aplikácie, najmä pri ladení.

Jednou z najdôležitejších výhod prostredia VB je spoľahlivosť procesu vývoja programu: programový kód pod kontrolou tlmočníka teoreticky nemôže narušiť prácu systému Windows a samotného VB. Programátor si nemusí dávať veľký pozor na správnosť odovzdávania parametrov volaným funkciám – takéto chyby ľahko odhalí aj samotný interpret, či už pri preklade kódu, alebo pri jeho vykonávaní. V najnepríjemnejšom prípade sa režim spracovania jednoducho preruší s uvedením, kde a prečo sa chyba vyskytla.

Používanie rozhraní Windows API alebo iných knižníc DLL priamo odstraňuje túto kontrolu nad prenosom údajov a vykonávaním kódu mimo prostredia VB. Preto chyba vo volaní externých funkcií môže viesť k nefunkčnosti VB aj operačného systému. To platí najmä vo fáze vývoja programu, keď je prítomnosť chýb celkom prirodzená. S využitím širších možností funkcií základnej vrstvy systému teda preberá zodpovednosť za ich správne používanie programátor.

Problém je umocnený skutočnosťou, že rôzne programovacie jazyky používajú rôzne spôsoby odovzdávania parametrov medzi procedúrami. (Presnejšie povedané, štandardne sa používajú rôzne metódy prenosu, pretože mnoho jazykov môže podporovať viacero metód.) Rozhrania Win API sú implementované v C / C ++ a používajú konvencie odovzdávania parametrov tohto systému, ktoré sa líšia od bežných pre VB.

V tejto súvislosti je potrebné poznamenať, že objavenie sa analógov funkcií API zabudovaných do VB je odôvodnené práve prispôsobením týchto funkcií syntaxi VB a implementáciou zodpovedajúceho mechanizmu kontroly výmeny údajov. Všimnite si tiež, že vo fáze experimentálneho ladenia aplikácie, pri vytváraní spustiteľného modulu, je lepšie použiť možnosť kompilácie P-kódu namiesto Native Code (strojový kód). V prvom prípade bude program bežať pod kontrolou tlmočníka – pomalšie ako strojový kód, ale spoľahlivejšie z hľadiska možného chybného dopadu na operačný systém a poskytuje pohodlnejší režim na identifikáciu prípadných chýb.

Tip 3: Desať najlepších postupov Dana Applemana pre robustné programovanie API vo VB

Používanie funkcie API vyžaduje starostlivejšie programovanie pomocou niektorých nie príliš známych metód volania procedúr (v porovnaní s VB). Ďalej sa budeme týmito otázkami neustále zaoberať. A teraz uvádzame zhrnutie rád na túto tému, ktoré sformuloval Dan Appleman (ich prvá verzia sa objavila už v roku 1993) s niektorými našimi dodatkami a komentármi.

1. Pamätajte na ByVal. Najčastejšou chybou pri prístupe k funkciám API a DLL je nesprávne použitie kľúčového slova ByVal: buď ho zabudnú zadať, alebo naopak vložia, keď to nie je potrebné.

Tieto príklady ukazujú vplyv operátora ByVal na odovzdávanie parametrov

Typ parametra S ByVal Bez ByVal
Celé číslo Vloží 16-bitové celé číslo do zásobníka Vložená do zásobníka 32-bitová adresa 16-bitového celého čísla
Dlhé Do zásobníka sa vloží 32-bitové celé číslo Vložená do zásobníka 32-bitová adresa 32-bitového celého čísla
Reťazec Reťazec sa skonvertuje do formátu používaného v C (údaje a ukončovací nulový bajt). 32-bitová adresa nového riadku sa vloží do zásobníka VB riadkový deskriptor sa vloží do zásobníka. (Takéto deskriptory nikdy nepoužíva samotné Windows API a sú rozpoznané iba v knižniciach DLL implementovaných špeciálne pre VB.)

Tu je potrebné pripomenúť, že parametre sa odovzdávajú v akomkoľvek programovacom systéme, vrátane VB, dvoma hlavnými spôsobmi: odkazom (ByRef) alebo hodnotou (ByVal). V prvom prípade sa odovzdá adresa premennej (táto možnosť sa štandardne používa vo VB), v druhom - jej hodnota. Zásadný rozdiel je v tom, že pomocou referencie sa volajúcemu programu vráti zmenená hodnota odovzdaného parametra.

Ak chcete na to prísť, spustite experiment pomocou nasledujúcich programov:

Dim v As Integer v = 2 Volanie MyProc (v) MsgBox „v =“ & v Sub MyProc (v As Integer) v = v + 1 End Sub

Spustením tohto príkladu dostanete správu s hodnotou premennej rovnajúcou sa 3. Faktom je, že v tomto prípade je adresa premennej v, fyzicky vytvorená vo volajúcom programe, odovzdaná podprogramu MyProc. Teraz zmeňte popis postupu na

Sub MyProc (ByVal v. As Integer)

Výsledkom je, že pri vykonávaní testu dostanete v = 2, pretože procedúre sa odovzdá iba pôvodná hodnota premennej - výsledok operácií s ňou vykonaných sa nevracia volajúcemu programu. Režim prevodu podľa hodnoty je možné zmeniť aj pomocou operátora hovoru nasledovne:

Sub MyProc (v As Integer) ... Call MyProc ((v)) '(v) - zátvorky označujú prenos podľa hodnoty _ mód.

Keď sa však odkazuje na interné postupy VB, kľúčové slovo ByVal nie je v príkaze Call povolené – namiesto toho sa používajú zátvorky. Existuje na to vysvetlenie.

V klasickom prípade (C, Fortran, Pascal) rozdiel medzi ByRef a ByVal závisí od toho, čo sa presne vloží do zásobníka výmeny dát – adresa premennej alebo jej hodnota. V Basic sa historicky používa verzia softvérovej emulácie ByVal - adresa je vždy v zásobníku, ale iba pri odovzdaní hodnotou sa na to vytvorí dočasná premenná. Na rozlíšenie týchto dvoch možností (Classic a Basic) sa používajú rôzne spôsoby popisu režimu ByVal. Všimnite si, že emulácia režimu ByVal vo VB poskytuje vyššiu spoľahlivosť programu: zámenou formy volania programátor len riskuje, že sa volajúcemu programu vráti (alebo nevráti) opravená hodnota premennej. V „klasickej“ verzii môže takýto zmätok viesť k fatálnej chybe pri vykonávaní procedúry (napríklad keď sa namiesto adresy pamäte použije premenná hodnota rovnajúca sa povedzme nule).

Funkcie DLL sú implementované podľa "klasických" princípov, a preto vyžadujú povinný popis spôsobu výmeny údajov s každým z argumentov. Práve na tento účel slúžia deklarácie funkcií cez popis Declare (presnejšie zoznam odovzdaných argumentov). Najčastejšie sa odovzdávanie parametrov do funkcie Windows API alebo DLL vykonáva pomocou kľúčového slova ByVal. Navyše ho možno špecifikovať v príkaze Declare aj priamo pri volaní funkcie.

Dôsledky nesprávneho odovzdávania parametrov sa dajú ľahko predvídať. Ak dostanete zjavne neplatnú adresu, zobrazí sa vám výzva so správou GPF (General Protection Fault). Ak funkcia dostane hodnotu, ktorá sa zhoduje s platnou adresou, potom sa funkcia API dostane do oblasti niekoho iného (napríklad do jadra Windowsu) so všetkými z toho vyplývajúcimi katastrofálnymi následkami.

2. Skontrolujte typ odovzdaných parametrov. Rovnako dôležitý je aj správny počet a typ odovzdaných parametrov. Argumenty deklarované v Declare sa musia zhodovať s očakávanými parametrami vo funkcii API. Najčastejšia chyba pri odovzdávaní parametrov súvisí s rozdielom medzi reťazcami NULL a nulovou dĺžkou – nezabudnite, že nejde o to isté.

3. Skontrolujte typ návratu.

VB je celkom tolerantný voči typovým nezhodám v návratových hodnotách funkcií, pretože číselné hodnoty sa zvyčajne vracajú cez registre a nie cez zásobník. Nasledujúce pravidlá vám pomôžu určiť správnu hodnotu vrátenú funkciou API:

  • Funkcia DLL, ktorá nevracia hodnotu (podobne ako void v 'C'), musí byť deklarovaná ako VB Sub.
  • Funkcia API, ktorá vracia celočíselné hodnoty (Integer alebo Long), môže byť definovaná ako Sub alebo Funkcia, ktorá vracia hodnotu vhodného typu.
  • žiadna z funkcií API nevracia čísla s pohyblivou rádovou čiarkou, ale niektoré knižnice DLL môžu vrátiť tento typ údajov.

4. Konštrukciu "Ako každý" používajte veľmi opatrne. Mnoho funkcií Windows API má schopnosť akceptovať parametre rôznych typov a používať konštrukciu As Any (typ je interpretovaný v závislosti od hodnoty ostatných odovzdaných parametrov).

Dobrým riešením by v tomto prípade bolo použitie viacerých aliasov funkcií, vytvorenie dvoch alebo viacerých deklarácií pre rovnakú funkciu, pričom každý z popisov špecifikuje parametre špecifického typu.

5. Nezabudnite inicializovať reťazce. V rozhraní Win API je veľa funkcií, ktoré vracajú informácie načítaním údajov do vyrovnávacích pamätí reťazcov odovzdaných ako parameter. Zdá sa, že vo vašom programe robíte všetko správne: nezabudnite na ByVal, správne odovzdajte parametre funkcii. Systém Windows však nemôže skontrolovať, aká veľká je pamäť alokovaná pre reťazec. Veľkosť riadku musí byť dostatočne veľká, aby sa doň zmestili všetky údaje, ktoré je možné doň umiestniť. Je zodpovednosťou programátora VB rezervovať vyrovnávaciu pamäť správnej veľkosti.

Treba si uvedomiť, že v 32-bitovom Windowse sa pri použití reťazcov prevod vykonáva z Unicode (dvojbajtové kódovanie) na ANSI (jednobajtové) a naopak, pričom sa zohľadňujú národné nastavenia systému. Preto je niekedy vhodnejšie použiť bajtové polia namiesto reťazcových premenných na rezervovanie vyrovnávacích pamätí. (Viac o tom nižšie.)

Funkcie Win API vám najčastejšie umožňujú definovať maximálnu veľkosť bloku sami. Najmä na to je niekedy potrebné zavolať inú funkciu API, ktorá „vyzve“ veľkosť bloku. Napríklad GetWindowTextLength vám umožňuje určiť veľkosť riadku, ktorý je potrebný na umiestnenie nadpisu okna, ktorý získava funkcia GetWindowText. V tomto prípade sa systém Windows postará o to, aby ste neodišli do zahraničia.

6. Uistite sa, že používate Option Explicit.

7. Starostlivo skontrolujte hodnoty parametrov a návratové hodnoty. VB má dobré možnosti typovej kontroly. To znamená, že keď sa pokúsite odovzdať neplatný parameter funkcii VB, najhoršia vec, ktorá sa môže stať, je, že dostanete chybu z VB. Ale tento mechanizmus, žiaľ, nefunguje pri prístupe k funkciám Windows API.

Windows 9x má vylepšené overovanie parametrov pre väčšinu funkcií API. Prítomnosť chyby v údajoch teda zvyčajne nespôsobuje fatálnu chybu, no nie je také jednoduché určiť, čo ju spôsobilo.

Tu môžete odporučiť použitie niekoľkých spôsobov ladenia tohto typu chyby:

  • použite jednokrokový režim ladenia alebo príkaz Debug.Print na kontrolu každého podozrivého volania API. Skontrolujte výsledky týchto hovorov, aby ste sa uistili, že je všetko v rámci normálnych limitov a funkcia sa dokončila správne;
  • použite ladiaci program Windows, ako je CodeView a ladiaci program Windows (dostupný v súprave Windows SDK). Tieto nástroje dokážu odhaliť chyby parametrov a prinajmenšom určiť, ktorá funkcia API spôsobuje chybu;
  • použiť ďalšie nástroje tretích strán na kontrolu typov parametrov a platnosti ich hodnôt. Takéto nástroje dokážu nielen nájsť chyby parametrov, ale dokonca ukázať na riadok kódu VB, kde sa chyba vyskytla.

Okrem toho je nevyhnutné skontrolovať výsledok funkcie API.

8. Pamätajte, že celé čísla vo VB a Windows nie sú to isté. V prvom rade si treba uvedomiť, že výraz „Integer“ vo VB znamená 16-bitové číslo, v dokumentácii Win 32 - 32-bitové. Po druhé, celé čísla (Integer a Long) vo VB sú hodnoty so znamienkom (to znamená, že jedna číslica sa používa ako znamienko, zvyšok ako mantisa čísla), v systéme Windows sa používajú iba nezáporné čísla. Túto okolnosť je potrebné mať na pamäti, keď vytvárate odovzdávaný parameter pomocou aritmetických operácií (napríklad výpočet adresy pridaním nejakého základu a posunu). Štandardné aritmetické funkcie VB nie sú na to vhodné. Ako byť v tomto prípade, budeme hovoriť samostatne.

9. Pozorne si všímajte názvy funkcií. Na rozdiel od Win16 sú názvy všetkých funkcií Win32 API citlivé na presné použitie veľkých a malých písmen (čo nebol prípad Win16). Ak niekde použijete malé písmeno namiesto veľkého písmena alebo naopak, požadovaná funkcia nebude nájdená. Tiež sa uistite, že príponu A alebo W používate správne vo funkciách, ktoré používajú reťazcové parametre. (Viac o tom nájdete nižšie.)

10. Ukladajte si prácu častejšie. Chyby súvisiace s nesprávnym používaním DLL a Win API môžu viesť k abnormálnemu ukončeniu prostredia VB a možno aj celého operačného systému. Pred spustením testu musíte zabezpečiť, aby bol kód, ktorý napíšete, uložený. Najjednoduchšie je nastaviť režim automatického nahrávania modulov projektu pred spustením projektu v prostredí VB.

Po prečítaní predchádzajúceho tipu si možno pomyslíte, že používanie funkcií Win API je riskantná záležitosť. Do istej miery je to pravda, ale len v porovnaní s bezpečným programovaním, ktoré poskytuje samotné VB. No pri ich šikovnej aplikácii a znalosti možných nástrah je toto riziko minimálne. Okrem toho je často jednoducho nemožné úplne opustiť používanie Win API - stále budú potrebné pre akýkoľvek seriózny vývoj.

Okrem toho sme už skôr spomenuli úskalia pre širokú triedu knižníc DLL. V prípade Win API je všetko oveľa jednoduchšie, keďže forma volania týchto funkcií je tu jednoznačne zjednotená. V tomto prípade je potrebné mať na pamäti nasledujúce hlavné body:

  1. Funkcie Win32 API sú len funkcie, teda procedúry typu Function (v Win16 API bolo veľa Subs). Všetky tieto funkcie sú typu Long, takže ich popisy sú napísané v nasledujúcom tvare: Declare Function name ... As Long ‘typ funkcie _ je definovaný explicitne

    Deklarovať Názov funkcie a „typ funkcie _ je definovaný s príponou

    Volanie funkcie API vyzerá takto:

Výsledok & = ApiName & ([ Zoznam argumentov]
  1. Návratovou hodnotou funkcie je najčastejšie kód ukončenia operácie. Navyše nenulová hodnota znamená v tomto prípade normálne dokončenie, nulová hodnota znamená chybu. Zvyčajne (ale nie vždy) môžete objasniť povahu chyby volaním funkcie GetLastError. Popis tejto funkcie vyzerá takto: Declare Function GetLastError & Lib “kernel32” ()

    POZOR! Pri práci vo VB je lepšie použiť vlastnosť LastDLLError objektu Err na získanie hodnoty kvalifikovaného chybového kódu, pretože niekedy VB resetuje funkciu GetLastError medzi volaním API a pokračovaním vykonávania programu.

    Kód, ktorý vracia GelLastError, môžete interpretovať pomocou konštánt napísaných v súbore API32.TXT, pričom názvy začínajú príponou ERROR_.

    Najčastejšie chyby majú nasledujúce kódy:

    • ERROR_INVALID_HANDLE = 6 & - neplatný popisovač
    • ERROR_CALL_NOT_IMPLEMENTED = 120 & - volanie vo Windows 9x funkcia dostupná len pre Windows NT
    • ERROR_INVALID_PARAMETER = 87 & - neplatná hodnota parametra

    Mnoho funkcií však vracia hodnotu niektorého požadovaného parametra (napríklad OpenFile vracia hodnotu deskriptora súboru). V takýchto prípadoch je chyba identifikovaná nejakou inou špeciálnou hodnotou Return & value, najčastejšie 0 alebo –1.

  2. Win32 API používajú prísne fixné spôsoby odovzdávania najjednoduchších dátových typov. a) ByVal ... Tak dlho

    Dlhé premenné vykonávajú aspoň 80 % odovzdania argumentu. Všimnite si, že argument vždy nasleduje kľúčové slovo ByVal, čo okrem iného znamená, že sa vykonáva jednosmerný prenos dát – z VB programu do API funkcie.

    B) ByVal ... Ako reťazec

    Tento typ prenosu údajov sa tiež vyskytuje pomerne často a tiež s argumentom vždy Používa sa ByVal. Pri volaní funkcie API sa adresa reťazca zapíše do zásobníka, takže v tomto prípade je možná obojsmerná výmena údajov. Pri práci so strunami je potrebné zvážiť niekoľko úskalí.

    Po prvé, vo volajúcom programe je rezervovaná pamäť pre reťazec, takže ak funkcia API vypĺňa reťazce, musíte pred jej volaním vytvoriť reťazec požadovanej veľkosti. Napríklad funkcia GetWindowsDirectory vráti cestu k adresáru Windows, ktorý by podľa definície nemal byť dlhší ako 144 znakov. Volanie tejto funkcie by teda malo vyzerať asi takto:

    WinPath $ = Medzera $ (144) 'rezervovať reťazec _ 144 znakov Výsledok & = GetWindowsDirectory & (WinTath $, 144) _' vyplniť vyrovnávaciu pamäť 'Výsledok & - skutočný počet znakov v _ názve adresára WinPath $ = Left $ (WinPath, Výsledok a)

    Druhým problémom je, že pri volaní funkcie API sa pôvodný reťazec skonvertuje na nejakú internú reprezentáciu a pri ukončení funkcie je to naopak. Ak v časoch Win16 táto operácia pozostávala len z pridania nulového bajtu na koniec reťazca, tak s príchodom Win32 to bolo doplnené o transformáciu dvojbajtového kódovania Unicode na ANSI a naopak. (Toto bolo podrobne rozobraté v článku „Funkcie práce s reťazcovými premennými vo VB“, ComputerPress 10'99 a 01'2000). Zatiaľ si všimnime, že pomocou konštrukcie ByVal ... As String môžete vymieňať reťazce iba so znakovými údajmi.

    B) ... Ako každý

    To znamená, že do zásobníka sa vloží nejaká adresa vyrovnávacej pamäte, ktorej obsah bude interpretovať funkcia API napríklad v závislosti od hodnoty iných argumentov. Avšak As Any možno použiť iba v príkaze Declare – pre konkrétne volanie funkcie musí byť ako argument definovaná konkrétna premenná.

    D) ... Ako UserDefinedType

    Tento dizajn sa tiež často používa, keď je potrebné vymieňať dáta (spravidla v oboch smeroch) pomocou nejakej štruktúry. V skutočnosti je táto konštrukcia akousi konkrétnou implementáciou formy prenosu As Any, ale v tomto prípade je funkcia nakonfigurovaná pre pevnú štruktúru.

    Tvar dátovej štruktúry je určený špecifickou funkciou API a je na zodpovednosti programátora, aby ju správne popísal a rezervoval vo volajúcom programe. Tento dizajn vždy používaný bez slová ByVal, čiže v tomto prípade sa vykoná prevod odkazom - adresa premennej sa zapíše do zásobníka.

Príklad volania funkcie API

Vyššie uvedené ilustrujme na príklade použitia dvoch užitočných funkcií na prácu so súbormi – lopen a lread, ktoré sú popísané nasledovne:

Deklarovať funkciu lopen Lib „kernel32“ _ Alias ​​​​„_lopen“ (_ ByVal lpFileName ako reťazec, _ ByVal wReadWrite ako dlhý) ako dlho deklarovať funkciu lread Lib „kernel32“ _ alias ​​„_lread“ (_ ByVal hFile ako dlhý, lpBuffer As Any, _ ByVal wBytes As Long) As Long

Vo VB sú ich náprotivky – v tomto prípade presné – príkazy Open a Get (pre binárny režim). Okamžite venujme pozornosť použitiu kľúčového slova Alias ​​v deklarácii funkcie - to je presne ten prípad, keď sa bez neho nezaobídete. Názvy skutočných funkcií v knižnici začínajú podčiarkovníkom (typický štýl C), čo nie je povolené vo VB.

Operácia otvorenia súboru môže vyzerať takto:

Const INVALID_HANDLE_VALUE = -1 'neplatná _ hodnota deskriptora lpFileName $ = “D: \ calc.bas”' názov súboru wReadWrite & = 2 'režim čítania a zápisu hFile & = lopen (lpFileName $, wReadWrite &) _' definujte deskriptor súboru If hFile & = INVALID_HANDLE_VALUE Potom _ 'chyba pri otváraní súboru' objasniť kód chyby CodeError & = Err.LastDllError 'CodeError & = GetLastError _' táto konštrukcia nefunguje End If

Tu je potrebné poznamenať dva body:

  • ako hodnotu funkcie dostaneme hodnotu deskriptora súboru. Chyba zodpovedá hodnote –1;
  • práve v tomto prípade nefunguje volanie funkcie GetLastError - aby sme získali zadanú chybovú hodnotu, obrátili sme sa na objekt Err (o možnosti takejto situácie sme hovorili vyššie).

Potom si môžete prečítať obsah súboru, ale to predpokladá, že programátor musí trochu rozumieť jeho štruktúre (rovnako ako pri práci s ľubovoľnými binárnymi súbormi). V tomto prípade môže volanie funkcie lread vyzerať takto:

Dim MyVar As Single wBytes = lread (hFile &, MyVar, Len (MyVar) 'prečítané skutočné číslo, 4 bajty' wBytes - počet skutočne načítaných údajov,' -1 - chyba ... Type MyStruct x As Single i As Integer End Typ Dim MyVar As MyStruct wBytes = lread (hFile &, MyVar, Len (MyVar)) 'čítanie dátovej štruktúry, 6 bajtov

Všimnite si znova: druhý argument funkcie sa odovzdáva odkazom, zvyšok sa odovzdáva hodnotou.

Dim MyVar As String MyVar = Medzera $ (10) 'rezervovať premennú pre 10 znakov wBytes = lread (hFile &, ByVal MyVar, Len (MyVar))' prečítať reťazec znakov, 10 znakov

Tu môžete vidieť dôležitý rozdiel oproti predchádzajúcemu príkladu – reťazcová premenná je nevyhnutne sprevádzaná kľúčovým slovom ByVal.

Čítanie obsahu súboru v poli (pre jednoduchosť použijeme jednorozmerné bajtové pole) sa vykonáva takto:

Dim MyArray (1 až 10) As Byte wBytes = lread (hFile &, MyArray (1), _ Len (MyArray (1)) * 10) 'čítanie 10 prvkov poľa

Zadaním prvého prvku poľa ako argumentu odovzdávame adresu začiatku oblasti pamäte vyhradenej pre pole. Je zrejmé, že akýkoľvek fragment poľa možno vyplniť týmto spôsobom:

WBytes = lread (hFile &, MyArray (4), _ Len (MyArray (1)) * 5) 'čítanie prvkov poľa 4 až 8

Tip 5. Použite alias pre Gears a parametre Ako každý

Tu na základe predchádzajúceho príkladu odhalíme podstatu štvrtého tipu Dana Applemana.

Pri práci s funkciou lread nezabúdajte, že pri prístupe pomocou reťazcovej premennej musíte použiť kľúčové slovo ByVal (inak sa nedá vyhnúť hláseniu o nelegálnej operácii). Pre istotu môžete urobiť dodatočný špeciálny popis tej istej funkcie, aby fungovala iba s reťazcovými premennými:

Deklarovať funkciu lreadString Lib „kernel32“ _ Alias ​​​​„_lread“ (_ ByVal hFile As Long, ByVal lpBuffer As Long, _ ByVal wBytes As Long) As Long

Pri práci s týmto popisom už nemusíte pri prístupe špecifikovať ByVal:

WBytes = lreadString (hFile &, MyVarString, _ Len (MyVarString))

Zdá sa, že syntax príkazu Declare vám umožňuje vytvoriť podobný špeciálny popis pre pole:

Deklarovať funkciu lreadString Lib „kernel32“ Alias ​​​​„_lread“ (_ ByVal hFile As Long, lpBuffer () As Byte, _ ByVal wBytes As Long) As Long

Avšak odvolanie

WBytes = lreadArray (hFile &, MyArray (), 10)

nevyhnutne vedie k fatálnej chybe programu.

Toto je pokračovanie rozhovoru o zvláštnostiach spracovania reťazcových premenných vo Visual Basic: VB používa dvojbajtové kódovanie Unicode, Win API - jednobajtové ANSI (a s formátom akceptovaným v C - s nulovým bajtom na koniec). Preto pri použití reťazcových premenných ako argumentu sa konverzia z Unicode na ANSI vždy automaticky vykoná pri volaní funkcie API (presnejšie funkcie DLL) a spätná konverzia sa vykoná pri návrate.

Vyvodenie z toho je jednoduché: pomocou premenných typu String si môžete vymieňať znakové údaje, ale nemôžete ich použiť na výmenu ľubovoľných binárnych informácií (ako to bolo v prípade 16-bitových verzií VB). V druhom prípade je lepšie použiť jednorozmerné bajtové pole.

Ako viete, typ String možno použiť na popis vlastnej štruktúry. V tejto súvislosti treba pamätať na nasledovné:

  • Je kategoricky nemožné použiť nasledujúcu konštrukciu na odkazovanie na Win API: Type MyStruct x As Single s As String 'reťazec s premennou dĺžkou End Type

    V prípade reťazca s premennou dĺžkou je reťazcový deskriptor odovzdaný ako súčasť štruktúry so všetkými z toho vyplývajúcimi dôsledkami vo forme chyby vykonávania programu.

  • Ako prvok štruktúry môžete použiť reťazec s pevnou dĺžkou: Typ MyStruct x As Single s As String * 8 'reťazec s pevnou dĺžkou End Type

V tomto prípade sa vykoná zodpovedajúca konverzia kódovania.

A posledná poznámka: v žiadnom prípade nemôžete použiť pole reťazcových premenných (pevnej aj premennej dĺžky) pri volaní funkcie API. V opačnom prípade bude zaručený vzhľad „nezákonnej operácie“.

Je veľmi pravdepodobné, že sa dostanete do situácie, kedy si budete musieť napísať vlastné funkcie DLL. Potreba tohto sa nevyhnutne objaví, ak používate technológiu zmiešaného programovania - použitie dvoch alebo viacerých programovacích jazykov na implementáciu jednej aplikácie.

V tejto súvislosti si všimnite, že zmiešané programovanie je celkom bežné pre pomerne zložité aplikácie. Každý jazyk (presnejšie programovací systém založený na jazyku) má svoje silné a slabé stránky, takže je celkom logické využívať výhody rôznych nástrojov na riešenie rôznych problémov. Napríklad VB - na vytváranie používateľského rozhrania, C - na efektívny prístup k systémovým zdrojom, Fortran - na implementáciu numerických algoritmov.

Názor autora je taký, že každé seriózne programovanie vyžaduje, aby vývojár vlastnil aspoň dva nástroje. Samozrejme, v moderných podmienkach jasnej deľby práce je veľmi ťažké byť vynikajúcim odborníkom aj v dvoch systémoch, takže schéma „hlavného a pomocného jazyka“ je logickejšia. Ide o to, že aj povrchná znalosť „pomocného“ jazyka (písanie pomerne jednoduchých postupov) môže výrazne zvýšiť efektivitu „hlavného“ jazyka. Všimnite si, že znalosť VB, aspoň ako pomocná, je dnes takmer povinnou požiadavkou profesionálneho programátora. Mimochodom, v časoch DOSu pre každého programátora, vrátane Basicu, bolo veľmi žiaduce poznať základy Assemblera.

Tak či onak, ale aj v podmienkach skupinovej práce, keď sa každý programátor zaoberá vlastným špecifickým podnikaním, by všetci účastníci projektu mali mať predstavu o vlastnostiach procedurálneho rozhrania v rôznych jazykoch. A vedieť, že mnohé programovacie systémy (vrátane VB) umožňujú okrem predvoleného rozhrania použiť aj iné, rozšírené metódy odkazovania na procedúry, ktoré umožňujú prispôsobiť rozhranie inému jazyku.

Pri štúdiu interprocedurálneho rozhrania by ste mali venovať pozornosť nasledujúcim možným úskaliam:

  • Rôzne jazyky môžu používať rôzne konvencie na zapisovanie identifikátorov. Napríklad podčiarkovník sa často používa na začiatku názvu procedúry, čo je vo VB zakázané. Tento problém sa dá ľahko vyriešiť použitím kľúčového slova Alias ​​v príkaze Declare (pozri príkladový tip 2.3).
  • Je možné použiť inú postupnosť zápisu odovzdaných argumentov do zásobníka. Napríklad v časoch DOSu (úprimne neviem, ako to teraz vyzerá vo Windows), C písalo argumenty od konca zoznamu, iné jazyky (Fortran, Pascal, Basic) - od začiatku.
  • Štandardne sa používajú rôzne princípy odovzdávania parametrov - odkazom alebo hodnotou.
  • Rôzne princípy ukladania reťazcových premenných. Napríklad v C (rovnako ako vo Fortrane a Pascale) je dĺžka reťazca definovaná nulovým bajtom na jeho konci, zatiaľ čo v Basic je dĺžka zapísaná explicitne v deskriptore reťazca. Samozrejme, treba pamätať na možnosť použitia rôznych kódovaní znakov.
  • Pri prenose viacrozmerných polí nezabúdajte, že existujú rôzne možnosti prevodu viacrozmerných štruktúr na jednorozmerné (začínajúc od prvého indexu alebo od posledného, ​​vo vzťahu k dvojrozmerným poliam – „po riadkoch“ alebo „po stĺpcoch“).

Vzhľadom na toto všetko je možné sformulovať nasledujúce odporúčania:

  • Použite najjednoduchšie, osvedčené spôsoby odovzdávania argumentov funkciám DLL. Dobrým príkladom sú štandardy prijaté pre Win API.
  • Za žiadnych okolností neodovzdávajte polia reťazcových premenných.
  • Používajte veľmi opatrné odovzdávanie jednoduchých reťazcových premenných a viacrozmerných polí.
  • Nezabudnite špeciálne skontrolovať funkčnosť mechanizmu na odovzdávanie argumentov do a z volanej procedúry. Napíšte vlastný test na testovanie prenosu údajov. Samostatne skontrolujte, či je každý argument odovzdaný správne. Napríklad, ak máte procedúru s viacerými argumentmi, najprv skontrolujte, či je každý parameter správne odovzdaný pre variant s jedným argumentom a až potom pre celý zoznam.

Čo ak však funkcia DLL už bola napísaná napríklad vo Fortrane, ale jej vstupné rozhranie nezodpovedá vyššie uvedeným štandardom VB? Tu sú dva tipy. Najprv napíšte testovaciu funkciu DLL a pomocou nej sa pokúste nájsť správne volanie z programu VB metódou pokus-omyl. Po druhé: v rovnakom Fortrane napíšte procedúru adaptéra, ktorá by poskytla jednoduché rozhranie medzi VB a funkciou DLL s prevodom jednoduchých dátových štruktúr na zložité (napríklad prevodom viacrozmerného poľa bajtov na pole reťazcov).

Takže: použite funkcie DLL. Ale buďte v strehu...

ComputerPress 9 "2000

Konečne! Konečne! Dnes začneme vytvárať plnohodnotné okno Windows. Zbohom úbohá konzola!!!

V tomto bode by ste už mali mať dobré znalosti syntaxe C++, byť schopní pracovať s vetvami a slučkami a dobre rozumieť fungovaniu funkcií. Ak ste zvládli námornú bitku, môžete predpokladať, že ste sa toto všetko naučili.

Maďarská notácia

Všetok kód, s ktorým sa vo WinAPI stretneme, je napísaný v maďarskej podobe. Toto je kódovacia konvencia.

V tomto prípade je začiatočné písmeno typu umiestnené pred názvom premennej. Všetky slová v názvoch premenných a funkcií začínajú veľkým písmenom.

Tu sú niektoré predpony:

b - premenná typu bool.
l je dlhá celočíselná premenná.
w - od slova (slova) - 16 bitov. Premenná typu unsigned short.
dw - z dvojitého slova (dvojitého slova) - 32 bitov. Premenná typu unsigned long.
sz - reťazec ukončený nulou. Len obyčajná šnúrka, ktorú sme používali stále.
p alebo lp je ukazovateľ (od ukazovateľa). lp (od dlhého ukazovateľa) - tieto ukazovatele prešli z minulosti. Teraz lp a p znamenajú to isté.
h - deskriptor (od rukoväte).

Napríklad ukazovateľ by bol pomenovaný takto:

void * pData;

Túto formu záznamu používa spoločnosť Microsoft. Mnoho ľudí tento spôsob pomenovania premenných kritizuje. Ale tieto druhy vecí (kódovacie konvencie) sú životne dôležité vo veľkých spoločnostiach.

Dovoľte mi pripomenúť, že konštantné identifikátory zvyčajne pozostávajú iba z veľkých písmen: WM_DESTROY. WM_DESTOY je 2, konštanta je definovaná pomocoudefin.

Okrem toho winAPI používa veľa prepísaných typov. Tu na tejto stránke - http://msdn.microsoft.com/en-us/library/aa383751(VS.85).aspx nájdete popisy všetkých typov Windows (v angličtine).

A ešte jedna vec, ktorú sme neanalyzovali. Ukazovatele majú často priradené hodnoty NULL. Predstavte si to len ako 0 a ukazovatele, ktorým je priradená hodnota NULL (nula), neukazujú na žiadny kus pamäte.

Windows API (WinAPI)

Všetky programy Windows používajú špeciálne programovacie rozhranie WinAPI. Je to súbor funkcií a štruktúr C, vďaka ktorým je váš program kompatibilný so systémom Windows.

Windows API má obrovské možnosti pre prácu s operačným systémom. Dalo by sa dokonca povedať – neobmedzené.

Nepokryjeme ani jedno percento všetkých možností WinAPI. Pôvodne som chcel vziať viac materiálu, ale trvalo by to príliš dlho a uviaznuť v bažine WinAPI, DirectX "a dostaneme sa za pár rokov. Popis WinAPI bude trvať dve lekcie (vrátane tejto). nimi sa budeme zaoberať iba aplikačným rámcom pre Windows.

Program Windows, rovnako ako program DOS, má hlavnú funkciu. Táto funkcia sa tu nazýva WinMain.

Funkcia WinMain

Program Windows pozostáva z nasledujúcich častí (toto všetko sa deje vo WinMain):

Vytvorenie a registrácia triedy okien. Nenechajte sa zamieňať s triedami C ++. WinAPI je napísaný v C, neexistujú žiadne triedy v našom obvyklom zmysle slova.
Vytvorenie okna programu.
Hlavná slučka, v ktorej sa spracovávajú správy.
Spracovanie programových správ v procedúre okna. Postup okna je normálna funkcia.
Tieto štyri body sú základom programu Windows. Počas tejto a nasledujúcej lekcie sa tomu všetkému budeme podrobne venovať. Ak ste zmätení v popise programu, vráťte sa k týmto bodom.

Teraz sa pozrime na to všetko podrobne:

WinAPI: Štruktúra WNDCLASS

Najprv musíte vytvoriť a vyplniť premennú štruktúry WNDCLASS a na základe nej zaregistrovať triedu okna.

Takto vyzerá táto štruktúra:

c ++ kód typedef struct (štýl UINT; // štýl okna WNDPROC lpfnWndProc; // ukazovateľ na procedúru okna int cbClsExtra; // extra bajty po triede. Vždy vložte 0 int cbWndExtra; // extra bajtov za inštanciu okna. Vždy vložte 0 HINSTANCE hInstance ; / / inštancia aplikácie. Odovzdané ako parameter vo WinMain HICON hIcon; // ikona aplikácie HCURSOR hCursor; // kurzor aplikácie HBRUSH hbrBackground; // farba pozadia LPCTSTR lpszMenuName; // názov ponuky LPCTSTR lpszClassName; // názov triedy) WNDCLASS, * PWNDCLASS ;

Štruktúra WNDCLASS ako súčasť WinAPI určuje základné vlastnosti vytváraného okna: ikony, typ kurzora myši, či má okno menu, ktorá aplikácia bude okno vlastniť...

Po vyplnení tejto štruktúry z nej môžete zaregistrovať triedu okna. Nehovoríme o triedach ako v C ++. Skôr môžete predpokladať, že trieda okna je takouto šablónou, zaregistrovali ste ju v systéme a teraz môžete na základe tejto šablóny vytvoriť niekoľko okien. A všetky tieto okná budú mať vlastnosti, ktoré ste definovali v premennej štruktúry WNDCLASS.

WinAPI: Funkcia CreateWindow

Po zaregistrovaní triedy okna sa na jej základe vytvorí hlavné okno aplikácie (teraz sme prešli k druhému bodu). To sa vykonáva pomocou funkcie CreateWindow. Má nasledujúci prototyp:

c ++ kód HWND CreateWindow (LPCTSTR lpClassName, // názov triedy LPCTSTR lpWindowName, // názov okna (zobrazený v nadpise) DWORD dwStyle, // štýl okna int x, // horizontálna súradnica od ľavého okraja obrazovky int y, // vertikálne súradnica od horného okraja obrazovky int nWidth, // šírka okna int nHeight, // výška okna HWND hWndParent, // rodičovské okno HMENU hMenu, // rukoväť ponuky HINSTANCE hInstance, // inštancia aplikácie LPVOID lpParam // parameter; vždy nastavený na NULL);

Ak sú základné vlastnosti okna nastavené v triede okna (štruktúra WNDCLASS), tu sú pre každé okno konkrétnejšie: veľkosť okna, súradnice ...

Táto funkcia vráti do okna rukoväť. Pomocou rukoväte sa môžete odvolať na okno, je to ako identifikátor.

Všimnite si, že je tu veľa nových typov. V skutočnosti sú všetky staré, len predefinované. Napríklad: HWND je prepísanie typu HANDLE, čo je zase prepísanie PVOID, čo je zase prepísanie void *. Ako hlboko je pravda pochovaná! Napriek tomu je typ HWND ukazovateľom neplatnosti.

Okno sa skladá z niekoľkých častí. Takmer v každom programe uvidíte: názov okna, systémové menu (ak kliknete na ikonu aplikácie v ľavej hornej časti okna), tri systémové tlačidlá pre prácu s oknom: minimalizovať, rozbaliť na celú obrazovku a zavrieť . V aplikácii je tiež takmer vždy menu. To druhé určite mať nebudeme. A samozrejme väčšinu okna zaberá tzv. klientskej oblasti, v ktorej používateľ zvyčajne pracuje.

Toto je pre režim v okne. Dlho budeme cvičiť s DiectX v okne – režim na celú obrazovku nevyužijeme.

Spracovanie správ

Hlavným rozdielom medzi všetkými našimi predchádzajúcimi programami a programami Windows je spracovanie správ.

Napríklad, keď používateľ stlačí kláves na klávesnici, vygeneruje sa správa, že bol stlačený kláves. Táto správa potom prejde do aplikácie, ktorá bola aktívna, keď používateľ stlačil kláves.

Tu máme udalosť (udalosť) - stlačila sa klávesa.

Udalosťou môže byť: pohyb kurzora myši, zmena zamerania aplikácie, stlačenie klávesu na klávesnici, zatvorenie okna. Udalostí je veľa. vysoko! V operačnom systéme sa môžu vyskytnúť desiatky udalostí za sekundu.

Keď teda dôjde k udalosti, operačný systém vytvorí správu: stlačil sa taký a taký kláves, zmenili sa súradnice kurzora myši, otvorilo sa nové okno.

Správy môže generovať operačný systém aj rôzne aplikácie.

Správa má štruktúru a vyzerá takto:

c ++ kód typedef struct tagMSG (HWND hwnd; // okno, ktoré dostane túto správu UINT; // kód správy WPARAM wParam; // parameter LPARAM lParam; // čas parametra DWORD; // čas, kedy sa správa objavila POINT pt; // súradnice kurzora myši) MSG;

Všimnite si, ako sú štruktúry prepísané s typedef.

Na vytvorenie tejto štruktúry môžete použiť nasledujúci kód:

c ++ kód msg.messgae == 2; // tieto dva riadky sú ekvivalentné, pretože msg.message == WM_DESTROY; // konštanta WM_DESTROY sa rovná dvom

Tu je pole, ktoré obsahuje kód správy (názov správy, je porovnané s konštantou WM_DESTROY. WM - from Windows Message (Windows message). WM_DESTROY je správa, ktorá sa vygeneruje pri zatvorení okna (destroy).

Kódy správ sú definované pomocou konštánt a majú predponu WM_: WM_CLOSE, WM_CREATE atď.

Štruktúra MSG obsahuje typ HWND - od Window Handle (okenná kľučka alebo okenná kľučka). To je taká vec, ktorá „opisuje“ okno. Je to niečo ako identifikátor (názov okna).

Zapamätajte si toto slovo - rukoväť (deskriptor). V systéme Windows sa tento koncept používa veľmi často. Takmer všetky typy Windows, ktoré začínajú na H, sú deskriptory: deskriptor ikony, deskriptor písma, deskriptor inštancie aplikácie. Čo si pamätám, je ich tridsať.

Všetky interakcie medzi aplikáciami v systéme Windows sa vykonávajú pomocou rovnakých deskriptorov okien (HWND).

Je tu ešte jeden dôležitý deskriptor - deskriptor aplikácie (HINSTANCE je prvý parameter WinMain) - ide o jedinečný identifikátor aplikácie, vďaka ktorému si operačný systém nebude môcť pomýliť dva rôzne programy. Je to ako čiarový kód. Pozrieme sa na to neskôr.

Zakaždým, keď používateľ vykoná nejakú akciu, vytvorí sa a vyplní správa: nastaví sa rukoväť okna, ktoré má prijať túto správu, nastaví sa identifikátor správy, vyplnia sa parametre, vyplní sa čas (aktuálny) a súradnice sú označené kurzorom myši (pozri štruktúru).

Táto správa sa potom umiestni do frontu správ operačného systému. Keď sa front dostane k našej správe, odošle sa do požadovaného okna (windows vďaka deskriptorom vie, do ktorého okna má poslať jednotlivé správy). Keď do aplikácie dorazí správa, zaradí sa do frontu správ aplikácie. Hneď ako príde na rad, je spracovaný.

Pozrite, medzi okamihom, keď používateľ vykonal akciu (vyskytla sa udalosť a vygenerovala sa správa) a okamihom, keď program na túto akciu zareagoval (správu program spracoval), dôjde k mnohým udalostiam. Koniec koncov, front správ systému Windows aj front správ aplikácie môže obsahovať veľa správ. V prvom prípade sa môžeme baviť o stovkách, v druhom prípade aspoň o niekoľkých.

Postup v okne (WndProc)

Pokračujeme od momentu, keď sa správa dostala do frontu správ aplikácie. Hneď ako sa k nemu dostane obrat, je spracovaný. Na spracovanie správ musí mať každý program špeciálnu funkciu - procedúru okna. Zvyčajne sa nazýva WndProc (pre Window Procedure). Volanie procedúry okna sa nachádza v hlavnej slučke programu a vykonáva sa pri každej iterácii cyklu.

Správy (vo forme štruktúrnych MSG premenných) spadajú do tejto funkcie vo forme parametrov: handle okna, identifikátor správy a dva parametre. Všimnite si, že polia time a pt sa neprenesú do procedúry okna. To znamená, že správa už bola „analyzovaná“.

Vo vnútri procedúry okna sa nachádza vetva prepínača, v ktorej sa kontroluje identifikátor správy. Tu je príklad jednoduchého postupu v okne (je plne funkčný):

c ++ kód// zatiaľ ignorujte HRESULT a __stdcall. Pozrieme sa na ne neskôr HRESULT __stdcall WndProc (HWND hWnd, správa UINT, WPARAM wParam, LPARAM lParam) (prepínač (správa) (prípad WM_PAINT: // kód spracovania správy WM_PAINT návrat 0; prípad WM_DESTROY: // kód spracovania správy WM_D EST return 0; ) // handler pre všetky ostatné správy)

A posledná je hlavná slučka. Je to veľmi jednoduché. Každá iterácia cyklu kontroluje front správ aplikácie. Ak sa vo fronte správ nachádza správa, vyskočí z frontu. Potom sa v tele cyklu zavolá procedúra okna na spracovanie správy prevzatej z frontu.

To je vo všeobecnosti na dnes všetko. Už teraz je jasné, že program WinAPI je oveľa zložitejší ako program pre DOS. Ako som napísal vyššie, v nasledujúcej lekcii budeme analyzovať kód pracovného programu.

Vytvorte nový projekt ako cvičenie. V okne Nový projekt vyberte šablónu - Win32Project (zatiaľ sme zvolili Win32 Console Application). V jednom z nasledujúcich okien nechajte políčko Empty Project nezačiarknuté a IDE vygeneruje útržok programu.

Ak sa pozorne pozriete na kód súboru project_name.cpp, nájdete tam všetky veci, o ktorých sme hovorili: premennú štruktúry MSG, vyplnenie štruktúry WNDCLASS, vytvorenie okna s funkciou CreateWindow, hlavnú programovú slučku. Okrem toho súbor definuje funkciu WndProc. Spracováva niekoľko správ vo vetvách prepínača: WM_COMMAND, WM_PAINT, WM_DESTROY. Všetko nájdete v súbore.

Okrem toho, čo sme videli, program obsahuje množstvo doplnkového kódu. V budúcom čísle sa pozrieme na kód programu, v ktorom sa vystrihne všetko nepotrebné. Bude to oveľa jednoduchšie a prehľadnejšie ako to, čo generuje IDE.