开发者

.Net Compact Framework - Calling ActiveX Object that uses [out] SAFEARRAY(float) *

开发者 https://www.devze.com 2023-01-22 17:56 出处:网络
In the Compact Framework 3.5, I am attempting to call an ActiveX object that has an IDL function signature:

In the Compact Framework 3.5, I am attempting to call an ActiveX object that has an IDL function signature:

HRESULT MyFunc([out] SAFEARRAY(float) *var)

The Interop generation creates the msil

[out] class [mscorlib]System.Array&  marshal( safearray float32)

Which seems reasonable enough, but I keep getting a "NotSupportedException". According to an article entitled "Interop: Common issues and debugging techniques" (I can't post more than one hyperlink, it's the first google result for that phrase), in the first bullet point under the "Marshaling" heading, the compact framework doesn't properly marshal SAFEARRAYs.

I have attempted to get around this problem, by manipulating the answer described in this MSDN forum post (Last entry describes his method): http://social.msdn.microsoft.com/forums/en-US/clr/thread/6641abfc-3a9c-4976-a523-43890b2b79a2/

So, I have created the following definition:

[StructLayout(LayoutKind.Sequential)]
struct SafeArray
{
    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
}

And redefined the IDL for the function signature to:

HRESULT MyFunc([out] long *var)

And then issuing the following code:

IntPtr safeArrayPtr = 开发者_高级运维Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(SafeArray)));
SafeArray safeArray;
safeArray.dimensions = 1;
safeArray.features = 0;
safeArray.elementSize = (uint)(Marshal.SizeOf(typeof(float)));
safeArray.locks = 0;
safeArray.elementCount = 6;
safeArray.lowerBound = 0;
safeArray.dataPtr = Marshal.AllocCoTaskMem((int)(safeArray.elementCount * safeArray.elementSize));

Marshal.StructureToPtr(safeArray, safeArrayPtr, false);
int iTmp = safeArrayPtr.ToInt32();
MyFunc(out iTmp)

While the code appears to succeed, when I try to read back the data values, using the Marshal.Copy(dataPtr, myFloatArr, false) function, I am getting all 0's for the data, which tells me that the pointer that the ActiveX DLL is getting is probably totally bogus and it's off writing into oblivion.

Any suggestions as to what I may have messed up in these definitions, or suggestions for other ways of approaching this problem?

Thanks In Advance...


Well, I've solved this one.
Hopefully, my answer will help others who encounter the same problem. The problem that I was running into was that the [out] tag in a COM tlb declaration means that whatever I pass in will be overwritten with the object created inside the COM library. A rather complicated version of the classic (and very elementary problem) "Pass By Reference vs Pass By Value"

So, the correct marshaling is to use the definition of SafeArray that I posted above.

Don't touch the IDL signature itself - that's not a very clean way of doing it. Instead, use ildasm on the generated Interop library to modify the il from:

[out] class [mscorlib]System.Array&  marshal( safearray float32)

to

[out] native int&

and then reassemble with ilasm which will produce a C# function signature

void MyFunc(out IntPtr var)

The calling code then becomes:

IntPtr ip;
SafeArray safeArray;
float []arrFloats;
MyFunc(out ip);
//Marshal the structure itself
Marshal.StructureToPtr(safeArray, ip, false);

//Marshal the data over to .NET
float []arrFloats = new float[safeArray.elementCount];
Marshal.Copy(safeArray.dataPtr, arrFloats, 0, (int)safeArray.elementCount);

Finally, we need to free the memory (remember, we changed the function signature so we're not giving .NET enough information to actually free the memory on its own.

//Don't forget to free both the structure and the object
Marshal.FreeCoTaskMem(safeArray.dataPtr);
Marshal.FreeCoTaskMem(ip);
0

精彩评论

暂无评论...
验证码 换一张
取 消