Serißl - 18. dφl

Mal² ·vod do API

18. Mal² ·vod do API
    18.1 Co je to API
    18.2 Deklarace funkcφ API
    18.3 P°φklad pou₧itφ
    18.4 Chyby


18. Mal² ·vod do API

V dneÜnφm dφle se dozvφte n∞co o technologii, kterß d∞lß z Visual Basicu siln² programovacφ jazyk, dφky kterΘ m∙₧e obstßt v silnΘ konkurenci ostatnφch produkt∙.

18.1 Co je to API

Funkce API (Application Programming Interface) jsou funkce, kterΘ jsou obsa₧eny v knihovnßch DLL (Dynamic Link Library). Ve skuteΦnosti jsou funkce API vyu₧φvßny vÜemi programy ve Windows. V₧dy kdy₧ vytvo°φte okno, prvek, zm∞nφte vlastnost prvku, je volßna n∞jakß API funkce. Abyste nemuseli znovu psßt ji₧ jednou vytvo°enΘ funkce, m∙₧ete proto vyu₧φvat ty ze soubor∙ DLL. Jak z t∞ch, kterΘ jsou souΦßstφ systΘmu Windows, nap°. GDI32.DLL, SHELL32.DLL atd. tak i z t∞ch, kterΘ napsal n∞kdo jin². Shrneme-li si v²hody pou₧itφ DLL do n∞kolika bod∙, byly by to asi tyto:

  • ┌spora programovΘho k≤du, tj. i mφsta na disku. Nap°. funkci pro vykreslenφ kru₧nice nemusφte vytvß°et znovu, pou₧ijete tu zknihovny GDI32.DLL. Stejn∞ tak ji mohou vyu₧φvat jinΘ aplikace.
  • Jednoduchß ·prava k≤du. Nemusφte m∞nit ka₧dou aplikaci, kterß by vyu₧φvala stejn² k≤d, jednoduÜe zm∞nφte knihovnu DLL (samoz°ejm∞ pokud jde o VaÜi, asi t∞₧ko budete m∞nit systΘmovΘ).
  • Rychlost. Spousta knihoven je napsßna v jazyce C++. Dnes u₧ sice nenφ o tolik rychlejÜφ ne₧ VB, ale p°esto volßte funkci p°φmo a ne p°es n∞jakΘ objekty, kterΘ nakonec stejn∞ volajφ API funkce.
  • ┌spora pam∞ti. Pokud spustφte vßÜ program, je nahrßn do pam∞ti. Pokud vÜak volßte n∞jakou funkci z knihovny DLL, je a₧ v tΘ chvφli nahrßna do pam∞ti. Tzn. dokud ji nepou₧ijete, co₧ se m∙₧e klidn∞ stßt po celou dobu b∞hu programu, nezabφrß pam∞¥.

Samoz°ejm∞ ₧e nebudete (alespo≥ doufßm) od te∩ pou₧φvat na vÜechno funkce API mφsto p°φkaz∙ Visual Basicu. Bylo by to mnohem pracn∞jÜφ. Pro n∞kterΘ akce, nap°. zßpis kamkoliv do registru, vÜak Visual Basic nemß ₧ßdnΘ objekty a proto musφte pou₧φt API.

Zp∞t na obsah


18.2 Deklarace funkcφ API

Deklaraci funkce API m∙₧ete napsat v podstat∞ kdekoliv. Ve t°φd∞, v modulu i ve formulß°i, stejn∞ jako ka₧dou jinou funkci. Rozdφl je pouze v tom, ₧e mßli b²t globßlnφ, tedy Public, musφ b²t v modulu. Ve formulß°i nebo ve t°φd∞ m∙₧e b²t pouze lokßlnφ, tedy Private.

Popis deklarace si ukß₧eme na p°φkladu.

Declare Function WritePrivateProfileString Lib "kernel32" _
  Alias "WritePrivateProfileStringA" (ByVal lpApplicationName As String, _
  ByVal lpKeyName As Any, ByVal lpString As Any, _
  ByVal lpFileName As String) As Long

Samotnou deklaraci uvozuje klφΦovΘ slovo Declare. Po n∞m nßsleduje bu∩ Function nebo Sub, podle toho, zda vracφ nebo nevracφ hodnotu (v∞tÜina je Function). T°etφm parametrem je jmΘno funkce, kterΘ bude pou₧ito k volßnφ ve VB (nemusφ b²t v₧dy jmΘno funkce v knihovn∞ DLL - vysv∞tlφm pozd∞ji). ╚tvrt² parametr je nßzev knihovny DLL uvozen² klφΦov²m slovem Lib. ╪et∞zec za klφΦov²m slovem Alias je opravdov²m nßzvem funkce v knihovn∞. Takov² zßpis se provßdφ z vφce d∙vod∙. Prvnφm m∙₧e b²t kolize nßzvu funkce API s ji₧ existujφcφ funkcφ ve VB. Proto se v klauzuli Alias uvede prav² nßzev funkce, ale volß se nßzev zadan² za slovem Declare. Druh²m d∙vodem je takΘ skuteΦnost, ₧e mnoho funkcφ API je vytvo°eno v jazyku C, tj. mohou obsahovat v nßzvu znaky, kterΘ VB nepodporuje. Funkce API takΘ nemusφ mφt v₧dy nßzev, jsou urΦeny po°adov²m Φφslem. Potom je v klauzuli Alias uvedeno po°adovΘ Φφslo, uvozeno znakem #, a funkce je op∞t volßnß nßzvem za slovem Declare. Dßle u₧ nßsledujφ jednotlivΘ parametry stejn∞ jako u normßlnφch funkcφ a nakonec typ nßvratovΘ hodnoty.

Funkce m∙₧ete deklarovat dv∞ma zp∙soby. Bu∩ znßte jejφ nßzev, vφte ve kterΘ knihovn∞ se nachßzφ, znßte poΦet i typy vÜech jejich parametr∙ a napφÜete ji sami. Tento zp∙sob nenφ moc vhodn², proto₧e m∙₧ete lehce ud∞lat chybu, zvlßÜt∞ ze zaΦßtku a navφc mßlokdo znß zpam∞ti vÜechny tyto informace. Druhß mo₧nost je pou₧φt program API Viewer, kter² je dodßvan² s VB. Jeho pou₧itφ je velmi jednoduchΘ. Spus¥te jej a z menu File zvolte polo₧ku Load text file. S programem jsou dodßvßny t°i soubory s deklaracemi, vßs bude za zaΦßtku zajφmat Win32api.txt. Ten tedy otev°ete. V hornφm rozbalovacφm seznamu (API Type) si vybφrßte mezi funkcemi, konstantami a u₧ivatelsky definovan²mi typy. Vyberete po₧adovanou polo₧ku (funkci, typ ...), zadßte zda mß b²t Private nebo Public a stisknete tlaΦφtko Add. Tφm se p°idß do dolnφho textovΘho pole. Mßte-li vybrßny po₧adovanΘ polo₧ky, stiskn∞te tlaΦφtko Copy, kterΘ zkopφruje text do schrßnky a pak u₧ staΦφ jej ve VB ze schrßnky vlo₧it. Tento postup vßm oproti prvnφmu zaruΦφ bezchybnou syntaxi funkcφ nehled∞ na to, ₧e je mnohem rychlejÜφ, ne₧ kdybyste vÜe m∞li psßt sami. Pokud program API viewer nemßte (VB 6.0 learning edition ho ·dajn∞ neobsahuje - nevφm, nem∞l jsem tu mo₧nost seznßmit se s touto verzφ), m∙₧ete zkusit jeÜt∞ mnohem poveden∞jÜφ program API Guide.

Zp∞t na obsah


18.3 P°φklad pou₧itφ

P°esto₧e se dnes u₧ moc nepou₧φvajφ INI soubory, p°esto si ukß₧eme, jak s nimi pracovat pomocφ funkcφ API. Registr Windows (nßhrada INI) bude pozd∞ji samostatn² dφl naÜeho serißlu. I p°esto m∙₧ete nφ₧e uvedenΘ funkce s ·sp∞chem vyu₧φt, nap°. pro uklßdßnφ n∞jak²ch jednoduch²ch strukturovan²ch informacφ kterΘ se do registru nehodφ.

