通过CComSafeArray将BSTR的SAFEARRAY从C ++ DLL返回到VB6会导致字符串长度不匹配

时间:2015-10-07 12:05:34

标签: c++ arrays dll vb6 atl

我想将BSTR的SAFEARRAY从C ++ DLL传递给VB6。这就是我当前的DLL实现(在Win7 x64 SP1上使用VC2013U5编译):

sabstrtovb6.h:

#ifdef SABSTRTOVB6_EXPORTS
#define SABSTRTOVB6_API __declspec(dllexport)
#else
#define SABSTRTOVB6_API __declspec(dllimport)
#endif

#include <OAIdl.h>

SABSTRTOVB6_API HRESULT __stdcall FnSABSTRToVB6(LPSAFEARRAY* ppSABSTR);

sabstrtovb6.cpp(注意:我正在使用SysAllocStringByteLen()分配ANSI字符串,因为VB6在与DLL接口时总是执行宽字符/ ANSI和反之亦然的字符串转换。当函数返回时,VB6执行数组中每个字符串的宽字符转换。):

#include "stdafx.h"
#include "sabstrtovb6.h"

#include <atlsafe.h>

SABSTRTOVB6_API HRESULT __stdcall FnSABSTRToVB6(LPSAFEARRAY* ppSABSTR)
{
    if (!ppSABSTR)
        return E_POINTER;

    if (*ppSABSTR)
        return DISP_E_BADINDEX;

    HRESULT hr = SafeArrayDestroy(*ppSABSTR);
    if FAILED(hr)
        return hr;

    CComSafeArray<BSTR> cSABSTR(ULONG(0), LONG(0));

    cSABSTR.Add(SysAllocStringByteLen("Apple\0", strlen("Apple\0")));
    cSABSTR.Add(SysAllocStringByteLen("Orange\0", strlen("Orange\0")));
    cSABSTR.Add(SysAllocStringByteLen("Wall\0", strlen("Wall\0")));
    cSABSTR.Add(SysAllocStringByteLen("Bananas\0", strlen("Bananas\0")));
    cSABSTR.Add(SysAllocStringByteLen("Fax\0", strlen("Fax\0")));

    *ppSABSTR = cSABSTR.Detach();

    return S_OK;
}

一切都很好,除了一些字符串的长度与NULL字符的偏移量不匹配。在VB6中执行以下代码时...

Option Explicit

Private Declare Function FnSABSTRToVB6 Lib "sabstrtovb6" (ByRef sa() As String) As Long

Private Sub Form_Load()

ChDrive App.Path: ChDir App.Path

Dim sa() As String
Dim res As Long: res = FnSABSTRToVB6(sa)

If res >= 0 Then
    Dim i As Long
    For i = 0 To UBound(sa)
        Debug.Print (sa(i) + ":" + CStr(Len(sa(i))))
    Next
End If

End Sub

...这是调试窗口显示为输出的内容:

Apple :6
Orange:6
Wall:4
Bananas :8
Fax :4

显然,Apple,Bananas和Fax的长度指示器是错误的。我觉得所有带有奇数编号的字符串都用一个额外的字节填充。这里发生了什么?有人可以帮我解决这个问题吗?

UPDATE:似乎CComSafeArray包装器在这里引起了麻烦。当我以老式的方式分配BSTR时,如...

SAFEARRAYBOUND bounds;
bounds.cElements = 5;
bounds.lLbound = 0;
*ppSABSTR = SafeArrayCreate(VT_BSTR, 1, &bounds);

SafeArrayLock(*ppSABSTR);

BSTR *SABSTRArray = (BSTR *)(*ppSABSTR)->pvData;
SABSTRArray[0] = SysAllocStringByteLen("Apple\0", strlen("Apple\0"));
SABSTRArray[1] = SysAllocStringByteLen("Orange\0", strlen("Orange\0"));
SABSTRArray[2] = SysAllocStringByteLen("Wall\0", strlen("Wall\0"));
SABSTRArray[3] = SysAllocStringByteLen("Bananas\0", strlen("Bananas\0"));
SABSTRArray[4] = SysAllocStringByteLen("Fax\0", strlen("Fax\0"));

SafeArrayUnlock(*ppSABSTR);
VB6中的

... Len将显示正确的值:

Apple:5
Orange:6
Wall:4
Bananas:7
Fax:3

在处理BSTR时,这是CComSafeArray的已知行为吗?

0 个答案:

没有答案