Windows 11下VBA向C++ DLL传递字符串问题:接收为空
问题概述
在64位Excel(版本2307 Build 16.0.16626.20170)环境下,尝试将VBA字符串传递给Visual Studio 2022编译的x64 Debug版C++ DLL时,始终无法正常接收字符串(参数为空或乱码)。原32位环境下的代码可正常运行,升级64位后故障。已尝试LPSTR、BSTR、LPCSTR三种参数类型,均未解决问题。
尝试过的无效代码示例
1. LPSTR参数版本
C++代码:
// dllmain.cpp : Defines the entry point for the DLL application. #include "pch.h" #include <Windows.h> extern "C" { __declspec(dllexport) void ShowMessage(LPSTR message) { MessageBoxA(NULL, message, "DLL Message", MB_OK); } }
VBA代码:
'Declare function Declare PtrSafe Function ShowMessage _ Lib "MyFunTest.dll" (ByVal message As String) Function VBShowName() As String Dim str_message As String str_message = "Hello World" ShowMessage (str_message) VBShowName = str_message End Function
2. BSTR参数版本
C++代码:
// dllmain.cpp : Defines the entry point for the DLL application. #include "pch.h" #include <Windows.h> #include <oleauto.h> extern "C" { __declspec(dllexport) void ShowMessage(BSTR message) { MessageBoxW(NULL, message, L"DLL Message", MB_OK); } }
3. LPCSTR参数版本
C++代码:
extern "C" __declspec(dllexport) void __stdcall passByNarrowVal(LPCSTR s) { USES_CONVERSION; MessageBox(NULL, A2W(s), L"Pass by Narrow Val", MB_OK | MB_ICONINFORMATION); }
VBA代码:
Declare PtrSafe Function passByNarrowVal _ Lib "MyFunTest.dll" (ByVal s As String) Sub TestNarrow() Dim tline As String tline = "Hello World" passByNarrowVal (tline) End Sub
解决方案
核心问题在于调用约定不匹配和字符串编码处理不当,64位环境下对这两点的要求比32位更严格。
1. 匹配调用约定
VBA的Declare语句默认使用__stdcall调用约定,C++函数必须显式声明该约定,否则会导致参数传递栈异常,出现空值或乱码。
2. 正确处理宽字符字符串(推荐方案)
64位VBA的String类型本质是UTF-16编码的BSTR,直接用BSTR作为C++函数参数是最可靠的方式:
正确C++ DLL代码
// dllmain.cpp : Defines the entry point for the DLL application. #include "pch.h" #include <Windows.h> #include <oleauto.h> extern "C" { // 显式指定__stdcall调用约定,与VBA匹配 __declspec(dllexport) void __stdcall ShowMessage(BSTR message) { // MessageBoxW接收宽字符,BSTR本身就是UTF-16格式,无需额外转换 MessageBoxW(NULL, message, L"DLL Message", MB_OK); } }
对应VBA代码
' 声明PtrSafe适配64位,默认StdCall调用约定与C++匹配 Declare PtrSafe Function ShowMessage _ Lib "MyFunTest.dll" (ByVal message As String) Function VBShowName() As String Dim str_message As String str_message = "Hello World" ' 调用时直接传递字符串,避免多余括号(括号会强制表达式求值,可能引发异常) ShowMessage str_message VBShowName = str_message End Function
3. 可选:传递ANSI字符串(不推荐)
如果需要兼容旧的ANSI逻辑,需在VBA中手动转换编码:
C++代码
extern "C" { __declspec(dllexport) void __stdcall ShowAnsiMessage(LPCSTR message) { MessageBoxA(NULL, message, "DLL Message", MB_OK); } }
VBA代码
Declare PtrSafe Function ShowAnsiMessage _ Lib "MyFunTest.dll" (ByVal message As String) Function VBShowAnsi() As String Dim str_message As String str_message = "Hello World" ' 将VBA的Unicode字符串转换为ANSI后传递 ShowAnsiMessage StrConv(str_message, vbFromUnicode) VBShowAnsi = str_message End Function
额外排查点
- 确保DLL放置在Excel可访问路径(如Excel安装目录、系统目录,或与Excel文件同目录),避免加载旧版本DLL。
- 调试时确认VS加载的是当前编译的x64 Debug版DLL,符号文件匹配。
内容的提问来源于stack exchange,提问作者Brian Howard
相关产品推荐
相关产品推荐