Spus¥te Visual Basic a otev°ete nov² projekt. Spus¥te API Viewer a vlo₧te z n∞j do k≤du formulß°e deklaraci funkcφ WritePrivateProfileString (pro zßpis hodnoty do INI souboru), GetPrivateProfileInt (pro Φtenφ ΦφselnΘ hodnoty), GetPrivateProfileString (pro Φtenφ °et∞zce), vÜechny jako lokßlnφ pro formulß°, tj. Private. Deklarace by m∞la vypadat takto:

Private Declare Function WritePrivateProfileString Lib "kernel32" Alias "WritePrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpString As Any, ByVal lpFileName As String) As Long

Private Declare Function GetPrivateProfileInt Lib "kernel32" Alias "GetPrivateProfileIntA" (ByVal lpApplicationName As String, ByVal lpKeyName As String, ByVal nDefault As Long, ByVal lpFileName As String) As Long

Private Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long

POZOR - pokud budete funkce psßt sami, m∞jte na pam∞ti, ₧e Win32 rozliÜuje malß a velkß pφsmena.

Pou₧itφ funkcφ API je dßle stejnΘ jako pou₧φvßnφ jak²chkoliv jin²ch funkcφ. V naÜem p°φkladu zapφÜeme do INI souboru dv∞ hodnoty, Φφslo a °et∞zec. Tyto hodnoty nßsledn∞ p°eΦteme a zobrazφme v okn∞ Immediate. K≤d pro takovou akci je velmi jednoduch² (umφstit ho m∙₧ete t°eba do udßlosti Load formulß°e):

Dim ret As Long, sTmp As String

'zßpis
ret = WritePrivateProfileString("PRVNI", "pocet", "10", "C:\priklad.ini")
ret = WritePrivateProfileString("DRUHA", "jmeno", "visual basic", "C:\priklad.ini")

'Φtenφ Φφsla
ret = GetPrivateProfileInt("PRVNI", "pocet", 0, "C:\priklad.ini")
Debug.Print ret

'Φtenφ °et∞zce
sTmp = Space(260)
ret = GetPrivateProfileString("DRUHA", "jmeno", "", sTmp, 260, "C:\priklad.ini")
Debug.Print Left(sTmp, ret)

Te∩ si p°φklad podrobn∞ji popφÜeme. Prvnφ dva °ßdky (mimo deklaraci prom∞nn²ch) zapisujφ hodnoty do souboru C:\priklad.ini. Prvnφ zapφÜe °ßdek pocet s hodnotou 10 do sekce PRVNI a druh² zapφÜe °ßdek jmeno s hodnotu visual basic do sekce DRUHA. V²sledn² INI soubor bude vypadat takto:

[PRVNI]
pocet=10

[DRUHA]
jmeno=visual basic

