Bước đầu tiên làm quen với gọi các chương trình từ bên ngoài bằng windows API

Euler

Mod
Thành viên BQT
Chào mọi người. Chúng tôi là diễn đàn tuhocvba.net
Hiện nay diễn đàn chưa thể thành lập Box API riêng, nhưng chưa nhận được sự ủng hộ cổ vũ của mọi người.
Trong khả năng của mình, chúng tôi sẽ trình bày dưới dạng các topic nhỏ từ các nguồn tham khảo internet, có thể chưa có tính hệ thống, với mong muốn giúp các bạn làm quen dần với API.

Với Excel VBA, chúng ta làm được thật là nhiều việc, quả là tiện lợi phải không các bạn.
Thế nhưng, có những thứ chúng ta thấy rằng, nếu như tính năng này có thể được dùng trên Excel thì tiện biết bao, ví dụ như sử dụng chuột giữa để di chuyển dữ liệu hiển thị trên listbox, hoặc nếu như có thể kết hợp tính năng này vào Excel thì còn tiện hơn nữa ấy nhỉ…
Và để biến những điều đó thành hiện thực, chúng ta hãy làm quen với API.

Windows API là gì?
Đầu tiên, API là các chữ cái đầu của chữ Application Programming Interface. Đây là một thủ đoạn để có thể gọi các chương trình từ bên ngoài. Nó đóng vai trò là cầu nối giữa chương trình chúng ta muốn gọi và chức năng mà chúng ta muốn dùng.
Và tất nhiên, từ Excel VBA, chúng ta cũng có thể làm được điều đó, bằng cách gọi (các chương trình từ bên ngoài) các tính năng của hệ điều hành windows ra phục vụ chúng ta.

Windows API và DLL
Trong khi sử dụng Windows API, chúng ta sẽ nhắc nhiều tới DLL.
Ví dụ: User32.DLL
DLL là các chữ cái viết tắt cảu từ Dynamic Link Library. Nó là một tập hợp các hàm mà các ứng dụng như Excel có thể gọi chúng ra và sử dụng.
Trường hợp chúng ta sử dụng Windows API để gọi các tính năng ngoài phạm vi của Excel, thì:
1. Bằng Windows API, chúng ta sẽ chỉ định gọi hàm bên trong DLL và trao cho nó tham số đầu vào.
2. Bằng Windows API, chúng ta sẽ làm cho hàm được chỉ định bên trong DLL tiến hành xử lý theo ý đồ và mục đích của chúng ta.
3. Thông qua Windows API, kết quả xử lý của hàm trong DLL sẽ được trả về Excel VBA.

Windows API có sẵn với số lượng rất lớn
Các hàm Windows API được công khai có số lượng rất lớn, các trang web của nước ngoài liệt kê rất nhiều, nhưng ở Việt Nam, cho tới nay chỉ có caulacbovb.com là có topic trình bày cụ thể hơn cả. tuhocvba.net tiếp cận từ hướng Excel, nên sẽ có lựa chọn giới thiệu hàm gì mà chúng tôi cho là hữu ích, cách trình bày sẽ tuân thủ theo phương châm của diễn đàn từ trước tới nay, đó là dễ hiểu, và dễ áp dụng. Thành viên của diễn đàn cũng có thể giới thiệu các hàm API mà các bạn tâm đắc nhưng cũng cần tuân thủ cách trình bày dễ hiểu, đưa ra ví dụ áp dụng.
Về thư viện các hàm API được công khai, các bạn có thể tham khảo thêm ở đây:

Thực hành: Sử dụng Windows API để tạo âm thanh cảnh báo giống Windows System

Khai báo về việc sử dụng API bằng từ khóa Declare
Để có thể sử dụng Windows API từ Excel VBA, chúng ta cần phải khai báo, tôi muốn sử dụng chương trình có tên là … và đang nằm ở ….
Và để thực hiện khai báo này, ta phải dùng từ khóa Declare.

Private Declare Function tên_chương_trình_API LibTên_DLL” (tham số) As Kiểu

Đàu tiên, tên chương trình chúng ta muốn dùng để tạo ra âm thanh cảnh báo của windows là MessageBeep.
Và, MessageBeep được cất trong file User32.DLL, chúng ta sử dụng tên file DLL thì giản lược và viết là “User32”.
Hơn nữa, MessageBeep cần có tham số truyền vào, tham số ở đây là số hiệu âm thanh mà bạn muốn dùng (có rất nhiều loại âm thanh cảnh báo, ứng với nó sẽ là các số hiệu khác nhau).
Hàm này có kiểu giá trị trả về là số nguyên integer.
Tóm lại hàm này được khai báo như sau:
Private Declare Function MessageBeep Lib “user32” (ByVal sType As Integer) As Integer

Bây giờ ta sẽ ứng dụng nó trong thực tế như dưới đây:
Mã:
#If Win64 then
    Private Declare PtrSafe Function MessageBeep Lib "user32" (ByVal sType As Integer) As Integer
#Else
    Private Declare Function MessageBeep Lib "user32" (ByVal sType As Integer) As Integer
