VBA Nâng cao: WinAPI - Khai báo Long và LongPtr tương thích đa nền tảng

Liên hệ QC

HeSanbi

Nam Nhân✨Hiếu Lễ Nghĩa Trí Tín✨
Tham gia
24/2/13
Bài viết
2,382
Được thích
3,532
Giới tính
Nam
Nếu các bạn đang hăng say học VBA hoặc đang lập trình VBA và vọc mã WinAPI thì hãy học ngay mẹo này để viết cho ứng dụng của bạn gọn nhẹ hơn. Khi ứng dụng của bạn viết đa nền tảng và đa phiên bản thì gặp phải nhiều vấn đề về tương thích. Nhất là khai báo Long, LongPtr, LongLong, hoặc các hàm VarPtr, StrPtr, ...
Hoặc các bạn cần bộ nhớ kiểu biến lớn hơn là LongLong trong dự án của bạn. Để tận dụng vào các thuật toán mà bạn tạo ra.

Với mẹo vô cùng đơn giản dưới đây là các bạn có thể bỏ qua các dòng mã rườm rà làm mã của bạn rối và khó đọc.

Đơn giản thôi các bạn hãy thêm dòng mã sau vào bất kì dự án nào của bạn có sử dụng các API 32 và 64 bit.


JavaScript:
#If VBA7 = 0 Then
   Public Enum LongLong:[_]:End Enum
   #If Win64 = 0 Then
   Public Enum LongPtr:[_]:End Enum
   #End If
#End If

