sql >> Databasteknik >  >> RDS >> Access

DefType-uttalanden i VBA:The Dark Side of Backward Compatibility

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:

  1. För att uppmuntra dig att uttryckligen deklarera Variantvariabler As Variant
  2. 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.

Externa referenser

Deftype-satser (VBA) Office VBA-referensämneMicrosoft Docso365devx Windows API-deklarationer i VBA för 64-bitarSå här konverterar du dina API-deklarationer till 64-bitars. - Vanliga myter avslöjas, nyckelfaktorer förklaras!CodekabinettPhilipp Stiefel

Refererade artiklar

Respekt för bakåtkompatibilitet En funktion som användes flitigt av en mycket liten andel av avancerade användare har bibehållits under loppet av fem efterföljande uppgraderingar (och allt fler). Nu visar det vördnad för bakåtkompatibilitet. Inte längre inställd Mike Wolfe RegOp Class för 64-bitars VBAUppdatering av en klassisk VBA-registerläs- och skrivklassmodul för 64-bitars kompatibilitet. Inte längre inställd Mike Wolfe
  1. Hur installerar man ett Python-paket på Linux så att det hittas av den redan fungerande PostgreSQL 13 plpython3u-tillägget?

  2. hur kan jag uppdatera topp 100 poster i sql server

  3. Vad är gränsen för SQL-variabler man kan specificera i en enda execSQL-fråga

  4. PLSQL Infoga i med subquery och returnerande klausul