从 VBA 中的 (c++ BSTR) 字符串中删除 chr(0)
Remove chr(0) from (c++ BSTR) string in VBA
我在 DLL 中编写了一个 C++ 函数,它将字符串导出到 VBA 程序:
BSTR _stdcall myFunc()
{
CRegKey Key;
CString sValue;
BSTR Str;
LONG nA = Key.Open(HKEY_LOCAL_MACHINE, _T("[group path goes here]"), KEY_READ);
ULONG nValueLength = 0;
LONG nB = Key.QueryStringValue(_T("[key I want to read goes here]"), NULL, &nValueLength);
if (nValueLength > 0)
{
LONG nC = Key.QueryStringValue(_T("[key I want to read goes here]"), sValue.GetBufferSetLength(nValueLength - 1), &nValueLength);
}
Str = _bstr_t(sValue.AllocSysString(), false);
return Str;
现在 Str
类似于版本号:比方说“4.10.122”。
如果我从 VBA 调用该函数,我收到的是“4 . 1 0 . 1 2 2”,其中每个字符之间的 "spaces" 是 NULL
(在 VBA 中它们是 Chr(0)
).
我不喜欢在我的 VBA 代码中使用 Replace
函数的想法,那么有什么办法可以在我的 C++ 代码中包含该步骤吗?
编辑:在我用来调用 VBA 中的函数的代码下方:
Private Declare Function myFunc Lib "[Path of my DLL here]" () As String
Sub Return_string()
Dim a As String
a = myFunc()
End Sub
远远不能帮助您解决这个特定需求,但对这个主题很好奇,如果我想出一个解决方案,我会从这个答案开始
更新
根据上面提到的@SimonMourier 的详细回答和进一步的阅读,你应该考虑字符串内容编码。
DLL 的上下文 - VBA 编组要考虑并且还取决于 VBA 函数声明(由@HansPassant 声明)。
- 当 VBA 用户定义函数被声明为采用字符串参数时,Excel 以特定于语言环境的方式将提供的字符串转换为字节字符串。
- 如果您希望向您的函数传递 Unicode 字符串,您的 VBA 用户定义函数应该接受 Variant 而不是 String 参数。
所以如果你使用:
Private Declare Function myFunc Lib "[Path of my DLL here]" () As String
需要通过 StrConv
从 UNICODE 转换为 ANSI
Dim str As String
str = StrConv(myFunc(), vbFromUnicode)
否则你应该摆脱 StrConv
但使用 DLL 导出 BSTR
与此声明:
Private Declare Function myFunc Lib "[Path of my DLL here]" () As Variant
问题源于Declare
语句。特别是与此类函数和字符串交互时,VBA 将始终在两个方向上执行隐式 ANSI 到 Unicode 转换。在您的情况下,VBA 需要一个 ANSI 字符串作为 return 值,然后它可以扩展为 Unicode 等价物。
要解决这个问题,您必须 return 一个 ANSI 字符串并求助于 SysAllocStringByteLen
:
CStringA sValueA(sValue);
Str = SysAllocStringByteLen(sValueA.GetBuffer(), sValueA.GetLength());
作为替代方案,您也可以在 DLL 中嵌入类型库。这将省略自动字符串转换。
我在 DLL 中编写了一个 C++ 函数,它将字符串导出到 VBA 程序:
BSTR _stdcall myFunc()
{
CRegKey Key;
CString sValue;
BSTR Str;
LONG nA = Key.Open(HKEY_LOCAL_MACHINE, _T("[group path goes here]"), KEY_READ);
ULONG nValueLength = 0;
LONG nB = Key.QueryStringValue(_T("[key I want to read goes here]"), NULL, &nValueLength);
if (nValueLength > 0)
{
LONG nC = Key.QueryStringValue(_T("[key I want to read goes here]"), sValue.GetBufferSetLength(nValueLength - 1), &nValueLength);
}
Str = _bstr_t(sValue.AllocSysString(), false);
return Str;
现在 Str
类似于版本号:比方说“4.10.122”。
如果我从 VBA 调用该函数,我收到的是“4 . 1 0 . 1 2 2”,其中每个字符之间的 "spaces" 是 NULL
(在 VBA 中它们是 Chr(0)
).
我不喜欢在我的 VBA 代码中使用 Replace
函数的想法,那么有什么办法可以在我的 C++ 代码中包含该步骤吗?
编辑:在我用来调用 VBA 中的函数的代码下方:
Private Declare Function myFunc Lib "[Path of my DLL here]" () As String
Sub Return_string()
Dim a As String
a = myFunc()
End Sub
远远不能帮助您解决这个特定需求,但对这个主题很好奇,如果我想出一个解决方案,我会从这个答案开始
更新
根据上面提到的@SimonMourier 的详细回答和进一步的阅读,你应该考虑字符串内容编码。 DLL 的上下文 - VBA 编组要考虑并且还取决于 VBA 函数声明(由@HansPassant 声明)。
- 当 VBA 用户定义函数被声明为采用字符串参数时,Excel 以特定于语言环境的方式将提供的字符串转换为字节字符串。
- 如果您希望向您的函数传递 Unicode 字符串,您的 VBA 用户定义函数应该接受 Variant 而不是 String 参数。
所以如果你使用:
Private Declare Function myFunc Lib "[Path of my DLL here]" () As String
需要通过 StrConv
Dim str As String
str = StrConv(myFunc(), vbFromUnicode)
否则你应该摆脱 StrConv
但使用 DLL 导出 BSTR
与此声明:
Private Declare Function myFunc Lib "[Path of my DLL here]" () As Variant
问题源于Declare
语句。特别是与此类函数和字符串交互时,VBA 将始终在两个方向上执行隐式 ANSI 到 Unicode 转换。在您的情况下,VBA 需要一个 ANSI 字符串作为 return 值,然后它可以扩展为 Unicode 等价物。
要解决这个问题,您必须 return 一个 ANSI 字符串并求助于 SysAllocStringByteLen
:
CStringA sValueA(sValue);
Str = SysAllocStringByteLen(sValueA.GetBuffer(), sValueA.GetLength());
作为替代方案,您也可以在 DLL 中嵌入类型库。这将省略自动字符串转换。