Само защото можете да направите нещо, не означава, че трябва.
Вярвам дълбоко в светостта на обратната съвместимост. Но идва с тъмна страна. Понякога старите начини за правене на нещата изпадат в немилост. Използването им става толкова загадъчно, че сме склонни да забравяме, че дори съществуват.
Така че върви с операторите DefType.
Това, което не знаете, може да ви нарани
Преди няколко месеца написах статия за модула за клас Операции на системния регистър на Romke Soldaat.
Публикувах промените, които направих в декларациите на API на Romke, за да накарам кода да работи под 64-битов VBA. Всяко API извикване беше обвито в #If VBA7
маркери за условна компилация и актуализирани с PtrSafe
ключова дума.
Имаше само един проблем.
Забравих да включа ключова промяна, която направих в една от декларациите на ниво модул в кода на Romke. Без тази промяна модифицираният код на Romke няма да се компилира под 64-битов VBA. Възникна грешка при компилиране на следния ред:
Съобщението за грешка беше „Несъответствие на типа аргумент на ByRef " и маркираната променлива беше hCurKey
.
Ето обидния ред код от оригиналния клас модул на Romke:
Private hCurKey
За да коригирате грешката при компилиране, горният ред код може да бъде променен на това:
Private hCurKey As Variant
Но чакайте, казвате вие, тези два реда код не правят ли едно и също нещо?!?! Всеки знае, че ако не декларирате типа на променлива във VBA, тя имплицитно се декларира като Variant. ... Или е така?
Изричното е по-добро от имплицитното
И така, какво всъщност се случва тук?
Проблемът е, че първият ред от кода по-горе – Private hCurKey
– дефинира променливата hCurKey като Long
тип данни.
Как може да бъде това?
Това беше заради тази странна линия в горната част на модула за клас на Romke:
DefLng H-I, L, N
Какво прави тази линия? Казва, че всяка декларирана променлива в текущия модул без изрично деклариран тип, чието име на променлива започва с H
, I
, L
или N
, ще се третира от компилатора като Long
тип данни.
И така, редът Private hCurKey
направи имплицитно декларирайте тип за променливата hCurKey, но имплицитната декларация беше като тип данни Long вместо Variant.
Защо има Вариант Компилиране, но Дълго Не?
Що се отнася до защо кодът се компилира, когато hCurKey
е вариант, но не успява, когато е дълъг, това е въпрос на 32-битов в 64-битов процес на преобразуване.
За да открием източника на проблема, трябва да проверим мигрирания код за декларацията на API RegCreateKeyEx:
#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
Когато извикаме RegCreateKeyEx
от кода, ние предаваме hCurKey
променлива като предпоследен аргумент във функцията. С други думи, той се предава като phkResult
аргумент. Обърнете внимание, че във версията преди VBA7 (Access 2007 и по-стари), phkResult
е деклариран като Long, но във версията VBA7 е деклариран като LongPtr
.
Това е така, защото phkResult
получава ръкохватка към създадения или отворен ключ на системния регистър. Всеки път, когато видите думата „дръжка“, свързана с извикване на API, можете безопасно да я преведете в главата си на „адрес на паметта“. Ето защо аргументът се предефинира като LongPtr
в кода на VBA7:когато се изпълнява в 32-битова среда, LongPtr
се третира като 32-битов Long
цяло число, но в 64-битова среда, LongPtr
се третира като 64-битов LongLong
цяло число.
Деклариране на hCurKey
тъй като Variant е малко пряк път. Следната адаптация също ще работи (и работи по-бързо, въпреки че увеличението на скоростта вероятно ще бъде незабележимо за потребителя, освен ако не бъде извикано много пъти в цикъл):
#If VBA7 Then
Private hCurKey As LongPtr
#Else
Private hCurKey As Long
#End If
Както казах, горният подход е по-ясен при предаването на намерението на разработчика, работи по-добре и ще доведе до повече грешки по време на компилиране от Private hCurKey As Variant
алтернатива.
Но е известно, че съм мързелив и Private hCurKey As Variant
е почти също толкова добре с много по-малко писане.
Използвайте знанията си за добро
Сега си спомняте какво казах в началото на тази статия?
Само защото можете да направите нещо, не означава, че трябва.
Написах тази статия по две причини:
- За да ви насърчим да изрично декларирайте Вариантни променливи
As Variant
- За повишаване на осведомеността относно тайнствен аспект на VBA, който може да ви препъне, ако поддържате (или копирате и поставяте) код на някой друг
АЗНЕ напишете тази статия, за да ви вдъхнови да пишете изявления DefType във вашия собствен код. НЕ ПРАВИ ТОВА!!! Не забравяйте, че само защото можете да направите нещо, не означава, че трябва.