6

I have a managed function with the following declaration (both interface and implementation):

[return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
String[] ManagedFunction()
{
    String[] foo = new String[1];
    foo[0] = "bar";
    return foo;
}

There is also a native C++ interface with the same methods as the managed interface, inside of that interface, this method has the following declaration:

void ManagedFunction(SAFEARRAY* foo); 

This function is called by native code in the following way:

void NativeFunction(ManagedBinding binding)
{
    CComSafeArray<BSTR> cComSafeArray;
    cComSafeArray.Create(); 
    LPSAFEARRAY safeArray = cComSafeArray.Detach();
    binding.comObject->ManagedFunction(safeArray); 
}

I'm not sure what I'm doing wrong but after my managed function has been called, safeArray appears to have garbage values, somethings going wrong while marshalling the return value back to native code. Could someone with more experience than me with .Net interop please shed some light on this? Also, It might be relevant to mention that I haven't had problems with returning ValueTypes from my managed function (boolean if you're curious), something about returning a String array is messing things up. Thanks!

2 Answers 2

1

1) Your function return a SAFEARRAY, so why you allocate it before calling function?
2) ManagedFunction supposed to return a SAFEARRAY, so it should get a SAFEARRAY* to be able to return it! so you should say:

LPSAFEARRAY lpsa;
binding.comObject->ManagedFunction(&lpsa);
CComSafeArray<BSTR> cComSafeArray;
cComSafeArray.Attach(lpsa);
Sign up to request clarification or add additional context in comments.

3 Comments

That part is not code I'm particularly inclined to touch. I guess I should have mentioned that. I am not aware of the reasons for calling the method on binding.comObject the way it is called but I'm sure there's a good reason. ManagedFunction does not have any trouble in getting the array. Its marshalling back to native code which is the problem.
The problem is ManagedFunction should never get the SAFEARRAY, since you are calling managed code through a COM object and in COM return values are marked with [out, retval] attributes that cause the marshaller to never marshal them from the caller but from callee
well but that's what I want, the array is empty to begin with. So long as the changes made within the caller get propagated back, I'm going to be happy. I tried a bunch of other techniques (eg. passing the SAFEARRAY as an out param to the managed function instead of keeping it as the return value) but none of them worked. I think there is some sort of complexity to marshalling arrays of strings or reference types in general which is not documented well enough.
0

Well I finally got it to work. I created a managed representation of SAFEARRAY called ManagedSafeArray (stolen from here : http://social.msdn.microsoft.com/Forums/en-US/clr/thread/6641abfc-3a9c-4976-a523-43890b2b79a2/):

[StructLayout(LayoutKind.Sequential)]
struct ManagedSafeArray
{
    public ushort   dimensions;     // Count of dimensions in the SAFEARRAY
    public ushort   features;       // Flags to describe SAFEARRAY usage
    public uint     elementSize;    // Size of an array element
    public uint     locks;          // Number of times locked without unlocking
    public IntPtr   dataPtr;        // Pointer to the array data
    public uint     elementCount;   // Element count for first (only) dimension
    public int      lowerBound;     // Lower bound for first (only) dimension
}

I changed the signature of my method to:

void ManagedMethod(ref ManagedSafeArray foo);

Inside my method, I manually updated the dataPtr field by calling Marshal.AllocCoTaskMem(...) and then copied over the strings I wanted the SAFEARRAY to contain.

I have no idea as to why the CLR wasn't able to automatically marshal the parameters to and from native code and I'd still appreciate it if someone could try to explain that.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.