sql >> Database teknologi >  >> RDS >> Access

DefType Statements i VBA:The Dark Side of Backward Compatibility

Bare fordi du kan gøre noget, betyder det ikke, at du skal.

Jeg tror dybt på helligheden af ​​bagudkompatibilitet. Men det kommer med en mørk side. Nogle gange falder de gamle måder at gøre tingene på. Deres brug bliver så mystisk, at vi har en tendens til at glemme, at de overhovedet eksisterer.

Så det går med DefType-udsagn.

Hvad du ikke ved kan skade dig

For flere måneder siden skrev jeg en artikel om Romke Soldaats Registry Operations klassemodul.

Jeg offentliggjorde de ændringer, jeg lavede i Romkes API-deklarationer for at få koden til at køre under 64-bit VBA. Hvert API-kald blev pakket ind i #If VBA7 betingede kompileringstags og opdateret med PtrSafe søgeord.

Der var kun ét problem.

Jeg glemte at inkludere en nøgleændring, som jeg havde lavet til en af ​​deklarationerne på modulniveau i Romkes kode. Uden denne ændring ville Romkes modificerede kode ikke kompilere under 64-bit VBA. Kompileringsfejlen opstod på følgende linje:

Fejlmeddelelsen var "ByRef argument type mismatch " og den fremhævede variabel var hCurKey .

Her er den stødende kodelinje fra Romkes originale klassemodul:

Private hCurKey

For at rette kompileringsfejlen kan ovenstående kodelinje ændres til denne:

Private hCurKey As Variant

Men vent, siger du, gør de to linjer kode ikke det samme?!?! Alle ved, at hvis du ikke erklærer en variabels type i VBA, er den implicit erklæret som en Variant. ...  Eller er det?

Eksplicit er bedre end implicit

Så hvad sker der egentlig her?

Problemet er, at den første kodelinje ovenfor-Private hCurKey – var ved at definere hCurKey-variablen som en Long datatype.

Hvordan kunne dette være?

Det var på grund af denne mærkelige streg i toppen af ​​Romkes klassemodul:

DefLng H-I, L, N

Hvad laver den linje? Det siger, at hver deklarerede variabel i det aktuelle modul uden en eksplicit erklæret type, hvis variabelnavn begynder med H , I , L eller N , vil blive behandlet af compileren som en Long datatype.

Og så linjen Private hCurKey gjorde implicit erklære en type for variablen hCurKey, men den implicitte erklæring var som en lang datatype i stedet for en variant.

Hvorfor variant Kompiler men lang Gør det ikke?

Med hensyn til hvorfor koden kompileres når hCurKey er en variant, men fejler, når den er en lang, det er et spørgsmål om 32-bit til 64-bit konverteringsprocessen.

For at finde kilden til problemet skal vi undersøge den migrerede kode til RegCreateKeyEx API-erklæringen:

#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 kalder RegCreateKeyEx fra koden sender vi hCurKey variabel som det næstsidste argument i funktionen. Med andre ord bliver det videregivet som phkResult argument. Bemærk, at i versionen før VBA7 (Access 2007 og tidligere), phkResult er erklæret som en Long, men i VBA7-versionen er den erklæret som en LongPtr .

Det er fordi phkResult modtager et håndtag til den oprettede eller åbnede registreringsnøgle. Når du ser ordet "håndtag" forbundet med et API-kald, kan du roligt oversætte det i dit hoved til "hukommelsesadresse". Det er derfor, argumentet omdefineres som en LongPtr i VBA7-koden:ved afvikling i et 32-bit miljø, en LongPtr behandles som en 32-bit Long heltal, men i et 64-bit miljø, en LongPtr behandles som en 64-bit LongLong heltal.

Erklærer hCurKey da Variant er lidt af en genvej. Følgende tilpasning ville også fungere (og fungere hurtigere, selvom hastighedsstigningen sandsynligvis vil være umærkelig for brugeren, medmindre den kaldes mange gange inde i en løkke):

#If VBA7 Then
    Private hCurKey As LongPtr
#Else
    Private hCurKey As Long
#End If

Som jeg sagde, er ovenstående tilgang mere eksplicit til at formidle udviklerens hensigt, yder bedre og vil medføre flere kompileringsfejl end Private hCurKey As Variant alternativ.

Men jeg er kendt for at være doven og Private hCurKey As Variant er næsten lige så godt med meget mindre skrivning.

Brug din viden til gode

Husk nu, hvad jeg sagde i begyndelsen af ​​denne artikel?

Bare fordi du kan gøre noget, betyder det ikke, at du skal.

Jeg skrev denne artikel af to grunde:

  1. For at opfordre dig til eksplicit erklære Variantvariabler As Variant
  2. For at øge bevidstheden om et mystisk aspekt af VBA, der kan slå dig i stå, hvis du vedligeholder (eller kopierer) en andens kode

JEG HAR IKKE skriv denne artikel for at inspirere dig til at skrive DefType-udsagn i din egen kode. GØR DET IKKE!!! Husk, bare fordi du kan gøre noget, betyder det ikke, at du skal.

Eksterne referencer

Deftype-sætninger (VBA) Office VBA-referenceemneMicrosoft Docso365devx Windows API-erklæringer i VBA til 64-bitSådan konverterer du dine API-erklæringer til 64-bit. - Almindelige myter aflivet, nøglefaktorer forklaret!CodekabinettPhilipp Stiefel

Referencede artikler

Ærbødighed for bagudkompatibilitet En funktion, der blev flittigt brugt af en meget lille procentdel af superbrugere, er blevet opretholdt i løbet af fem efterfølgende opgraderinger (og tæller). Nu viser det ærbødighed for bagudkompatibilitet. Ikke længere indstilletMike Wolfe RegOp-klasse til 64-bit VBAOpdatering af et klassisk læse- og skriveklassemodul i VBA-registret til 64-bit kompatibilitet. Ikke længere indstilletMike Wolfe
  1. Sådan kontrollerer du MySQL-database- og tabelstørrelser

  2. Postgres:vælg summen af ​​værdier, og summer dette igen

  3. JSON_ARRAY() – Opret et JSON-array fra en liste over værdier i MySQL

  4. Indsæt et tidsstempel i databasen via ContentValues