V²slednß hodnota je ulo₧ena do prom∞nnΘ ret. Zde by bylo vhodnΘ testovat, zda se nerovnß 0. Pokud ano, vznikla p°i zßpisu n∞jakß chyba. DalÜφ °ßdek Φte Φφselnou hodnotu pocet ze sekce PRVNI. Jedin² parametr, kter² by mohl b²t nejasn², je nDefault (v naÜem p°φpad∞ 0). Tato hodnota je vrßcena v p°φpad∞, ₧e funkce hodnotu pocet nem∙₧e p°eΦφst (INI soubor neexistuje, Üpatnß sekce atp.). Jinak je samoz°ejm∞ vrßcena zjiÜt∞nß hodnota. V²pis hodnoty pocet p°eskoΦφme, to ji₧ pro vßs nenφ ₧ßdnß novinka. DalÜφ dva °ßdky zajistφ p°eΦtenφ hodnoty jmeno. Tato funkce funguje trochu jinak ne₧ pro zjiÜt∞nφ Φφsla. Nadefinujete-li ve VB prom∞nnou jako String, mß nulovou dΘlku. Z toho d∙vodu do nφ nenφ API funkce schopna nic zapsat. Proto musφte tuto prom∞nnou nejd°φve nastavit na po₧adovanou dΘlku. V tomto p°φpad∞ by m∞la staΦit hodnota 260. Prvnφ °ßdek tedy naplnφ prom∞nnou sTmp 260 mezerami. Funkci GetPrivateProfileString p°edßte parametry nßzev sekce, nßzev °ßdku, default hodnotu (pracuje stejn∞ jako u GetPrivateProfileInt), prom∞nnou do kterΘ se ulo₧φ p°eΦtenß hodnota, dΘlku prom∞nnΘ (260) a nßzev INI souboru. Funkce vracφ dΘlku °et∞zce, v tomto p°φpad∞ 12 (visual basic). Tφmto zp∙sobem fungujφ v podstat∞ vÜechny funkce API, kterΘ pracujφ s °et∞zci. Je to z toho d∙vodu, ₧e jazyk C pracuje s °et∞zci pon∞kud odliÜn²m zp∙sobem a v p°φpad∞, ₧e ne°eknete, jak dlouh² mß b²t, nenφ schopen rezervovat po₧adovanou pam∞¥. Mφsto napln∞nφ mezerami m∙₧ete taky deklarovat °et∞zec s pevnou dΘlkou, Dim sTmp As String * 260. Visual Basic .NET u₧ vÜak tuto mo₧nost podporovat nebude, proto rad∞ji pou₧φvejte °et∞zce s prom∞nnou dΘlkou.

Zp∞t na obsah


18.4 Chyby

Zde si ukß₧eme jak zachytßvat a obsluhovat chyby, kterΘ nastanou v API funkcφch. Narozdφl od chyb, kterΘ nastanou v ostatnφm k≤du, nem∙₧eme chyby, kterΘ nastanou v API funkcφch zachytßvat pomocφ konstrukce On Error .... OvÜem objekt Err pou₧φt m∙₧eme, a to konkrΘtn∞ jeho vlastnost LastDllError, kterß obsahuje Φφslo chyby, kterß nastala p°i volßnφ API funkce. Stejnou funkΦnost nabφzφ i API funkce GetLastError.

Declare Function GetLastError Lib "kernel32" () As Long

U API funkcφ z knihoven standardn∞ dodßvan²ch s Windows jsou v zßsad∞ pou₧φvßny t°i rozdφlnΘ zp∙soby obsluhy chyb v API funkcφch. Prvnφ a dnes ji₧ nep°φliÜ pou₧φvan² zp∙sob pochßzφ z 16-bitov²ch dob, kdy je n∞kdy nutnΘ provßd∞t obsluhu chyb individußln∞ v zßvislosti na pou₧itΘ API funkci. Tento zp∙sob je pou₧φvßn pouze u funkcφ, kterΘ jsou poskytovßny pro zp∞tnou kompatibilitu. Typick²m p°φkladem je nap°φklad funkce GetProfileString, kterß v p°φpad∞ problΘm∙ vracφ dva chybovΘ stavy, indikovanΘ vrßcenou hodnotou, je₧ je zßvislß na hodnot∞ poslednφho parametru.

Druh² zp∙sob vyu₧φvß nßvratovΘ hodnoty funkce. V p°φpad∞, ₧e funkce prob∞hla v po°ßdku, vratφ hodnotu ERROR_SUCCESS Φili 0, jinak je vrßcenß hodnota Φφslem chyby. Takto se nap°φklad chovajφ API funkce pro prßci s registrem. Nßsledujφcφ p°φklad ilustruje zachytßvßnφ chyb na funkci pro testovßnφ existence klφΦe v registru.

'Chybov² k≤d
Const ERROR_BADKEY = 1010&

