Search code examples
c++dcombstrsafearray

SAFEARRAY(BSTR) as [out] parameter in DCOM fails to return strings


I want to write a DCOM server and a client so that they could exchange some data. Both sides are compiled using MS Visual Studio 2008, client connects using pure WinAPI, proxy/stub is a separate dll (in case anything of this matters). The problem is, I can't return an array of strings (it's filled properly yet client receives an array of empty strings).

Server: IDL declaration of COM interface has this method:

[id(7)] HRESULT foo([in] int arg1, [out] SAFEARRAY(int)* arg2, [out] SAFEARRAY(BSTR)* arg3);

Implementation, with header as generated by Studio:

HRESULT STDMETHODCALLTYPE CoClass::foo(int arg1, SAFEARRAY** arg2, SAFEARRAY** arg3){
    SAFEARRAYBOUND bounds;
    bounds.cElements = arg1;
    bounds.lBound = 0;
    *arg2 = SafeArrayCreate(VT_INT, 1, &bounds);
    *arg3 = SafeArrayCreate(VT_BSTR, 1, &bounds);
    for(LONG i=0; i<arg1; ++i){
        int int_value = 42;
        BSTR string_value = SysAllocString(L"Hello");
        //string_value is correct here
        SafeArrayPutElement(*arg2, &i, &int_value);
        SafeArrayPutElement(*arg3, &i, &string_value);
        //string_value isn't destroyed here (explicitly, at least)
    }
    return ERROR_SUCCESS;
}

Client: Included Studio-generated header:

virtual /* [id] */ HRESULT STDMETHODCALLTYPE foo(
    /* [in] */ int arg1,
    /* [out] */ SAFEARRAY * *arg2,
    /* [out] */ SAFEARRAY * *arg3) = 0;

Caller code (pInterface is properly initialized, other calls are successful):

SAFEARRAY *pInts = NULL, *pStrings = NULL;
HRESULT error = pInterface->foo(23, &pInts, &pStrings);
// in debugger:
// error is ERROR_SUCCESS, pInts filled properly, 
// pStrings is an array of 23 NULLs

Other details:

  • There is no other method with ID 7 in IDL file;
  • Using [out] BSTR *str works, the string is returned properly;
  • pInterface is recieved from CoCreateInstanceEx call;
  • There is no older version of the server on the system;
  • The code is to be run on Windows XP without some updates, so using Visual Studio 2008 is a constraint that's hard to bypass.

Does anyone have an idea what I'm doing wrong?


Solution

  • The answer was provided by Hans Passant in comments section.

    The answer is: syntax of adding elements to SAFEARRAY is different for int and BSTR:

    // SAFEARRAY **intArray, **stringArray; LONG i;
    int int_value = 42;
    BSTR string_value = SysAllocString(L"Hello");
    
    SafeArrayPutElement(*intArray, &i, &int_value);
    //SafeArrayPutElement(*stringArray, &i, &string_value); //WRONG!
    SafeArrayPutElement(*stringArray, &i, string_value); //Right
    

    Note that syntax for reading is the same:

    // SAFEARRAY *intArray, *stringArray; LONG i;
    int int_value;
    BSTR string_value;
    
    SafeArrayGetElement(intArray, &i, &int_value);
    SafeArrayGetElement(stringArray, &i, &string_value);