问题描述
我真的很困惑,将字符串从VBA传递到C ++。这是VBA代码: 私有声明Sub passBSTRVal Libfoo.dll(ByVal s As字符串)
私有声明Sub passBSTRRef Libfoo.dll(ByRef s As String)
私有声明Sub passByNarrowVal Libfoo.dll(ByVal as As String)
私有声明Sub passByNarrowRef Libfoo.dll(ByRef s As String)
私有声明Sub passByWideVal Libfoo.dll(ByVal as As String)
私有声明Sub passByWideRef Libfoo.dll(ByRef s As String)
Sub foobar()
Dim s As String,str As String
str =Hello there,World!
s = str
调用passByBSTRVal()
s = str
调用passByBSTRRef(s)
s = str
调用passByNarrowVal(s)
s = str
调用passByNarrowRef(s)
s = str
调用passByWideVal(s)
s = str
调用passByWideRef(s)
End Sub
而C ++ DLL代码:
void __stdcall passByBSTRVal(BSTR s)
{
MessageBox(NULL,s,LPass BSTR by value,MB_OK | MB_ICONINFORMATION);
$ b void __stdcall passByBSTRRef(BSTR * s)
{
MessageBox(NULL,* s,L通过ref传递BSTR,MB_OK | MB_ICONINFORMATION);
}
void __stdcall passByNarrowVal(LPCSTR s)
{
USES_CONVERSION;
MessageBox(NULL,A2W(s),LPass by Narrow Val,MB_OK | MB_ICONINFORMATION);
}
void __stdcall passByNarrowRef(LPCSTR * s)
{
USES_CONVERSION;
MessageBox(NULL,A2W(* s),LPass by Narrow Ref,MB_OK | MB_ICONINFORMATION);
void __stdcall passByWideVal(LPCWSTR s)
{
MessageBox(NULL,s,LPass by Wide Val,MB_OK | MB_ICONINFORMATION);
}
void __stdcall passByWideRef(LPCWSTR * s)
{
MessageBox(NULL,* s,LPass by Wide Ref,MB_OK | MB_ICONINFORMATION)
}
我的期望是前两次调用passByBSTRVal和passByBSTRRef将会起作用。为什么?因为VBA字符串是COM BSTR对象。然而,在逐步通过C ++代码时,这两个函数的值为垃圾(一堆汉字)。此外,显示的消息框是(相同)。我真的很惊讶前两个函数不起作用。
我的下一个期望是对传递BthNarrowVal和passByNarrowRef的第二个调用不起作用,因为定义了BSTR作为typedef OLECHAR * BSTR,OLECHAR是宽字符类型,而LPCSTR是窄字符类型。但是,与我的期望相反,这两个功能实际上是有效的。当我浏览C ++代码时,参数s正是我期望的。我的期望再次出错。最后,我对最终的2个函数(通过宽val和ref进行传递)的期望是他们会工作,因为OLECHAR是一个字符串的宽字符,所以LPCWSTR应该能够指向一个BSTR。但是与第1案(我猜这两个例子是一样的),我的期望是错误的。参数s由垃圾字符组成(MessageBox显示相同的垃圾字符。)
为什么我的直觉完全错误?有人可以解释一下我在这里的不了解吗?
这种形式的外部函数调用存在与早期版本兼容的Visual Basic,并继承其语义。特别地,VB3在16位窗口上运行,只处理ANSI(即MBCS)字符串。
Declare
语法具有相同的限制。假设VBA将其从UTF-16转换为ASCII,VBA将转换您的字符串。这允许用VB3编写的代码在VB4,VB5和VB6中工作不变。因此,例如AZ以 \\\A\\\Z
开头,转换为ANSI,变为 \x41\x5A
,它被重新解释为 \\\婁
,它是娄。
(使用VB4,Microsoft将WordBasic,Excel Basic和Visual Basic合并为单一语言VBA。)
从VBA调用函数的新方式是使用MIDL为需要使用的外部函数创建一个类型库,并将其添加为项目的引用。类型库可以描述函数的确切签名(例如 BSTR
, LPCSTR
, LPCWSTR
, [out] BSTR *
等)特别是将不需要对象从VBA调用它们(尽管如果你想从VBScript调用它们)。
- 一组DLL函数被描述为一个MIDL
模块
:
或者,您也不用为单个功能启动 midl
,您可以使用 VarPtr
/ StrPtr
/ CopyMemory
hack。这几乎相当于 PEEK
和 POKE
。
I'm really confused about passing strings from VBA to C++. Here's the VBA code:
Private Declare Sub passBSTRVal Lib "foo.dll" (ByVal s As String)
Private Declare Sub passBSTRRef Lib "foo.dll" (ByRef s As String)
Private Declare Sub passByNarrowVal Lib "foo.dll" (ByVal s As String)
Private Declare Sub passByNarrowRef Lib "foo.dll" (ByRef s As String)
Private Declare Sub passByWideVal Lib "foo.dll" (ByVal s As String)
Private Declare Sub passByWideRef Lib "foo.dll" (ByRef s As String)
Sub foobar()
Dim s As String, str As String
str = "Hello There, World!"
s = str
Call passByBSTRVal(s)
s = str
Call passByBSTRRef(s)
s = str
Call passByNarrowVal(s)
s = str
Call passByNarrowRef(s)
s = str
Call passByWideVal(s)
s = str
Call passByWideRef(s)
End Sub
And the C++ DLL code:
void __stdcall passByBSTRVal( BSTR s )
{
MessageBox(NULL, s, L"Pass BSTR by value", MB_OK | MB_ICONINFORMATION);
}
void __stdcall passByBSTRRef( BSTR *s )
{
MessageBox(NULL, *s, L"Pass BSTR by ref", MB_OK | MB_ICONINFORMATION);
}
void __stdcall passByNarrowVal( LPCSTR s )
{
USES_CONVERSION;
MessageBox(NULL, A2W(s), L"Pass by Narrow Val", MB_OK | MB_ICONINFORMATION);
}
void __stdcall passByNarrowRef( LPCSTR* s )
{
USES_CONVERSION;
MessageBox(NULL, A2W(*s), L"Pass by Narrow Ref", MB_OK | MB_ICONINFORMATION);
}
void __stdcall passByWideVal( LPCWSTR s )
{
MessageBox(NULL, s, L"Pass by Wide Val", MB_OK | MB_ICONINFORMATION);
}
void __stdcall passByWideRef( LPCWSTR* s )
{
MessageBox(NULL, *s, L"Pass by Wide Ref", MB_OK | MB_ICONINFORMATION);
}
My expectation was that the first two calls to passByBSTRVal and passByBSTRRef would work. Why? Because VBA strings are COM BSTR objects. However, while stepping through the C++ code, the value of s for both of these functions was garbage (a bunch of kanji). Additionally, the displayed message box was (the same). I'm really surprised the first two functions didn't work.
My next expectation was for the second two calls to passByNarrowVal and passByNarrowRef to not work because a BSTR is defined as "typedef OLECHAR *BSTR", and an OLECHAR is a wide character type, while LPCSTR is a narrow character type. However, contrary to my expectation, these two functions actually did work. When I stepped through the C++ code, the parameter s was exactly what i was expecting it to be. My expectation was wrong again.
Lastly, my expectation for the final 2 functions (pass by wide val and ref) was that they would work, since an OLECHAR is a string of wide characters, so a LPCWSTR should be able to point to a BSTR. But as with case #1 (I guess these two cases are identical) my expectation was wrong. The parameter s was made up of garbage characters (and the MessageBox displayed the same garbage characters.)
Why was my intuition completely wrong? Can someone please explain what I'm not understanding here?
This form of external function call exists to be compatible with earlier versions of Visual Basic, and inherits their semantics. In particular, VB3 ran on 16-bit windows and dealt only with ANSI (i.e. MBCS) strings.
The Declare
syntax has the same restriction. VBA converts your string on the assumption that it is converting it from UTF-16 to ASCII. This allows code written in VB3 to work unchanged in VB4, VB5 and VB6.
So for example "AZ" begins as \u0041\u005A
, is converted to ANSI and becomes \x41\x5A
which is reinterpreted as \u5A41
which is "婁".
(With VB4, Microsoft merged WordBasic, Excel Basic and Visual basic into a single language, VBA.)
The "new" way to call functions from VBA, is to create a type library for the external functions you need to use, using MIDL, and add it as a reference to the project. Type libraries can describe the exact signature of the function, (e.g. BSTR
, LPCSTR
, LPCWSTR
, [out]BSTR*
, etc.) In particular it is not necessary to wrap the functions in a COM object to call them from VBA (though it is if you wish to call them from VBScript).
- A set of DLL functions is described as a MIDL
module
: https://msdn.microsoft.com/en-us/library/windows/desktop/aa367099(v=vs.85).aspx
Alternatively you can't be bothered to fire up midl
for a single function, you can use the VarPtr
/StrPtr
/CopyMemory
hack. This is pretty much equivalent to PEEK
and POKE
.
这篇关于将字符串从VBA传递给C ++ DLL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!