To, że możesz coś zrobić, nie oznacza, że powinieneś.
Głęboko wierzę w świętość wstecznej kompatybilności. Ale ma też ciemną stronę. Czasami stare sposoby robienia rzeczy wypadają z łask. Ich użycie staje się tak tajemnicze, że mamy tendencję do zapominania, że nawet istnieją.
Tak samo jest z deklaracjami DefType.
To, czego nie wiesz, może cię zranić
Kilka miesięcy temu napisałem artykuł o module klasy Registry Operations Romke Soldaat.
Opublikowałem zmiany, które wprowadziłem do deklaracji API Romke'a, aby kod działał pod 64-bitowym VBA. Każde wywołanie API zostało opakowane w #If VBA7
warunkowe tagi kompilacji i zaktualizowane za pomocą PtrSafe
słowo kluczowe.
Był tylko jeden problem.
Zapomniałem zawrzeć kluczową zmianę, którą wprowadziłem do jednej z deklaracji na poziomie modułu w kodzie Romkego. Bez tej zmiany zmodyfikowany kod Romkego nie skompilowałby się w 64-bitowym VBA. Wystąpił błąd kompilacji w następującym wierszu:
Komunikat o błędzie to „Niezgodność typu argumentu ByRef ", a podświetloną zmienną był hCurKey
.
Oto obraźliwy wiersz kodu z oryginalnego modułu klasy Romke:
Private hCurKey
Aby naprawić błąd kompilacji, powyższy wiersz kodu może zostać zmieniony na następujący:
Private hCurKey As Variant
Ale czekaj, mówisz, czy te dwie linijki kodu nie robią tego samego?!?! Wszyscy wiedzą, że jeśli nie zadeklarujesz typu zmiennej w VBA, zostanie ona domyślnie zadeklarowana jako Wariant. ... A może tak jest?
Jasne jest lepsze niż niejawne
Więc co się tutaj naprawdę dzieje?
Problem polega na tym, że pierwsza linijka kodu powyżej – Private hCurKey
– definiował zmienną hCurKey jako Long
typ danych.
Jak to możliwe?
To z powodu tej dziwnej linii na górze modułu klasy Romke:
DefLng H-I, L, N
Co robi ta linia? Oznacza to, że każda zadeklarowana zmienna w bieżącym module bez jawnie zadeklarowanego typu, której nazwa zmiennej zaczyna się od H
, I
, L
lub N
, będzie traktowane przez kompilator jako Long
typ danych.
I tak, wiersz Private hCurKey
zrobił domyślnie zadeklarować typ dla zmiennej hCurKey, ale niejawna deklaracja była typem danych Long zamiast Variant.
Dlaczego Wariant Kompiluj, ale długo Nie?
Dlaczego kod kompiluje się, gdy hCurKey
? jest Wariantem, ale kończy się niepowodzeniem, gdy jest Długi, to kwestia procesu konwersji 32-bitowej na 64-bitową.
Aby znaleźć źródło problemu, musimy sprawdzić zmigrowany kod dla deklaracji 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
Kiedy wywołujemy RegCreateKeyEx
z kodu przekazujemy hCurKey
zmienna jako przedostatni argument w funkcji. Innymi słowy, jest przekazywany jako phkResult
argument. Zauważ, że w wersji wcześniejszej niż VBA7 (Access 2007 i wcześniejsze) phkResult
jest zadeklarowany jako Long, ale w wersji VBA7 jest zadeklarowany jako LongPtr
.
To dlatego, że phkResult
otrzymuje uchwyt do utworzonego lub otwartego klucza rejestru. Za każdym razem, gdy zobaczysz słowo „uchwyt” skojarzone z wywołaniem API, możesz bezpiecznie przetłumaczyć je w swojej głowie na „adres pamięci”. Dlatego argument został przedefiniowany jako LongPtr
w kodzie VBA7:podczas wykonywania w środowisku 32-bitowym LongPtr
jest traktowany jako 32-bitowy Long
liczba całkowita, ale w środowisku 64-bitowym LongPtr
jest traktowany jako 64-bitowy LongLong
liczba całkowita.
Deklaracja hCurKey
bo Variant to trochę na skróty. Poniższa adaptacja również zadziała (i będzie działać szybciej, chociaż wzrost prędkości prawdopodobnie będzie niezauważalny dla użytkownika, chyba że zostanie wywołany wiele razy w pętli):
#If VBA7 Then
Private hCurKey As LongPtr
#Else
Private hCurKey As Long
#End If
Jak powiedziałem, powyższe podejście jest bardziej wyraźne w przekazywaniu intencji programisty, działa lepiej i spowoduje więcej błędów w czasie kompilacji niż Private hCurKey As Variant
alternatywa.
Ale jestem znany z tego, że jestem leniwy i Private hCurKey As Variant
jest prawie tak dobre, z dużo mniejszą ilością pisania.
Wykorzystuj swoją wiedzę na dobre
A teraz pamiętasz, co powiedziałem na początku tego artykułu?
Tylko dlatego, że możesz coś zrobić, nie oznacza, że powinieneś.
Napisałem ten artykuł z dwóch powodów:
- Aby zachęcić Cię do wyraźnego zadeklaruj zmienne wariantu
As Variant
- Aby podnieść świadomość na temat tajemnego aspektu VBA, który może Cię potykać, jeśli utrzymujesz (lub kopiujesz i wklejasz) kod innej osoby
NIE napisz ten artykuł, aby zainspirować Cię do pisania instrukcji DefType we własnym kodzie. NIE RÓB TEGO!!! Pamiętaj, że tylko dlatego, że możesz coś zrobić, nie oznacza, że powinieneś.