Alleen omdat je iets kunt doen, wil nog niet zeggen dat je het moet doen.
Ik geloof diep in de heiligheid van achterwaartse compatibiliteit. Maar het komt met een donkere kant. Soms vallen de oude manieren om dingen te doen uit de gratie. Hun gebruik wordt zo geheimzinnig dat we de neiging hebben om te vergeten dat ze bestaan.
Zo gaat het met DefType-statements.
Wat u niet weet, kan u pijn doen
Enkele maanden geleden schreef ik een artikel over de klasse module Registerbewerkingen van Romke Soldaat.
Ik heb de wijzigingen die ik heb aangebracht in de API-declaraties van Romke gepubliceerd om de code onder 64-bits VBA te laten werken. Elke API-aanroep was verpakt in #If VBA7
voorwaardelijke compilatietags en bijgewerkt met de PtrSafe
zoekwoord.
Er was maar één probleem.
Ik vergat een belangrijke wijziging op te nemen die ik had aangebracht in een van de declaraties op moduleniveau in de code van Romke. Zonder deze wijziging zou de gewijzigde code van Romke niet worden gecompileerd onder 64-bits VBA. De compileerfout deed zich voor op de volgende regel:
De foutmelding was "ByRef argumenttype komt niet overeen " en de gemarkeerde variabele was hCurKey
.
Hier is de beledigende regel code uit de originele klassenmodule van Romke:
Private hCurKey
Om de compileerfout op te lossen, kan de bovenstaande regel code in dit worden gewijzigd:
Private hCurKey As Variant
Maar wacht, zeg je, doen die twee regels code niet hetzelfde?!?! Iedereen weet dat als je het type van een variabele niet declareert in VBA, het impliciet wordt gedeclareerd als een variant. ... Of toch?
Expliciet is beter dan impliciet
Dus wat is hier echt aan de hand?
Het probleem is dat de eerste regel code hierboven-Private hCurKey
–definieerde de hCurKey-variabele als een Long
data type.
Hoe kan dit zo zijn?
Het kwam door deze rare regel bovenaan de lesmodule van Romke:
DefLng H-I, L, N
Wat doet die lijn? Het zegt dat elke gedeclareerde variabele in de huidige module zonder een expliciet gedeclareerd type waarvan de variabelenaam begint met H
, I
, L
, of N
, wordt door de compiler behandeld als een Long
gegevenstype.
En dus, de regel Private hCurKey
deed impliciet declareer een type voor de hCurKey-variabele, maar de impliciete declaratie was als een Long-gegevenstype in plaats van een Variant.
Waarom doet Variant Compileren Maar Lang Niet?
Waarom de code compileert wanneer hCurKey
is een variant maar mislukt als het een lang is, dat is een kwestie van het 32-bits naar 64-bits conversieproces.
Om de oorzaak van het probleem te vinden, moeten we de gemigreerde code voor de RegCreateKeyEx API-declaratie onderzoeken:
#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
Wanneer we RegCreateKeyEx
. aanroepen van de code geven we de hCurKey
. door variabele als het voorlaatste argument in de functie. Met andere woorden, het wordt doorgegeven als de phkResult
argument. Merk op dat in de pre-VBA7-versie (Access 2007 en eerder), phkResult
wordt gedeclareerd als een Long, maar in de VBA7-versie wordt het gedeclareerd als een LongPtr
.
Dat komt omdat de phkResult
ontvangt een handvat naar de aangemaakte of geopende registersleutel. Telkens wanneer u het woord 'handle' ziet dat is gekoppeld aan een API-aanroep, kunt u dat veilig in uw hoofd vertalen naar 'geheugenadres'. Daarom is het argument opnieuw gedefinieerd als een LongPtr
in de VBA7-code:bij uitvoering in een 32-bits omgeving, een LongPtr
wordt behandeld als een 32-bits Long
geheel getal, maar in een 64-bits omgeving, een LongPtr
wordt behandeld als een 64-bits LongLong
geheel getal.
hCurKey
. declareren als Variant is een beetje een kortere weg. De volgende aanpassing zou ook werken (en sneller werken, hoewel de snelheidsverhoging waarschijnlijk niet waarneembaar is voor de gebruiker, tenzij deze vaak binnen een lus wordt aangeroepen):
#If VBA7 Then
Private hCurKey As LongPtr
#Else
Private hCurKey As Long
#End If
Zoals ik al zei, is de bovenstaande benadering explicieter in het overbrengen van de bedoeling van de ontwikkelaar, presteert beter en zal meer compileerfouten veroorzaken dan de Private hCurKey As Variant
alternatief.
Maar ik sta bekend als lui en Private hCurKey As Variant
is bijna even goed met veel minder typen.
Gebruik uw kennis ten goede
Weet je nog wat ik aan het begin van dit artikel zei?
Alleen omdat je iets kunt doen, wil nog niet zeggen dat je het moet doen.
Ik heb dit artikel om twee redenen geschreven:
- Om u aan te moedigen om expliciet declareer Variantvariabelen
As Variant
- Om het bewustzijn te vergroten over een geheimzinnig aspect van VBA dat u zou kunnen laten struikelen als u de code van iemand anders onderhoudt (of kopieert)
Ik NIET schrijf dit artikel om je te inspireren om DefType-statements in je eigen code te schrijven. DOE DAT NIET!!! Onthoud dat het feit dat je iets kunt doen niet betekent dat je het ook moet doen.