Bara för att du kan göra något betyder det inte att du borde göra det.
Jag tror djupt på bakåtkompatibilitetens helighet. Men det kommer med en mörk sida. Ibland faller de gamla sätten att göra saker i onåd. Deras användning blir så mystisk att vi har en tendens att glömma att de ens existerar.
Så det går med DefType-satser.
Vad du inte vet kan skada dig
För flera månader sedan skrev jag en artikel om Romke Soldaats klassmodul Registry Operations.
Jag publicerade ändringarna jag gjorde i Romkes API-deklarationer för att få koden att köras under 64-bitars VBA. Varje API-anrop lindades i #If VBA7
villkorliga kompileringstaggar och uppdaterade med PtrSafe
nyckelord.
Det var bara ett problem.
Jag glömde att inkludera en nyckeländring som jag hade gjort i en av deklarationerna på modulnivå i Romkes kod. Utan denna förändring skulle Romkes modifierade kod inte kompilera under 64-bitars VBA. Kompileringsfelet inträffade på följande rad:
Felmeddelandet var "ByRef argument typ missmatch " och den markerade variabeln var hCurKey
.
Här är den stötande kodraden från Romkes ursprungliga klassmodul:
Private hCurKey
För att fixa kompileringsfelet kan ovanstående kodrad ändras till detta:
Private hCurKey As Variant
Men vänta, säger du, gör inte dessa två rader kod samma sak?!?! Alla vet att om du inte deklarerar en variabels typ i VBA så deklareras den implicit som en variant. ... Eller är det?
Explicit är bättre än implicit
Så vad är det som händer här egentligen?
Problemet är att den första raden i koden ovan–Private hCurKey
– definierade hCurKey-variabeln som en Long
data typ.
Hur kan detta vara?
Det var på grund av den här konstiga raden i toppen av Romkes klassmodul:
DefLng H-I, L, N
Vad gör den linjen? Det sägs att varje deklarerad variabel i den aktuella modulen utan en explicit deklarerad typ vars variabelnamn börjar med H
, I
, L
, eller N
, kommer att behandlas av kompilatorn som en Long
datatyp.
Och så, raden Private hCurKey
gjorde implicit deklarera en typ för variabeln hCurKey, men den implicita deklarationen var som en lång datatyp istället för en variant.
Varför Variant Kompilera men lång Inte?
Om varför koden kompileras när hCurKey
är en variant men misslyckas när den är lång, det är en fråga om 32-bitars till 64-bitars konverteringsprocessen.
För att hitta källan till problemet måste vi undersöka den migrerade koden för RegCreateKeyEx API-deklarationen:
#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
När vi anropar RegCreateKeyEx
från koden skickar vi hCurKey
variabel som det näst sista argumentet i funktionen. Med andra ord, det skickas som phkResult
argument. Observera att i versionen före VBA7 (Access 2007 och tidigare), phkResult
deklareras som en Long, men i VBA7-versionen deklareras den som en LongPtr
.
Det beror på att phkResult
får ett handtag till den skapade eller öppnade registernyckeln. Närhelst du ser ordet "hantera" kopplat till ett API-anrop kan du säkert översätta det i ditt huvud till "minnesadress". Det är därför argumentet omdefinieras som en LongPtr
i VBA7-koden:vid exekvering i en 32-bitarsmiljö, en LongPtr
behandlas som en 32-bitars Long
heltal, men i en 64-bitars miljö, en LongPtr
behandlas som en 64-bitars LongLong
heltal.
Deklarerar hCurKey
som Variant är lite av en genväg. Följande anpassning skulle också fungera (och fungera snabbare, även om hastighetsökningen sannolikt är omärklig för användaren om den inte anropas många gånger inuti en loop):
#If VBA7 Then
Private hCurKey As LongPtr
#Else
Private hCurKey As Long
#End If
Som jag sa är ovanstående tillvägagångssätt mer explicit när det gäller att förmedla utvecklarens avsikt, presterar bättre och kommer att ge upphov till fler kompileringsfel än Private hCurKey As Variant
alternativ.
Men jag är känd för att vara lat och Private hCurKey As Variant
är nästan lika bra med mycket mindre skrivning.
Använd din kunskap för gott
Kommer du ihåg vad jag sa i början av den här artikeln?
Bara för att du kan göra något betyder det inte att du borde göra det.
Jag skrev den här artikeln av två anledningar:
- För att uppmuntra dig att uttryckligen deklarera Variantvariabler
As Variant
- För att öka medvetenheten om en svårbegriplig aspekt av VBA som kan göra dig smutsig om du underhåller (eller kopierar in) någon annans kod
JAG GJORDE INTE skriv den här artikeln för att inspirera dig att skriva DefType-satser i din egen kod. GÖR INTE DET!!! Kom ihåg att bara för att du kan göra något betyder det inte att du borde göra det.