I would like to pass a SAFEARRAY of BSTR from a C++ DLL to VB6. This is what my current implementation of the DLL looks like (compiled with VC2013U5 on Win7 x64 SP1):
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 (Note: I'm allocating ANSI strings with SysAllocStringByteLen()
because VB6 always performs a wide-char / ANSI and vice versa translation of strings when interfacing with DLLs. When the function returns, VB6 performs a wide-char conversion for each string in the array.):
#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;
}
All works well, except that the length of some strings is mismatched by an offset of a NULL character. When executing the following code in 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
... this is what the debug window display as an output:
Apple :6
Orange:6
Wall:4
Bananas :8
Fax :4
Clearly, the length indicator of Apple, Bananas, and Fax is wrong. I get the feeling that all strings with an odd-numbered length are padded with an additional byte. What is going on here? Can someone help me fix this behavior?
UPDATE: It seems that the CComSafeArray wrapper is causing the trouble here. When I allocate the BSTRs in the old fashioned way, as in...
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);
... Len
in VB6 will display correct values:
Apple:5
Orange:6
Wall:4
Bananas:7
Fax:3
Is this a known behavior of CComSafeArray when dealing with BSTRs?