#End if
Sub Sound_Test()
    MessageBeep (20)
End Sub
Điều đương nhiên đó là chúng ta phải biết chính xác tên hàm API. Nếu viết sai thì chương trình sẽ bị lỗi, hãy chú ý.
Nguồn tham khảo:
 

tuhocvba

Administrator
Thành viên BQT
MesssageBeep nghe như tiếng lúc vào Win. Nhân đây mình cũng muốn giới thiệu một công cụ sẵn có cây nhà lá vườn của VBA, không phải hàm API.
Đó là Beep. Tiếng này rất phù hợp khi chúng ta muốn đưa ra thông báo dừng hay cảnh báo lỗi.
Mã:
Sub Sample()
    Dim i As Integer
   
    For i = 1 To 5 'Cho kêu 5 lần, mỗi lần kêu xong chờ 1s để chuyển sang lần kêu tiếp theo.
        Beep
        Application.Wait Now() + TimeSerial(0, 0, 1)
    Next i
End Sub
-------------------
Phát hiện ra tiếng kêu của Beep giống với tiếng kêu của MessageBeep khi tham số truyền vào là 1.
Mã:
MessageBeep (1)
Nguồn:
 

bvtvba

Thành viên tích cực
Beep dịch ra là câu lệnh(Statement) thì đúng hơn.
Nguồn:
Không chắc là một hàm (function) hay thủ tục (sub).
Sử dụng âm thanh trong chương trình VBA là một ý tưởng rất hay, vì nhiều khi dùng hai màn hình, mặc dù trong code đã sử dụng Msgbox rồi, nhưng mắt vẫn không nhìn thấy vì hộp thoại cảnh báo chương trình kết thúc vì nó hiện ở màn hình bên kia mà mình thì lại không để ý. :oops:
 

tuhocvba

Administrator
Thành viên BQT
Cho tới Office 2007 thì là VBA6 32bit. Nhưng từ Office 2010 trở lại đây thì là VBA7 bao gồm cả 32bit và 64bit.
Về phiên bản VBA thì trên soạn thảo code VBE các bạn vào phần HELP là xem được thông tin.

Khi khai báo sử dụng các hàm WIN32API thì tùy vào phiên bản VBA mà có sự khác biệt.
Chẳng hạn:
Mã:
'// VBA 7
Declare PtrSafe Function GetKeyboardLayout Lib "user32" Alias "GetKeyboardLayout" (ByVal dwLayout As Long) As LongPtr
'// VBA 6
Declare PtrSafe Function GetKeyboardLayout Lib "user32" Alias "GetKeyboardLayout" (ByVal dwLayout As Long) As Long
Hãy nhìn vào phía cuối các dòng code. Với VBA7 thì kiểu giá trị trả về là LongPtr. Với VBA6 thì kiểu giá trị trả về là Long.
LongPtr trong trường hợp 32bit thì nó là kiểu Long: -2,147,483,648 ~ 2,147,483,647 .
LongPtr trong trường hợp 64bit thì nó là kiểu LongLong : -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 .

Tôi nghe nói các bạn phải cài đặt exe để nhận help về các hàm WIN32API. Tôi thì không thích EXE, nay cung cấp cho các bạn .
Các bạn có thể download về phong trường hợp trong tương lai đường link có thể bị thay đổi.

File TXT này được tổng hợp từ VBA7.0 trở đi. Giả sử bạn muốn dùng cho VBA 6.0 thì cần thay đổi LongPtr thành Long như ví dụ ở trên.
Về PtrSafe , dù cho bạn không xóa từ khóa này thì 32bit vẫn hoạt động. Vì vậy từ nay chúng ta-các thành viên tuhocvba.net thống nhất với nhau, khai báo hàm API thì cứ viết PtrSafe vào.

WIN32API thường sử dụng cấu trúc riêng cho đối số hoặc giá trị trả về của nó.
Chẳng hạn: Hàm GetLocalTime lấy thời gian hiện tại, đối số của nó được định nghĩa riêng là kiểu SYSTEMTIME .
Trong trường hợp này, thì không chỉ khai báo hàm API, bạn còn phải khai báo kiểu SYSTEMTIME .
Mã:
Type SYSTEMTIME
    wYear As Integer
    wMonth As Integer
    wDayOfWeek As Integer
    wDay As Integer
    wHour As Integer
    wMinute As Integer
    wSecond As Integer
    wMilliseconds As Integer
End Type

Declare PtrSafe Sub GetLocalTime Lib "kernel32" Alias "GetLocalTime" (lpSystemTime As SYSTEMTIME)
Thử với code sau:
Mã:
Sub GetLocalTimeTest()
    Dim t   As SYSTEMTIME
    Dim s
  
    '// Lấy ngày giờ hiện tại
    Call GetLocalTime(t)
  
    '//Định dạng là yyyy/mm/dd hh:mm:ss.fff
    s = Format(t.wYear, "0000")
    s = s & "/"
    s = s & Format(t.wMonth, "00")
    s = s & "/"
    s = s & Format(t.wDay, "00")
    s = s & " "
    s = s & Format(t.wHour, "00")
    s = s & ":"
    s = s & Format(t.wMinute, "00")
    s = s & ":"
    s = s & Format(t.wSecond, "00")
    s = s & "."
    s = s & Format(t.wMilliseconds, "000")
  
    Debug.Print s