***Lưu ý: Khi các bạn chép mã vào VBA, nếu các dòng mã Hiện màu đỏ là bình thường nhé. VBA sẽ không thông dịch mã đó, vì hệ điều hành của các bạn tương thích mã đủ điều kiện.
(Mã này không nên đặt vào module có khai báo cục bộ là Option Private Module.
Vì sẽ không thể khai báo tham chiếu biến trong Class và Module Object (Sheet, Form).

Hãy chạy thử thủ tục TestPretreatment dưới đây để xem thông tin máy tính và Office

JavaScript:
Sub TestPretreatment ()
#If VBA7 Then
#If Win64 Then
     Debug.Print "VBA7 64bit"
#Else
     Debug.Print "VBA7 32Bit"
#End If
#ElseIf VBA6 Then
#If Win64 Then
     Debug.Print "VBA6 64bit"
#Else
     Debug.Print "VBA6 32Bit"
#End If
#Else
     Debug.Print "________"
#End If
End Sub



Như đã nói trên, chỉ cần các bạn khai báo như vậy là có thể viết những dòng mã tương thích đa nền tảng mà không cần phải nhờ đến hai PC hoặc hai ứng dụng khác phiên bản để kiểm thử mã, sau khi đã viết mã tương thích.

Ví dụ khai báo như sau, sẽ làm cho mã bị rối và dài:


JavaScript:
#If VBA7 And Win64 Then
Private Sub TimeProc_ApiWindowFlexMove(ByVal hwnd As LongPtr, ByVal wMsg^, ByVal idEvent As LongPtr, ByVal dwTime^)
#ElseIf VBA7 Then
Private Sub TimeProc_ApiWindowFlexMove(ByVal hwnd As LongPtr, ByVal wMsg&, ByVal idEvent As LongPtr, ByVal dwTime&)
#Else
Private Sub TimeProc_ApiWindowFlexMove(ByVal hwnd&, ByVal wMsg&, ByVal idEvent&, ByVal dwTime&)
#End If
End Sub

Bạn chỉ cần khai báo như sau, khi đã chép mã đã nói trên:
JavaScript:
Private Sub TimeProc_ApiWindowFlexMove(ByVal hwnd As LongPtr, ByVal wMsg As LongLong, ByVal idEvent As LongPtr, ByVal dwTime As LongLong)

End Sub


Các khai báo trước đây, như dưới đây hãy bỏ nó đi:

JavaScript:
#If VBA7  Then
Dim hwnd As LongPtr
#Else
Dim hwnd As Long
#End If

Và bây giờ các bạn chỉ cần khai báo, khi có mã đã nói trên, là đủ để tương thích, mà không cần khai báo tiền xử lý #:
JavaScript:
Dim hwnd As LongPtr

Với mẹo khai báo biến Long như vậy, các bạn có thể chép lại mã có các API với các tham số biến LongPtr, LongLong rất đơn giản.

Nếu các bạn chép mã API đang ở nền tảng win64, thì chỉ cần bỏ PtrSafe trong dòng mã để tương thích với Nền tảng win32 (Còn tùy thuộc vào hàm API, có hàm API không có ở Win32).
Bạn không cần sửa LongPtr, LongLong thành Long để tương thích với win32 nữa. Khi đã có mã nói trên.


Lưu ý: Sửa từ API không có PtrSafe sang API có PtrSafe thì có những trường hợp Long phải sửa thành LongPtr hoặc LongLong

Ví dụ khai báo tương thích và các hàm API tương thích đa nền tảng:

JavaScript:
' Khai báo tương thích đa nền tảng
#If VBA7 = 0 Then
   Public Enum LongLong:[_]:End Enum
   #If Win64 = 0 Then
   Public Enum LongPtr:[_]:End Enum
   #End If
#End If
' Các hàm API tương thích đa nền tảng
#If VBA7 Then
Private Declare PtrSafe Function IUnknown_GetWindow Lib "shlwapi" Alias "#172" (ByVal pIUnk As Any, ByVal hwnd As LongPtr) As Long
#Else
Private Declare Function IUnknown_GetWindow Lib "shlwapi" Alias "#172" (ByVal pIUnk As Any, ByVal hwnd As LongPtr) As Long
#End If

#If VBA7 Then
  #If Win64 Then
  Private Declare PtrSafe Function GetWindowLong Lib "user32" Alias "GetWindowLongPtrA" (ByVal hWnd As LongPtr, ByVal nIndex As Long) As LongPtr
  Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongPtrA" (ByVal hWnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
  Declare PtrSafe Function GetClassLong Lib "user32" Alias "GetClassLongPtrA" (ByVal hWnd As LongPtr, ByVal nIndex As Long) As LongPtr
  Private Declare PtrSafe Function SetClassLong Lib "user32" Alias "SetClassLongPtrA" (ByVal hWnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
  #Else
  Private Declare PtrSafe Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As LongPtr, ByVal nIndex As Long) As Long
  Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
  Declare PtrSafe Function GetClassLong Lib "user32" Alias "GetClassLongA" (ByVal hWnd As LongPtr, ByVal nIndex As Long) As Long
  Private Declare PtrSafe Function SetClassLong Lib "user32" Alias "SetClassLongA" (ByVal hWnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
  #End If
#Else
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Declare Function GetClassLong Lib "user32" Alias "GetClassLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetClassLong Lib "user32" Alias "SetClassLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
#End If

Có thể các bạn đang hỏi tại sao lại có kiểu LongPtr. Đơn giản thôi, nó là kiểu khai báo tương thích. Nếu LongPtr ở môi trường 32 bit thì nó là Long, nếu nó ở 64bit thì nó là LongLong. Nó không thực sự là Long và LongLong.

Ngay từ lúc này tôi khuyên các bạn nên tận dụng Kiểu LongLong vì nó khai thác bộ nhớ lớn hơn Long khá nhiều.
Vì khi bạn đang sử dụng Win64 mà lại dùng kiểu Long cho một thuật toán mạnh mẽ thì thật là tiếc nuối cho đà phát triển của bạn.


Mẹo tôi hướng dẫn trên đây sẽ giúp các bạn đơn giản việc viết mã đa nền tảng, các bạn có thể tận dụng nó trên con đường lập trình của mình.
Chúc các bạn thành công!

-------------------------------------------
Các bạn có thể tham khảo thêm bài viết của tôi tại tag
#sanbi udf
 
Lần chỉnh sửa cuối:
Sao lại lôi tôi vào những tranh cãi và chửi nhau ngu? Ai cũng có câu chửi đối phương là ngu thì kêu tôi vào ban nick cả 2 à?
Thánh đấy tự nhận ngu chứ tôi hơi sức nào. Tôi cho thánh vào danh sách "Vip" từ vài năm trước rồi. Kiểu người khoe khoang, học của người xong lấy làm của mình. Rồi lại đi khoe lại cho người.

Không xứng đáng để tôi bỏ vài giây để "xoi"
 
Upvote 0
Không thể đem cái file TLB ứng dụng trong VB6 để lập trình cho môi trường VBA được bởi vì VBA (từ Office 2010 trở lên) người lập trình phải tích hợp cả hai tình huống 32-bit và 64-bit trong một project, trong khi VB6 chỉ là 32-bit.
File TLB tạo ra bởi C++ hoặc Delphi hay IDE nào đó nó (biết định nghĩa cho Windows hiểu) không có kiểu LongLong hay LongPtr, Any vì thế file định nghĩa các hàm API khó có thể thể bao đủ tính huống, cách áp dụng trong VBA, hay có thể làm cho linh động so với việc hiểu và tự khai báo API.
Bản chất những gì khai báo trong file TLB rồi trong VBA lại nhúng vào thì VBA vẫn phải nạp vạp bộ nhớ thì trình biên dịch mới compile được chứ. Vì thế việc nhúng TLB vào với các khai báo các hàm API thừa thãi sẽ làm ứng dụng bị chiếm dụng bộ nhớ.

Tôi thấy topic này của tác giả là hữu ích cho mọi người cần học lập trình API trong VBA nên để người ta vui vẻ và tâm huyết làm.
 
Upvote 0
Không thể đem cái file TLB ứng dụng trong VB6 để lập trình cho môi trường VBA được bởi vì VBA (từ Office 2010 trở lên) người lập trình phải tích hợp cả hai tình huống 32-bit và 64-bit trong một project, trong khi VB6 chỉ là 32-bit.
File TLB tạo ra bởi C++ hoặc Delphi hay IDE nào đó nó (biết định nghĩa cho Windows hiểu) không có kiểu LongLong hay LongPtr, Any vì thế file định nghĩa các hàm API khó có thể thể bao đủ tính huống, cách áp dụng trong VBA, hay có thể làm cho linh động so với việc hiểu và tự khai báo API.
Bản chất những gì khai báo trong file TLB rồi trong VBA lại nhúng vào thì VBA vẫn phải nạp vạp bộ nhớ thì trình biên dịch mới compile được chứ. Vì thế việc nhúng TLB vào với các khai báo các hàm API thừa thãi sẽ làm ứng dụng bị chiếm dụng bộ nhớ.

Tôi thấy topic này của tác giả là hữu ích cho mọi người cần học lập trình API trong VBA nên để người ta vui vẻ và tâm huyết làm.
Tôi không tranh cải thớt này nữa .... Tôi nói lại là trước đây họ viết TLB chỉ có 32 bít sử dụng cho VB6 ... thì này họ thêm cả 64 bít và sử dụng cho VBA rồi bao gồm cả các hằng khai báo cho 64 bít

còn các chuyện khác tôi không bàn cải

Tôi đưa ra 1 ví dụ còn bạn tự suy nghĩ

1/ Khi cái Atools.dll của Bạn mang hết các hàm đó viết vào trong 1 File trên Module VBA thì khi mở File Excel lên buộc nó sẽ tính toán lại mọi cái có trên Excel

2/ Khi Check Atools.dll khai báo COM kết nối sớm cho VBA thì khi nào bạn cần sử dụng Hàm liên quan Tới Atools.dll thì nó mới chi phí thời gian phát sinh và chạy code còn không thì thôi nên nó sẽ rất nhanh hơn viết hết tất cả vào 1 Module trên VBA

3/ chi phí thời gian cho tham chiếu thư viện ngoài là không đáng kể ... quá nhỏ

.................
 
Lần chỉnh sửa cuối:
Upvote 0
Trong lúc chúng ta đang tranh cãi về sử dụng API 32 64
Thì người ta đã đưa chúng lên công nghệ web, đám mây từ lâu, việc cập nhật bấy giờ đơn giản do người chủ server quản lý (hay những nhà cung cấp dịch vụ quản lý) thay vì phải lo việc sai khác nữa.

Nên có thể bàn cứ bàn, có ích thì còn có ích cho chúng ta, và vài ứng dụng vừa trong 1 application hay môi trường cụ thể.
Còn để có giá trị hữu ích thật thì ta phải thay đổi từ hệ thống quan niệm. Và ngay cả cách chúng ta bàn luận và để xây dựng phát triển.

Góc độ tôi, thì hạn chế sử dụng các API _vì ta đâu biết mai họ (hệ điều hành) lại thay đổi, cực chẳng đã buộc phải áp dụng - thường xảy ra khi muốn thiệp vào hệ thống, giao diện , con trỏ (mouse) ...vvv
 
Lần chỉnh sửa cuối:
Upvote 0
LongPtr để vào đâu cũng không được chấp nhận. Có lẽ câu lệnh đó không đúng.
Máy Bác @Maika8008 còn lỗi này không.

Nếu còn bác thử với mã sau xem sao, chạy thử thủ tục TestPretreatment để xem trong Immediate giá trị trả lại.

JavaScript:
#If (VBA6 And Win64 = 0) Or (VBA7 = 0 And VBA6 = 0) Then
  Public Enum LongLong:[_]:End Enum
  Public Enum LongPtr:[_]:End Enum
#End If

Sub TestPretreatment ()
#If VBA7 Then
#If Win64 Then
     Debug.Print "VBA7 64bit"
#Else
     Debug.Print "VBA7 32Bit"
#End If
#ElseIf VBA6 Then
#If Win64 Then
     Debug.Print "VBA6 64bit"
#Else
     Debug.Print "VBA6 32Bit"
#End If
#Else
     Debug.Print "________"
#End If
End Sub
 
Upvote 0
Máy Bác @Maika8008 còn lỗi này không.

Nếu còn bác thử với mã sau xem sao, chạy thử thủ tục TestPretreatment để xem trong Immediate giá trị trả lại.

JavaScript:
#If (VBA6 And Win64 = 0) Or (VBA7 = 0 And VBA6 = 0) Then
  Public Enum LongLong:[_]:End Enum
  Public Enum LongPtr:[_]:End Enum
#End If

Sub TestPretreatment ()
#If VBA7 Then
#If Win64 Then
     Debug.Print "VBA7 64bit"
#Else
     Debug.Print "VBA7 32Bit"
#End If
#ElseIf VBA6 Then
#If Win64 Then
     Debug.Print "VBA6 64bit"
#Else
     Debug.Print "VBA6 32Bit"
#End If
#Else
     Debug.Print "________"
#End If
End Sub
Nó báo lỗi identifier chỗ LongPtr bạn à. Nếu sửa LongPtr thành gì đó (chẳng hạn LongPtrA) thì Sub TestPretreatment chạy cho ra kết quả là: VBA7 32Bit.

Máy tôi: Win 64 và Office 32
 
Upvote 0
@Maika8008 Bác thử thêm lần nữa mã dưới đây xem sao

JavaScript:
#If VBA7 Then
#ElseIf VBA6 Then
#Else
  Public Enum LongLong:[_]:End Enum
  Public Enum LongPtr:[_]:End Enum
#End If
 
Upvote 0
Nếu khôn ấy thì từng bước loại bỏ sự lệ thuộc vào các hàm API của Ms ra xong sử dụng bất kỳ 1 Tools lập trình hiện đại nào đang phát triển mà viết

trừ khi không có khả năng thì loanh quan luẩn quẩn tái sử dụng lại các hàm API của Ms

Việc tái sử dụng hàm API có nhiều hàm Ms không công bố hoặc không có chỉ dẫn mà do người dùng mò mẫm xong viết các hàm trung gian và khai báo các hằng tùy chỉnh cho nó nên có trường hợp đúng trên máy này và sai trên máy khác xong lại mò tiếp ....

còn trên VBA thì quá lỗi thời , tù túng và ọp ẹp khi muốn bứt phá ra và phát triển lên 1 tầm cao mới không có gì hay cả khi dựa vào nền tảng đã phát triển mấy chục năm trước không còn phát triển nữa

cứ loanh quanh luẩn quẩn hoài trên VBA xong tái sử dụng các Hàm API khai báo kiểu mò mẫm thì không phải là giải pháp khôn lắm --=0
 
Lần chỉnh sửa cuối:
Upvote 0
Cũng vậy thôi. Máy không chấp nhận LongPtr.
Máy của bác Thông dịch cả mã nằm trong khối phủ định của tiền xử lý, vẫn không hiểu sao lại có chuyện này xảy ra.

Nhét LongPtr tới tận Win16 bit, mà còn lỗi nữa thì không có cách nào khác. (win16 thì không còn dành cho người dùng cuối)

JavaScript:
#If VBA7 Then
#ElseIf VBA6 Then
#Else
    #If Win64 Then
    #ElseIf Win32 Then
    #Else
          Public Enum LongLong:[_]:End Enum
          Public Enum LongPtr:[_]:End Enum
    #End If
#End If


Không biết GPE, máy tính bạn nào chạy Windows 11 64bit và Office 32bit có gặp điều tương tự không.
 
Lần chỉnh sửa cuối:
Upvote 0

Theo hình chụp thì bạn khai báo sai kìa. Sao lại #ElseIf Win32. Đã test cả Windows XP, Office 2003 cũng Ok.

Máy tôi Windows 10 64-bit, Office 365 32-bit không lỗi.
Code điều hướng biên dịch trong VBA không cho phép sử dụng các toán tử NOT, AND, OR, = để kết hợp tạo biểu thức. Nếu đưa vào là lỗi, tôi từng kiểm nghiệm việc này. Vậy nên chỉ khai báo code chân phương như #IF VBA7 hay #IF VBA6 , #ElseIf VBA7 (hoặc VBA6) thì OK.
 
Upvote 0
Máy của bác Thông dịch cả mã nằm trong khối phủ định của tiền xử lý, vẫn không hiểu sao lại có chuyện này xảy ra.

Nhét LongPtr tới tận Win16 bit, mà còn lỗi nữa thì không có cách nào khác. (win16 thì không còn dành cho người dùng cuối)

JavaScript:
#If VBA7 Then
#ElseIf VBA6 Then
#Else
    #If Win64 Then
    #ElseIf Win32 Then
    #Else
          Public Enum LongLong:[_]:End Enum
          Public Enum LongPtr:[_]:End Enum
    #End If
#End If
Vẫn thế thôi bạn. Nằm ở đâu thì nó vẫn từ chối.
 
Upvote 0
Máy của bác Thông dịch cả mã nằm trong khối phủ định của tiền xử lý, vẫn không hiểu sao lại có chuyện này xảy ra.

Nhét LongPtr tới tận Win16 bit, mà còn lỗi nữa thì không có cách nào khác. (win16 thì không còn dành cho người dùng cuối)

JavaScript:
#If VBA7 Then
#ElseIf VBA6 Then
#Else
    #If Win64 Then
    #ElseIf Win32 Then
    #Else
          Public Enum LongLong:[_]:End Enum
          Public Enum LongPtr:[_]:End Enum
    #End If
#End If


Không biết GPE, máy tính bạn nào chạy Windows 11 64bit và Office 32bit có gặp điều tương tự không.

Máy tôi không bị lỗi nhé. Windows 11 64bit + Office 2013 32bit.

Screen Shot 2023-10-11 at 12.10.56.png


@cantl: code bị đỏ trong khai báo cho Win32/64 là bình thường khi nó ở các môi trường khác nhau, quan trong là Compile không báo lỗi.
 
Upvote 0
Lần chỉnh sửa cuối:
Upvote 0
@Maika8008 @cantl
Trời đất, mã màu đỏ không phải là lỗi, mà là mã tương thích giữa các Windows và Office. VBA sẽ đọc mã đủ điều kiện để thông dịch.
Vì #If là tiền xử lý mã.

Vậy là lâu nay bỏ công đi tìm lỗi, là do các Bác hiểu sai về cách VBA thông dịch mã

Không biết có ai nhầm giữa thông dịch và biên dịch không? "chạy đâu khỏi nắng"
 
Lần chỉnh sửa cuối:
Upvote 0
VBA không cho phép sử dụng các toán tử NOT, AND, OR, = để kết hợp tạo biểu thức. Nếu đưa vào là lỗi, tôi từng kiểm nghiệm việc này
Chắc anh có hiểu lầm gì rồi.
Hoàn toàn không có lỗi khi sử dụng các toán tử này.

MAC, VBA7, VBA6, VBA5, Win64, Win32, Win16 là các đối số biên dịch có giá trị 0 hoặc 1

Sử dụng các toán tử bình thường, nhưng hãy so sánh các đối số này với 0, như sau:

VBA7 <> 0 AND WIN64 <> 0
NOT -VBA7 sẽ trả về 0 (False), NOT VBA7 là NOT 1 trả về -2 vẫn là TRUE.

Các toán tử không phải trả về Boolean, đã đề cập tại bài viết, không biết anh đã đọc qua bài viết này chưa?

 
Upvote 0
@Maika8008 @cantl
Trời đất, mã màu đỏ không phải là lỗi, mà là mã tương thích giữa các Windows và Office. VBA sẽ đọc mã đủ điều kiện để thông dịch.
Vì #If là tiền xử lý mã.

Vậy là lâu nay bỏ công đi tìm lỗi, là do các Bác hiểu sai về cách VBA thông dịch mã

Không biết có ai nhầm giữa thông dịch và biên dịch không? "chạy đâu khỏi nắng"
Xin lỗi đã làm cho bạn phải vất vả. Tôi không biết thông dịch với biên dịch, cứ thấy màu đỏ thì nghĩ là lỗi gì rồi.
 
Upvote 0
Web KT
Back
Top Bottom