C# Marshal byte[] to COM SAFEARRAY parameter with "ref object" signature - c#

I've been going round and round in circles on Google on this, and I can find all kinds of discussion, lots of suggestions, but nothing seems to work. I have an ActiveX component which takes an image as a byte array. When I do a TLB import, it comes in with this signature:
int HandleImage([MarshalAs(UnmanagedType.Struct)] ref object Bitmap);
How do I pass a byte[] to that?
There's another function which can return the data with a similar signature, and it works because I can pass "null" in. The type that comes back is a byte[1..size] (non-zero bounded byte[]). But even if I try to pass in what came back, it still gets a type mismatch exception.
More details:
I've been editing the method in the IDispatch interface signature (using ILSpy to extract the interface from the auto-generated interop assembly). I've tried just about every combination of the following, it always gets Type mismatch exception:
Adding and removing the "ref"
Changing the parameter datatype to "byte[]" or "Array"
Marshalling as [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI1)]. After playing around with MarshalAs quite a bit, I'm becoming convinced that IDispatch does not use those attributes.
Also tried using the "ref object" interface as is, and passing it different types: byte[], Array.CreateInstance(typeof(byte) (which I think are both identical, but I found someone suggesting it, so it couldn't hurt to try).
Here's an example of Delphi code that creates a proper array to pass in:
var
image: OLEVariant;
buf: Pointer;
image := VarArrayCreate([0, Stream.Size], VarByte);
Buf := VarArrayLock(image);
Stream.ReadBuffer(Buf^, Stream.Size);
VarArrayUnlock(image);
Here's the C code to do the same thing. I guess if I can't get it to work from C#, I can invoke it through managed C++, although I'd rather have everything in one project:
long HandleImage(unsigned char* Bitmap, int Length)
{
VARIANT vBitmap;
VariantInit (&vBitmap);
VariantClear(&vBitmap);
SAFEARRAYBOUND bounds[1];
bounds[0].cElements = Length;
bounds[0].lLbound = 1;
SAFEARRAY* arr = SafeArrayCreate(VT_UI1, 1, bounds);
SafeArrayLock(arr);
memcpy(arr->pvData, Bitmap, Length);
SafeArrayUnlock(arr);
vBitmap.parray = arr;
vBitmap.vt = VT_ARRAY | VT_UI1;
long result;
static BYTE parms[] = VTS_PVARIANT;
InvokeHelper(0x5e, DISPATCH_METHOD, VT_I4, (void*)&result, parms,
&vBitmap);
SafeArrayDestroy(arr);
VariantClear(&vBitmap);
return result;
}

I finally figured out how to do it in 100% C# code. Apparently Microsoft never considered the idea that someone might use a method with this signature to pass data in, since it marshals correctly going the other direction (it properly comes back as a byte[]).
Also, ICustomMarshaler doesn't get called on IDispatch calls, it never hit the breakpoints in the custom marshaler (except the static method to get an instance of it).
The answer by Hans Passant in this question got me on the right track: Calling a member of IDispatch COM interface from C#
The copy of IDispatch there doesn't contain the "Invoke" method on IUnknown, but it can be added to the interface, using types in System.Runtime.InteropServices.ComTypes as appropriate: http://msdn.microsoft.com/en-us/library/windows/desktop/ms221479%28v=vs.85%29.aspx
That means you get 100% control over marshaling arguments. Microsoft doesn't expose an implementation of the VARIANT structure, so you have to define your own: http://limbioliong.wordpress.com/2011/09/19/defining-a-variant-structure-in-managed-code-part-2/
The input parameters of Invoke are a variant array, so you have to marshal those to an unmanaged array, and there's a variant output parameter.
So now that we have a variant, what should it contain? This is where automatic marshaling falls down. Instead of directly embedding a pointer to the SAFEARRAY, it needs a pointer to another variant, and that variant should point to the SAFEARRAY.
You can build SAFEARRAYs via P/Invoking these methods: http://msdn.microsoft.com/en-us/library/windows/desktop/ms221145%28v=vs.85%29.aspx
So main variant should be VT_VARIANT | VT_BYREF, it should point to another variant VT_UI8 | VT_ARRAY, and that should point to a SAFEARRAY generated via SafeArrayCreate(). That outermost variant should then be copied into a block of memory, and its IntPtr sent to the Invoke method.

Related

C# Interop Marshaling behaviour for arrays seems inconsistent with documentation

I am currently writing a thin C# binding for OpenGL. I've just recently implemented the OpenGL GenVertexArrays function, which has the following signature:
OpenGL Documentation on glGenVertexArrays.
Essentially, you pass it an array in which to store generated object values for the vertex arrays created by OpenGL.
In order to create the binding, I use delegates as glGenVertexArrays is an OpenGL extension function, so I have to load it dynamically using wglGetProcAddress. The delegate signature I have defined in C# looks like this:
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate void glGenVertexArrays(uint amount, uint[] array);
The function pointer is retrieved and converted to this delegate using Marshal.GetDelegateForFunctionPointer, like this:
IntPtr proc = wglGetProcAddress(name);
del = Marshal.GetDelegateForFunctionPointer(proc, delegateType);
Anyways, here's what bothers me:
In any official documentation I can find on default marshalling behaviour for reference types (which includes arrays), is this:
By default, reference types (classes, arrays, strings, and interfaces)
passed by value are marshaled as In parameters for performance
reasons. You do not see changes to these types unless you apply
InAttribute and OutAttribute (or just OutAttribute) to the method
parameter.
This is taken from this MSDN page: MSDN page on directional attributes
However, as can be seen from my delegate signatures, the [In] and [Out] directional attributes have not been used on the array of unsigned integers, meaning when I call this function I should actually not be able to see the generated object values which OpenGL should have stored in them. Except, I am. Using this signature, I can the following result when running the debugger:
As can be seen, the call absolutely did affect the array, even though I did not explicitly use the [Out] attribute. This is not, from what I understand, a result I should expect.
Does anyone know the reason behind this? I know it might seem as a minor deal, but I am very curious to know why this seems to break the default marshalling behaviour described by Microsoft. Is there some behind-the-scenes stuff going on when invoking delegates compared to pure platform invoke prototypes? Or am I misinterpreting the documentation?
[EDIT]
For anyone curious, the public method that invokes the delegate is defined on a static "GL" class, and is as followed:
public static void GenVertexArrays(uint amount, uint[] array)
{
InvokeExtensionFunction<glGenVertexArrays>()(amount, array);
}
It is not mentioned on the documentation page you linked, but there is another topic dedicated to the marshaling of arrays, where it says:
With pinning optimization, a blittable array can appear to operate as an In/Out parameter when interacting with objects in the same apartment.
Both conditions are met in your case: array of uint is blittable, and there is no machine-to-machine marshaling. It is still a good idea to declare it [Out], so your intention is documented within the code.
The documentation is correct in the general case. But uint is a bit special, it is a blittable type. An expensive word that means that the pinvoke marshaller does not have to do anything special to convert the array element values. An uint in C# is exactly the same type as an unsigned int in C. Not a coincidence at all, it is the kind of type that a processor can handle natively.
So the marshaller can simply pin the array and pass a pointer to the first array element as the second argument. Very fast, always what you want. And the function scribbles directly into the managed array, so copying the values back is not necessary. A bit dangerous too, you never ever want to lie about the amount argument, GC heap corruption is an excessively ugly bug to diagnose.
Most simple value types and structs of simple values types are blittable. bool is a notable exception. You'll otherwise never have to be sorry for using [Out] even if it is not necessary. The marshaller simply ignores it here.

Pass C# array of user defined struct to COM via MIDL

I have a custom struct defined in MIDL:
struct Foo
{
[string] char* Bar;
int Baz;
}
I'm trying to pass this from a C# application to a C++ application. The interface assembly was generated with tlbimp.exe.
I should mention that I have next to no knowledge of MIDL and COM interop, but we're stuck using this interop method for the time being, so there's no way around that for now.
I tried various ways of defining an array of such a type, none with success. Ideally I'd want to call it on the C# side just like this:
ComObject.SomeFunction(someArray);
However, I may not be able to do that without a size parameter, so this would also be fine:
ComObject.SomeFunction(someArray.Length, someArray);
I tried the following ways to define it, none of which were successful:
HRESULT SomeFunction(struct Foo array[]); // For some reason it interprets this as a
// ref parameter... I assume because it's
// equivalent to a pointer?
HRESULT SomeFunction(struct Foo** array); // Tried with a pointer as well,
// but I think the memory layout messes it up
HRESULT SomeFunction(int length, [size_is(length)] struct Foo array[]);
HRESULT SomeFunction(int length, [size_is(length)] struct Foo** array); // I don't know how
// to marshal that on
// the C# side
What's the right way to define this in the MIDL and to invoke it on the C# side? Ugly temporary solutions welcome, since this entire thing will be refactored anyway in two months, but we need this before then. For example, currently I'm considering passing in two arrays of simple types (char* and int) and then combine them into the appropriate structure on the C++ side.

How do I allocate GCHandle to structure when structure contains bool

I have been trying to create a handle to a structure type because I need a pinned pointer to it, but I am getting the error "Object contains non-primitive or non-blittable data"
My structure looks like this:
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
[MarshalAs(UnmanagedType.U1)]
public bool Test;
}
Now, when I call,
var mystruct = new MyStruct();
var handle = GCHandle.Alloc(mystruct, GCHandleType.Pinned);
I get the error "Object contains non-primitive or non-blittable data". Now I understand that the bool field is a non-blittable type. But I was under the impression that by adding the MarshalAs attribute, I could tell the marshaller how to convert the type. (I also tried UnmanagedType.Bool)
This structure has to be defined globally, because it is needed throughout my class. The only reason I need the pointer is because I have an unmanaged API that must pass this structure as a pointer. Then I have to get that structure in a callback and read/update members.
So this is the basic scenario.
Structure is created globally in a managed class
Pointer to structure is obtained
Pointer to the structure is passed into the API
The API calls a static method callback where I then need to get my structure and read/update members.
I tried to use Marshal.StructureToPtr but this only creates a copy, so if in my managed class I update the member, when the callback is raised, the updated value is not there.
Does anyone know how I can get a pinned pointer to my structure so I can read/modify the public members and have them available in the callback?
Thanks
You've got more than one problem here. Using a struct is highly inadvisable. It will get boxed before the GCHandle.Alloc() call and that boxed object gets pinned. You cannot see any updates to it through your mystruct variable. Use a class instead.
And avoid bool, it is a non-blittable type due to its highly variable implementation. It is 4 bytes in C, 1 byte in C++, 2 bytes in COM. Just make it a byte instead. You can write a property to get it back to a bool.
So:
[StructLayout(LayoutKind.Sequential)]
public class MyStruct
{
private byte _test;
public bool Test {
get { return _test != 0; }
set { _test = value ? 1 : 0; }
}
}
You're right that you're telling the marshaller how to marshal the type.
But that won't do you any good when you then attempt to bypass the marshaller.
You need to decide whether you want to use the marshaller, or whether you want the unmanaged code to directly write into managed memory.
If you want to use the marshaller:
Generally, a good way to handle this is to use it in both directions. You can use Marshal.StructureToPtr (as you've found), call the external function, and then use Marshal.PtrToStructure to convert it back into your managed representation.
Or you can use methods that are set up in such a way that marshalling happens automatically, without you needing to specify this manually. For example, calling a native method taking a ref MyStruct parameter will allow for that to happen.
If you don't want to use the marshaller:
Don't use any types that require marshalling. As Hans Passant comments, use a different type instead, byte would probably be a good choice.
(I'll refrain from commenting on the advantages and disadvantages of using structs here, except that the points already made about it are well worth reading and understanding.)

Safe way to cast (V)C++ long* to C# Int32*?

I'm currently writing a DLL in C++/CLI which will act as "proxy" between an unmanaged program and another C# DLL. The calling program requires my "proxy DLL" to implement various procedures that will be called by the unmanaged program. So far, no problem.
But: One of the functions has the following prototype:
extern "C" __declspec ( dllexport ) long Execute(unsigned long command, long nInBytes, byte bInData[], long nOutBytes, long* pnUsedOutBytes, byte bOutData[])
Well, my proxy DLL simply calls the C# DLL which provides the following function prototype (which was also given by the documentation of the calling program):
unsafe public UInt32 Execute(UInt32 command, Int32 nInBytes, byte* pInData, Int32 nOutBytes, Int32* pnUsedOutBytes, byte* pOutData);
The compiler throws an error (C2664) at parameter 5 pnUsedOutBytes and tells me, that long* cannot be cast to int*. Well OK, long and int currently have the same implementation which might change at some point in the future, so the thrown error is understandable (though the non-pointer long uses do not throw an error?).
Back to the actual question: What is the best solution to call my C# function? I've already read that (of course) the best solution is to use .NET types when calling a .NET function. So: Is it safe to do a simple type casting when calling the function or might there by any bad circumstance where this type cast will not work?
Using this line calms down the compiler, but is it really safe?
curInstance->Execute(command, nInBytes, pInData, nOutBytes, (System::Int32*)pnUsedOutBytes, pOutData);
Thanks in advance!
No, don't use that cast. Suppose that the actual value that pnUsedOutBytes was greater than 2^32. Best case, the call to Execute would overwrite the low bytes and leave the bits above 32 alone, resulting in a wrong answer.
The solution is to call Execute with a pointer to a 32-bit data type. Create one in your proxy, give it a sensible starting value if needed, make the call, and copy the resulting value into the long that pnUsedOutBytes points to.
Oh, and don't paraphrase error messages. The error message did not say that you can't cast long* to int*; you can. What it almost certainly said is that the compiler can't convert long* to int*. That's correct: there is no implicit conversion between the two types. Adding a cast tells the compiler to do it; with that you have an explicit conversion.
The easiest solution is just to fix the signature of the exported function:
extern "C" __declspec ( dllexport ) int32_t Execute(uint32_t command, int32_t nInBytes, byte bInData[], int32_t nOutBytes, int32_t* pnUsedOutBytes, byte bOutData[])
LoadLibrary will give you no grief whatsoever about the difference between int32_t and int and long, since they are all 32-bit integral types.
(Actually, LoadLibrary won't give you any grief for a bunch of actual errors either, ... but in this case you aren't using an incompatible type)

Passing a SAFEARRAY from C# to COM

I use 3rd party COM to find faces in a picture. One of the methods has the following signature, from SDK:
long FindMultipleFaces(
IUnknown* pIDibImage,
VARIANTARG* FacePositionArray
);
Parameters: pIDibImage[in] - The image
to search.
FacePositionArray[out]- The array of
FacePosition2 objects into which face
information is placed. This array is
in a safe array (VARIANT) of type
VT_UNKNOWN. The size of the array
dictates the maximum number of faces
for which to search.
which translates into the following C# method signature (from metadata):
int FindMultipleFaces(object pIDibImage, ref object pIFacePositions);
Being optimistic I call it the following way but get an exception that the memory is corrupt. The exception is thrown only when a face is present in the image.
FacePosition2[] facePositions = new FacePosition2[10];
object positions = facePositions;
int faceCount = FaceLocator.FindMultipleFaces(dibImage, ref positions);
What's the right way to pass SAFEARRAY to unmanaged code?
It's something like you initialize an array using Marshal.AllocCoTaskMem and then use Marshal.Copy to copy it to unmanaged memory and the pass an IntPtr pointing to the array into the COM method.
In general, look at the Marshal class:
http://msdn.microsoft.com/en-gb/library/system.runtime.interopservices.marshal.aspx
Oops, it seems it only needed from me to initialize the array because FacePosition2 was not a struct but class and it was not initialized automatically as I though it would. This piece was missing:
for (var i = 0; i < facePositions.Length; i++)
{
facePositions[i] = new FacePosition2();
}
There are more sophisticated method, but opinion is more correct:
change this signature Interop, so, he looks like taking an array.
Accessing a SafeArray Result from a COM Call in C#
Default Marshaling for Arrays
Correcting Common Interop Assembly Problems

Categories

Resources