nguyendang95
Thành viên hoạt động



- Tham gia
- 25/5/22
- Bài viết
- 106
- Được thích
- 94
Trong quá trình viết macro VBA trên Excel, chắc hẳn nhiều người dùng nhận ra nhiều hạn chế nhất định của ngôn ngữ lập trình này. DLL, hay thư viện liên kết động, có thể được viết bằng nhiều ngôn ngữ lập trình khác nhau (C/C++, Delphi, TwinBasic, v.v...) để VBA có thể khai báo và sử dụng thông qua từ khóa Declare statement (VBA).
Dưới đây là một ví dụ về việc viết và sử dụng DLL trong VBA, trong ví dụ này sử dụng code mẫu viết bằng TwinBasic và C/C++.
VD: Viết một DLL chứa hàm trả về mảng một chiều kiểu chuỗi gồm hai phần tử.
TwinBasic là một ngôn ngữ lập trình lấy cảm hứng từ VB6 và VB.NET nên cú pháp của nó khá tương đồng, giúp người dùng đã quen với VB có thể dễ dàng làm quen với ngôn ngữ lập trình này. Với thuận lợi này, người dùng có thể dễ dàng viết code một cách liền mạch, xuyên suốt.

Với C/C++, mọi thứ sẽ phức tạp hơn so với TwinBasic do cần phải khai báo và sử dụng nhiều hàm cần thiết để cho ra kết quả cuối cùng. Với VBA, kiểu String thực chất là kiểu BSTR, mảng là SAFEARRAY và Variant tương đương với VARIANT. Khi hàm trả về giá trị, người dùng không cần phải gọi hàm dọn dẹp (trường hợp ở đây là hàm VariantClear), đây là trách nhiệm của VBA sau khi sử dụng xong giá trị.

VBA khai báo và sử dụng hàm trong DLL:
Để khai báo hàm DLL, ta sử dụng cấu trúc như sau:

Dưới đây là một ví dụ về việc viết và sử dụng DLL trong VBA, trong ví dụ này sử dụng code mẫu viết bằng TwinBasic và C/C++.
VD: Viết một DLL chứa hàm trả về mảng một chiều kiểu chuỗi gồm hai phần tử.
TwinBasic là một ngôn ngữ lập trình lấy cảm hứng từ VB6 và VB.NET nên cú pháp của nó khá tương đồng, giúp người dùng đã quen với VB có thể dễ dàng làm quen với ngôn ngữ lập trình này. Với thuận lợi này, người dùng có thể dễ dàng viết code một cách liền mạch, xuyên suốt.

Mã:
Module MainModule
[DllExport]
Public Function GetDemoArray() As Variant
Dim arrResult(0 To 1) As String
For i As Long = LBound(arrResult) To UBound(arrResult)
arrResult(i) = "Đây là phần tử của mảng SAFEARRAY"
Next
Return arrResult
End Function
End Module
Với C/C++, mọi thứ sẽ phức tạp hơn so với TwinBasic do cần phải khai báo và sử dụng nhiều hàm cần thiết để cho ra kết quả cuối cùng. Với VBA, kiểu String thực chất là kiểu BSTR, mảng là SAFEARRAY và Variant tương đương với VARIANT. Khi hàm trả về giá trị, người dùng không cần phải gọi hàm dọn dẹp (trường hợp ở đây là hàm VariantClear), đây là trách nhiệm của VBA sau khi sử dụng xong giá trị.

C++:
#include "pch.h"
#include "comutil.h"
#include "comdef.h"
extern "C" __declspec(dllexport) VARIANT WINAPI GetDemoArray() {
VARIANT varResult;
HRESULT hr;
VariantInit(&varResult);
SAFEARRAYBOUND sab[1]{};
sab[0].cElements = 2;
sab[0].lLbound = 0;
SAFEARRAY* psa = SafeArrayCreate(VT_BSTR, 1, sab);
if (!psa) {
varResult.vt = VT_EMPTY;
return varResult;
}
for (long i = 0; i < 2; i++) {
BSTR bstrVal = SysAllocString(L"Đây là phẩn tử của mảng SAFEARRAY");
if (!bstrVal) {
varResult.vt = VT_EMPTY;
SafeArrayDestroy(psa);
return varResult;
}
hr = SafeArrayPutElement(psa, &i, (LPVOID)bstrVal);
if (FAILED(hr)) {
varResult.vt = VT_EMPTY;
SafeArrayDestroy(psa);
SysFreeString(bstrVal);
return varResult;
}
SysFreeString(bstrVal);
}
varResult.vt = VT_ARRAY | VT_BSTR;
varResult.parray = psa;
return varResult;
}
VBA khai báo và sử dụng hàm trong DLL:
Để khai báo hàm DLL, ta sử dụng cấu trúc như sau:
Mã:
[ Public | Private ] Declare PtrSafe Function name Lib "đường_dẫn_dll" [ ( [ tham_số_hàm_yêu_cầu] ) ] [ As type ]

Mã:
Option Explicit
Private Declare PtrSafe Function GetDemoArray Lib "Y:\data\CPlusPlus\SimpleDll\x64\Release\SimpleDll.dll" () As Variant
'Private Declare PtrSafe Function GetDemoArray Lib "Y:\data\twinBASIC_IDE_BETA\projects\Build\SimpleDll_win64.dll" () As Variant
Private Sub TestDll()
Dim varResult As Variant
varResult = GetDemoArray()
Debug.Print varResult(1)
End Sub
Lần chỉnh sửa cuối: