0

I am trying to pass an array of UINT32 from a C# client to C++ ATL COM service.

The IDL declaration is:

[propput, id(1)] HRESULT ThingyData([in] SAFEARRAY(UINT32) newVal);

The C# code that calls it is:

thingy.ThingyData = new UInt32[] { (UInt32)1 }.ToArray();

The C++ implementation is:

STDMETHODIMP CThingy::put_ThingyData(SAFEARRAY * newVal)
{
    CComSafeArray<UINT32> sa;   
    sa.CopyFrom(newVal); //fails here

    //... do some other stuff ...

    return S_OK;
}

The code fails at sa.CopyFrom(...), returning E_INVALIDARG. I debug into the CopyFrom function:

HRESULT CopyFrom(_In_ const SAFEARRAY *psaSrc)
{
    ATLENSURE_THROW(psaSrc != NULL, E_INVALIDARG);

    VARTYPE vt;
    HRESULT hRes = ::ATL::AtlSafeArrayGetActualVartype(const_cast<LPSAFEARRAY>(psaSrc), &vt);
    ATLENSURE_SUCCEEDED(hRes);
    ATLENSURE_THROW(vt == GetType(), E_INVALIDARG);

    // ... 
}

Where it fails at the second ATLENSURE_THROW because vt == 23 (VT_UINT) and GetType() returns 19 (VT_UI4).

My code is running in 32-bit so this should not really be a problem. After more digging I find I can explicitly declare the CComSafeArray to use VT_UINT:

CComSafeArray<UINT32, VT_UINT> sa;

This works, surprisingly. But I am not sure if this is the right way to go about things. Would there be issues with portability, or other weird side effects? I think I read somewhere using VT_UINT in COM is not recommended. What I would prefer is a way to get C# to send the array with VT_UI4 type; is that possible?

Thanks a lot. Trying to wrap my head around all this COM stuff.

smead
  • 1,768
  • 15
  • 23
  • Very hard to see how you could get a VT_UINT out of the CLR. The only plausible scenario I can reverse-engineer from the CLR source is that this started as a UIntPtr[] array. ToArray() in the snippet is quite weird as well, I suspect the actual code is very different. Do otherwise keep in mind that using unsigned types in interop code is unusual, there are a lot of runtime environments that could not use your COM server. – Hans Passant Jul 20 '17 at 12:53
  • @HansPassant Nope, my code was as written. It wasn't a `UIntPtr` array. I've since discovered if I change the IDL and c++ signatures to `SAFEARRAY(ULONG)` instead of `SAFEARRAY(UINT32)`, it works fine and C# passes a `VT_UI4` safearray. It's a little confusing on what the difference is, any ideas? Thanks – smead Jul 24 '17 at 16:13

0 Answers0