Marshaling an array as a return value?

How do you marshal an array as a return value? What marshalling attributes must be specified?

Specifically, using COM Interop, I want to use an interface member that returns an array. The implementation of this interface is written using an unmanaged object and a COM style interface.

In C#, the interface declaration would look like this:

public interface IMyInterface
{
int[] MyArray
{
get;
}
}


In unmanaged COM interface land, the property get function looks like this:

struct IMyInterface : public IUnknown
{
virtual HRESULT STDMETHODCALLTYPE get_MyArray( int** array, int* size );

// array is an "out" parameter, a pointer to an array of integers
// size is an "out" parameter, and returns the number of elements in the array.
};


How do I provide marshalling attributes on the MyArray property get declaration such that I can call this function?

If it's not directly possible, then I am willing to make some compromises:

1) I'm prepared to accept a method declaration in the c# interface declaration, rather than a property get. Like this:
int[] MyArray();

2) I'm prepared to rewrite the implementation of the get_MyArray unmanaged function

3) I'm prepared to modify the signature of the unmanaged function. For example, I'm okay if it has to look like this:
virtual HRESULT STDMETHODCALLTYPE get_MyArray( int* size, SOMETHING<SOMETHINGELSE>* array );

3) I'm prepared to allocate the array in the implementation using CoTaskMemAlloc or whatever memory allocation necessary.

Some details on which I am NOT willing to compromise:
1) The array must be a return value in C#. I am NOT prepared to change the IMyInterface method to this:
void MyArray( out int[] array, out int size );

2) I do not want to take additional marshalling steps in the caller. So I don't want to have to call:
Marshal.FreeCoTaskMem()

3) The size of the array must be variable, in that it must not a constant size in the marshalling attributes. The returned array, could contain any number of elements.


Here's something I tried that did not work:

public interface IMyInterface
{
int[] MyArray
{
[ return : MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1) ]
get;
}
}

I get the following error:

An unhandled exception of type 'System.Runtime.InteropServices.MarshalDirectiveException' occurred in Test.exe

Additional information: Can not marshal return value.

[3103 byte] By [Wyck] at [2007-12-18]
# 1

>3) I'm prepared to modify the signature of the unmanaged function.

Great, then try something like this (in IDL to highlight the attributes and SAFEARRAY element type):

HRESULT get_MyArray([out,retval] SAFEARRAY(int) *array);

Since SAREARRAYs are selfdescribing you don't need the size parameter.

MattiasSj?gren at 2007-10-7 > top of Msdn Tech,.NET Development,.NET Base Class Library...
# 2

I have to admit, I have no idea what SAFEARRAY is or where it's declared or anything. I'm not using MFC, or ATL, or anything like that. I'm not using the MIDL compiler. I have no .idl files. I don't have a type library.

It's a roll-your-own implementation. All I did is derive my object from an interface class that I made that inherits from IUnknown.

Could you explain SAFEARRAY please?

Wyck at 2007-10-7 > top of Msdn Tech,.NET Development,.NET Base Class Library...
# 3

SAFEARRAY is the Automation array type which carries type, rank and bounds with it. It's all documented here

http://msdn.microsoft.com/library/en-us/automat/html/28a00e34-3b5e-4a16-9f4c-dd2a72dc8e46.asp

I guess the C++ signature becomes

virtual HRESULT STDMETHODCALLTYPE get_MyArray(SAFEARRAY** array);

It's declared in Oaidl.h but you typically include Oleauto.h.

MattiasSj?gren at 2007-10-7 > top of Msdn Tech,.NET Development,.NET Base Class Library...

.NET Development

Site Classified