End Sub
Kết quả là:
Mã:
2018/09/24 00:06:15.138
Như bạn đã thấy, sự khác biệt giữa hàm VBA và hàm WIN32API đó là, bạn phải khai báo hàm WIN32API nếu muốn sử dụng chúng.
Tham khảo:
 

NhanSu

Thành Viên Nổi Bật

Mình giải thích thêm một chút. Để tra cứu hàm các bạn có thể vào link ở bài 4 của admin @tuhocvba hoặc (truy cập link dowload của MS ta được file exe nhưng thực chất là file nén tự bung, nếu các bạn ngại chạy nó thì có thể bấm chuột phải, chọn Open with winrar 2 lần để lấy file TXT). Ví dụ ta muốn tra hàm SetWindowLong, tìm trong file đó ta được (mình đã xóa bỏ các dòng khai báo hàm khác):
Mã:
#If Win64 Then
Declare PtrSafe Function SetWindowLongPtr Lib "user32" Alias "SetWindowLongPtrA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
#Else
Declare PtrSafe Function SetWindowLongPtr Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
' Provided for reference only.  Please use the LongPtr versions instead.
Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
#End If
Ở đây ta thấy cấu trúc
Mã:
#if điều_kiện then
#else
#end if
Cấu trúc đầy đủ là
Mã:
#Const ĐK1 = logical_value1
#Const ĐK2 = logical_value2
...
#IF ĐK1 then
Khối lệnh 1
#ELSEIF ĐK2 then
Khối lệnh 2
#ELSE
Khối lệnh 3
#ENDIF
Đây gọi là dẫn hướng biên dịch (compiler directive, VBA là thông dịch nhưng trên internet vẫn hay dùng lẫn lộn 2 khái niệm này) , điều kiện được xác định ngay từ đầu khi dịch chương trình, nếu ĐK1=true thì khối lệnh 1 sẽ được dịch, VBA sẽ không quan tâm đến các khối lệnh khác nữa. Cấu trúc này khác rẽ nhánh If then thông thường (điều kiện rẽ nhánh được xác định khi chuơng trình chạy).
WIN64 được định nghĩa trước và xác định khi bắt đầu dịch chuơng trình nên ta không cần khai báo #Const nữa, WIN64 = TRUE ở excel 64 bit và FALSE khi dùng excel 32 bit (chú ý WIN64=TRUE không phải là windows 64 bit; windows 64 có thể cài excel 32 hoặc 64 bit, windows 32 chỉ cài được excel 32 bit).
Khi khai báo hàm, nếu trong lệnh Declare có Alias thì tên ở sau Alias (SetWindowLongA, SetWindowLongPtrA) chính là tên thật của hàm trong thư viện API còn tên SetWindowLong hay SetWindowLongPtr có thể thay đổi được tùy ý.
Đối với VBA6 (office 2007 trở xuống), ta sẽ tra cứu trong file Win32API.txt được
Mã:
Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Ta sẽ đổi tên hàm thành SetWindowLongPtr cho khớp với VBA7
Như vậy đối với khai báo trên ta sẽ sửa lại là
Mã:
#If Win64 Then
'excel 64 bit
Declare PtrSafe Function SetWindowLongPtr Lib "user32" Alias "SetWindowLongPtrA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
#Elseif VBA7 then
'excel 32 bit, >=2010
Declare PtrSafe Function SetWindowLongPtr Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
#Else
'excel 32 bit, <=2007
Declare Function SetWindowLongPtr Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
#End If
hoặc có thể ngắn gọn thành
Mã:
#If Win64 Then
'excel 64 bit
Declare PtrSafe Function SetWindowLongPtr Lib "user32" Alias "SetWindowLongPtrA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
#Else
'excel 32 bit
Declare Function SetWindowLongPtr Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
#End If
 
Sửa lần cuối:

BKKBG

Thành viên
Mình giải thích thêm một chút.
Mã:
#If Win64 Then
Declare PtrSafe Function SetWindowLongPtr Lib "user32" Alias "SetWindowLongPtrA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
#Else
Declare PtrSafe Function SetWindowLongPtr Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As LongPtr
#End If
Tại sao dòng lệnh 4, giá trị trả về của hàm lại là As LongPtr nhỉ, theo mình hiểu thì là As Long. Có đúng không?
 

NhanSu

Thành Viên Nổi Bật

LongPtr (long pointer) là kiểu dữ liệu trong VBA7, nó là long (4 bytes) với excel 32 bit và 8 bytes với excel 64 bit.
 

BKKBG

Thành viên
Cảm ơn bạn @NhanSu đã giải thích. Tôi đã hiểu, vừa đọc #4 đã hiểu ra rồi:
LongPtr trong trường hợp 32bit thì nó là kiểu Long: -2,147,483,648 ~ 2,147,483,647 .
LongPtr trong trường hợp 64bit thì nó là kiểu LongLong : -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 .
 
Top