'P°edefinovanΘ handly klφΦ∙ (hKey)
Const HKEY_CLASSES_ROOT = &H80000000
Const HKEY_CURRENT_USER = &H80000001
Const HKEY_LOCAL_MACHINE = &H80000002
Const HKEY_USERS = &H80000003
Const HKEY_PERFORMANCE_DATA = &H80000004
Const HKEY_CURRENT_CONFIG = &H80000005
Const HKEY_DYN_DATA = &H80000006

Declare Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA" ( _
  ByVal hKey As Long, ByVal lpSubKey As String, _
  ByVal ulOptions As Long, ByVal samDesired As Long, _
  phkResult As Long) As Long
Declare Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As Long) As Long

Public Function KeyExist(hKey As Long, lpSubKey As String) As Boolean
  Dim retVal As Long, phkResult As Long

  retVal = RegOpenKeyEx(hKey, lpSubKey, 0, KEY_ALL_ACCESS, phkResult)
  If lRetVal <> ERROR_BADKEY Then
    RegCloseKey phkResult
    KeyExist = True
  Else
    KeyExist = False
  End If
End Function

T°etφ a nejrozÜφ°en∞jÜφ zp∙sob funguje tak, ₧e v p°φpad∞ chyby je zavolßna API funkce SetLastError v parametru s Φφslem chyby a v nßvratovΘ hodnot∞ funkce API je p°edßna 0. V praxi se postupuje tak, ₧e je otestovßna nßvratovß hodnota funkce a v p°φpad∞, ₧e je rovna nule, je zavolßna API funkce GetLastError a zjiÜt∞no Φφslo chyby, kterß nastala. Tento zp∙sob je nejb∞₧n∞jÜφ. Pokud funkce prob∞hla v po°ßdku, je nßvratovß hodnota nenulovß. U tohoto zp∙sobu m∙₧ete narazit po ·sp∞ÜnΘm provedenφ API funkce na dva mo₧nΘ stavy. Bu∩ je ponechßno nastavenφ chyby, kterß nastala p°ed provßd∞nφm danΘ funkce, beze zm∞ny, nebo API funkce zavolß SetLastError v parametru s hodnotou ERROR_SUCCESS Φili 0. V nßsledujφcφm p°φklad∞ testujeme uveden² postup na API funkci GetWindowRect. Je z°ejmΘ, ₧e zadßvat handle okna staticky je nesmysl, ale pro ilustraci oÜet°enφ chyby to lze p°ejφt (na mφst∞ Φφsla 1234 by se m∞lo nachßzet nap°φklad Me.hwnd).

Type RECT
  Left As Long
  Top As Long
  Right As Long
  Bottom As Long
End Type

Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long

...
Dim r As RECT, retVal As Long

retVal = GetWindowRect(1234, r)
If retVal = 0 Then
  MsgBox "Chyba: " & GetLastError()
End If

Nastane-li chyba, je dobrΘ mimo jejφ Φφslo znßt takΘ jejφ popis. K tomu slou₧φ API funkce FormatMessage, kterß po zadßnφ n∞kolika parametr∙, z nich₧ nejd∙le₧it∞jÜφ je Φφslo chyby, vrßtφ popis chyby. Pro zjednoduÜenφ uvßdφm v nßsledujφcφm p°φkladu funkci, kterß omezφ zadßvßnφ parametr∙ pouze na Φφslo chyby.

Private Declare Function GetLastError Lib "kernel32" () As Long
Private Declare Function FormatMessage Lib "kernel32" Alias "FormatMessageA" ( _
  ByVal dwFlags As Long, lpSource As Any, ByVal dwMessageId As Long, _
  ByVal dwLanguageId As Long, ByVal lpBuffer As String, ByVal nSize As Long, _
  Arguments As Long) As Long

Public Function ErrorDescription(nErr As Long) As String
  Dim retVal As Long, bufStr As String * 255

  retVal = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM Or FORMAT_MESSAGE_IGNORE_INSERTS, 0, nErr, 0, bufStr, 255, 0)
  ErrorDescription = Left$(bufStr, retVal)
End Function

  MsgBox GetLastError() & ":" & ErrorDescription(GetLastError()), vbOKOnly, "Chyba!"

Zp∞t na obsah