To, že něco umíš, neznamená, že bys měl.
Hluboce věřím v posvátnost zpětné kompatibility. Ale přichází to s temnou stránkou. Někdy staré způsoby dělání věcí upadnou v nemilost. Jejich používání se stává tak tajemným, že máme tendenci zapomínat, že vůbec existují.
Tak je to s příkazy DefType.
Co nevíte, může vám ublížit
Před několika měsíci jsem napsal článek o modulu Romke Soldaat's Registry Operations.
Zveřejnil jsem změny, které jsem provedl v deklaracích Romkeho API, aby kód běžel pod 64bitovým VBA. Každé volání API bylo zabaleno do #If VBA7
tagy podmíněné kompilace a aktualizovány pomocí PtrSafe
klíčové slovo.
Vyskytl se pouze jeden problém.
Zapomněl jsem zahrnout klíčovou změnu, kterou jsem provedl v jedné z deklarací na úrovni modulu v Romkeho kódu. Bez této změny by se upravený kód Romke nezkompiloval pod 64bitovým VBA. Na následujícím řádku došlo k chybě kompilace:
Chybová zpráva byla „Neshoda typu argumentu ByRef “ a zvýrazněná proměnná byla hCurKey
.
Zde je problematický řádek kódu z původního modulu třídy Romke:
Private hCurKey
Chcete-li opravit chybu kompilace, výše uvedený řádek kódu může být změněn na tento:
Private hCurKey As Variant
Ale počkat, říkáte si, nedělají ty dva řádky kódu totéž?!?! Každý ví, že pokud nedeklarujete typ proměnné ve VBA, je implicitně deklarována jako varianta. ... Nebo ano?
Explicitní je lepší než implicitní
Tak co se tu vlastně děje?
Problém je v tom, že první řádek kódu výše – Private hCurKey
–definoval proměnnou hCurKey jako Long
datový typ.
Jak by to mohlo být?
Bylo to kvůli této podivné řádce v horní části modulu třídy Romke:
DefLng H-I, L, N
Co ta linka dělá? Říká se, že každá deklarovaná proměnná v aktuálním modulu bez explicitně deklarovaného typu, jejíž název proměnné začíná H
, I
, L
, nebo N
, bude kompilátor považován za Long
datový typ.
A tak řádek Private hCurKey
udělal implicitně deklarovat typ pro proměnnou hCurKey, ale implicitní deklarace byla jako datový typ Long namísto Variant.
Proč varianta Kompilace, ale Dlouhá Není?
Proč se kód kompiluje, když hCurKey
je varianta, ale selže, když je dlouhá, to je záležitost procesu převodu z 32 bitů na 64 bitů.
Abychom našli zdroj problému, musíme prozkoumat migrovaný kód pro deklaraci RegCreateKeyEx API:
#If VBA7 Then
Private Declare PtrSafe Function RegCreateKeyEx _
Lib "advapi32.dll" Alias "RegCreateKeyExA" ( _
ByVal hKey As LongPtr, ByVal lpSubKey As String, _
ByVal Reserved As Long, ByVal lpClass As String, _
ByVal dwOptions As Long, ByVal samDesired As Long, _
lpSecurityAttributes As SECURITY_ATTRIBUTES, _
phkResult As LongPtr, lpdwDisposition As Long) As Long
#Else
Private Declare Function RegCreateKeyEx _
Lib "advapi32.dll" Alias "RegCreateKeyExA" ( _
ByVal hKey As Long, ByVal lpSubKey As String, _
ByVal Reserved As Long, ByVal lpClass As String, _
ByVal dwOptions As Long, ByVal samDesired As Long, _
lpSecurityAttributes As SECURITY_ATTRIBUTES, _
phkResult As Long, lpdwDisposition As Long) As Long
#End If
Když zavoláme RegCreateKeyEx
z kódu předáváme hCurKey
proměnná jako předposlední argument ve funkci. Jinými slovy, je předán jako phkResult
argument. Všimněte si, že ve verzi před VBA7 (Access 2007 a starší) phkResult
je deklarován jako Long, ale ve verzi VBA7 je deklarován jako LongPtr
.
To proto, že phkResult
obdrží kliku na vytvořený nebo otevřený klíč registru. Kdykoli uvidíte slovo „handle“ spojené s voláním API, můžete si to v hlavě bezpečně přeložit na „adresu paměti“. Proto je argument předefinován jako LongPtr
v kódu VBA7:při provádění v 32bitovém prostředí se zobrazí LongPtr
je považován za 32bitový Long
celé číslo, ale v 64bitovém prostředí LongPtr
je považován za 64bitový LongLong
celé číslo.
Deklarování hCurKey
protože Variant je trochu zkratka. Následující přizpůsobení by také fungovalo (a fungovalo rychleji, ačkoli zvýšení rychlosti bude pravděpodobně pro uživatele nepostřehnutelné, pokud není voláno mnohokrát uvnitř smyčky):
#If VBA7 Then
Private hCurKey As LongPtr
#Else
Private hCurKey As Long
#End If
Jak jsem řekl, výše uvedený přístup jasněji vyjadřuje záměr vývojáře, funguje lépe a způsobí více chyb při kompilaci než Private hCurKey As Variant
alternativa.
Ale je o mně známo, že jsem líný, a Private hCurKey As Variant
je téměř stejně dobré s mnohem méně psaním.
Použijte své znalosti pro dobro
Pamatujete si, co jsem řekl na začátku tohoto článku?
To, že něco dokážeš, neznamená, že bys měl.
Tento článek jsem napsal ze dvou důvodů:
- Abych vás povzbudil, abyste výslovně deklarovat proměnné varianty
As Variant
- Zvýšit povědomí o tajemném aspektu VBA, který by vás mohl podrazit, pokud udržujete (nebo kopírujete a vkládáte) kód někoho jiného.
NEMĚL napište tento článek, který vás inspiruje k psaní příkazů DefType ve vašem vlastním kódu. NEDĚLEJ TO!!! Pamatujte, že to, že něco umíte, neznamená, že byste to měli dělat.