Chỉ vì bạn có thể làm điều gì đó, không có nghĩa là bạn nên làm.
Tôi tin tưởng sâu sắc vào sự thần thánh của khả năng tương thích ngược. Nhưng nó đi kèm với một mặt tối. Đôi khi những cách làm cũ không còn được ưa chuộng nữa. Việc sử dụng chúng trở nên phức tạp đến mức chúng ta có xu hướng quên chúng thậm chí còn tồn tại.
Vì vậy, nó đi kèm với các câu lệnh DefType.
Điều bạn không biết có thể làm tổn thương bạn
Vài tháng trước, tôi đã viết một bài báo về mô-đun lớp Hoạt động đăng ký của Romke Soldaat.
Tôi đã xuất bản những thay đổi mà tôi đã thực hiện đối với các khai báo API của Romke để làm cho mã chạy trong VBA 64-bit. Mọi lệnh gọi API được gói trong #If VBA7
các thẻ biên dịch có điều kiện và được cập nhật với PtrSafe
từ khóa.
Chỉ có một vấn đề.
Tôi đã quên bao gồm một thay đổi quan trọng mà tôi đã thực hiện đối với một trong các khai báo cấp mô-đun trong mã của Romke. Nếu không có thay đổi này, mã đã sửa đổi của Romke sẽ không biên dịch theo VBA 64-bit. Đã xảy ra lỗi biên dịch trên dòng sau:
Thông báo lỗi là " Loại đối số ByRef không khớp "và biến được đánh dấu là hCurKey
.
Đây là dòng mã vi phạm từ mô-đun lớp ban đầu của Romke:
Private hCurKey
Để khắc phục lỗi biên dịch, dòng mã trên có thể được thay đổi thành sau:
Private hCurKey As Variant
Nhưng khoan đã, bạn nói, không phải hai dòng mã đó làm cùng một việc?!?! Mọi người đều biết rằng nếu bạn không khai báo kiểu của một biến trong VBA thì nó sẽ được khai báo ngầm là một Biến thể. ... Hay là nó?
Rõ ràng Tốt hơn Ngụ ý
Vậy điều gì đang thực sự xảy ra ở đây?
Vấn đề là dòng mã đầu tiên ở trên– Private hCurKey
–Đang xác định biến hCurKey là một Long
loại dữ liệu.
Làm sao chuyện này có thể?
Đó là vì dòng kỳ lạ này ở đầu mô-đun lớp của Romke:
DefLng H-I, L, N
Dòng đó đang làm gì? Nó nói rằng mọi biến được khai báo trong mô-đun hiện tại không có kiểu được khai báo rõ ràng có tên biến bắt đầu bằng H
, I
, L
hoặc N
, sẽ được trình biên dịch coi là Long
kiểu dữ liệu.
Và như vậy, dòng Private hCurKey
đã ngầm hiểu khai báo một kiểu cho biến hCurKey, nhưng khai báo ngầm định là kiểu dữ liệu Dài thay vì một Biến thể.
Tại sao Biến thể Biên dịch But Long Không?
Về lý do tại sao mã biên dịch khi hCurKey
là một Biến thể nhưng không thành công khi nó dài, đó là vấn đề của quá trình chuyển đổi từ 32 bit sang 64 bit.
Để tìm ra nguồn gốc của vấn đề, chúng tôi cần kiểm tra mã đã di chuyển cho khai báo 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
Khi chúng tôi gọi RegCreateKeyEx
từ mã, chúng tôi đang chuyển hCurKey
biến làm đối số thứ hai đến cuối cùng trong hàm. Nói cách khác, nó được chuyển dưới dạng phkResult
lý lẽ. Lưu ý rằng trong phiên bản trước VBA7 (Access 2007 trở về trước), phkResult
được khai báo là Long, nhưng trong phiên bản VBA7, nó được khai báo là LongPtr
.
Đó là bởi vì phkResult
nhận được một tay cầm vào khóa đăng ký đã tạo hoặc đã mở. Bất cứ khi nào bạn thấy từ "xử lý" được liên kết với lệnh gọi API, bạn có thể dịch từ đó một cách an toàn trong đầu thành "địa chỉ bộ nhớ". Đó là lý do tại sao đối số được xác định lại thành LongPtr
trong mã VBA7:khi thực thi trong môi trường 32 bit, LongPtr
được coi là Long
32 bit số nguyên, nhưng trong môi trường 64 bit, LongPtr
được coi là LongLong
64 bit số nguyên.
Khai báo hCurKey
như Biến thể là một chút đường tắt. Sự thích ứng sau đây cũng sẽ hoạt động (và hoạt động nhanh hơn, mặc dù sự gia tăng tốc độ có thể không được người dùng nhận thấy trừ khi nó được gọi nhiều lần trong vòng lặp):
#If VBA7 Then
Private hCurKey As LongPtr
#Else
Private hCurKey As Long
#End If
Như tôi đã nói, cách tiếp cận ở trên rõ ràng hơn trong việc truyền đạt ý định của nhà phát triển, hoạt động tốt hơn và sẽ gây ra nhiều lỗi thời gian biên dịch hơn so với Private hCurKey As Variant
thay thế.
Nhưng tôi được biết đến là người lười biếng và Private hCurKey As Variant
là gần như tốt với việc nhập ít hơn nhiều.
Sử dụng Kiến thức của Bạn cho Tốt
Bây giờ, hãy nhớ những gì tôi đã nói ở đầu bài viết này?
Chỉ vì bạn có thể làm điều gì đó, không có nghĩa là bạn nên làm.
Tôi viết bài này vì hai lý do:
- Để khuyến khích bạn rõ ràng khai báo các biến Variant
As Variant
- Để nâng cao nhận thức về một khía cạnh bí ẩn của VBA có thể khiến bạn khó chịu nếu bạn đang duy trì (hoặc sao chép dán) mã của người khác
TÔI KHÔNG CHƯA viết bài này để truyền cảm hứng cho bạn viết các câu lệnh DefType trong mã của riêng bạn. ĐỪNG LÀM THẾ!!! Hãy nhớ rằng, chỉ vì bạn có thể làm điều gì đó không có nghĩa là bạn nên làm.