Passing a SAFEARRAY from C# to COM - c#

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

Related

Converting array<System::string^> to std::string*

I am new to C++/Cli, actually in the C# project I have string[] which I converted to array, Now I need to pass this unmanaged array into the native CPP file, i.e I want to convert this "array" to std::string*. How I can do that. I tried this below:
void functionA(cli::array^varA){
cli::pin_ptr<System::String^>varA_value = &varA[0];
std::string* varA_value_final = varA_value;
}
But it gives error saying: a value of type cli::pin_ptr cannot be used to initialize entity of type std::string*
The managed class System::String is completely different from the unmanaged class std::string. The classes are completely different, and store completely different data. You cannot get a pointer to one and pretend it's the other.
Iterate over the cli::array<System::String^>, convert each element, and stick the results in a std::vector<std::string> or other unmanaged container. Grab a pointer to the first element. Use marshal_as<std::string>() to convert each element.

Sending a 2D int array between C# and C++

I'm trying to create a solution where I can run a 2D int array within a C# program through CUDA, so the approach I'm currently taking to try and do this is by creating a C++ dll which can handle the CUDA code then return the 2D array. The code I'm using to send my array to the dll and back again is below.
#include "CudaDLL.h"
#include <stdexcept>
int** cudaArrayData;
void CudaDLL::InitialiseArray(int arrayRows, int arrayCols, int** arrayData)
{
cudaArrayData = new int*[arrayCols];
for(int i = 0; i < arrayCols; i++)
{
cudaArrayData[i] = new int[arrayRows];
}
cudaArrayData = arrayData;
}
int** CudaDLL::ReturnArray()
{
return cudaArrayData;
}
The problem however is I get an error in C# on the return, "Cannot marshal 'return value': Invalid managed/unmanaged type combination." My hope was if I returned the array back as a pointer C# might have hopefully understood and accepted it however no such luck.
Any idea's?
As you’re using int[,] in C#, int** is not the right corresponding type in C++. int[][] is an array of arrays of ints, similar to int** in C++; whereas int[,] is one array of ints with 2D indexing: index = x + y * width. Using int** in C#/C++ interop is difficult, as you have many pointers to either managed or unmanaged memory, which is not directly accessible from one to another (see further down).
Already in InitialiseArray(..., int** arrayData) you read somewhere in your memory but not in array values as you don't pass an array with pointers to arrays of ints, you pass one single array of int.
When you return int** in ReturnArray(), your problem is that .net has no clue how to interpret that pointer to an pointer.
To fix this, use only int* on C++ side and don’t return the array as a function return value, this would only give you a pointer to the unmanaged array and not the entire data in managed memory. It is possible to use this from C#, but probably not in the way you intend to do. Use an array allocated in C# as function argument in void ReturnArray(int* retValues) to copy the data to.
The other problem is then data copying and memory allocations. You can avoid all of these steps if you handle memory in C# right, i.e. forbid the garbage collector to move your data around (it does that when cleaning up unused objects). Either use a fixed{} statement or do it manually via GCHandle.Alloc(array, GCHandleType.Pinned). Doing so, you can directly use the C# allocated arrays from within C++.
Finally, if all you need is to let a CUDA kernel run on your C# data, have a look at some cuda wrappers that handle all the Pinvoke hazards for you. Like managedCuda (I maintain this project) or cudafy and some others.

How to cast an allocated memory block to an array addressable data type.

I am trying to Marshal some data from c# to a c library. The struct that I need to pass is just a collection of pointers... something like:
struct sometype {
type1* element1;
type2** element2;
type3** element3;
type4* element4;
}
Now... I've opted to forgo the attempt to copy all of these types into c# and marshal them as structures and decided to just fill a IntPtr array with the correct data in the correct order and pass it as a pointer. As you can see, in this setup I am passing a 4 position array of pointers with positions 1 and 2 being arrays of pointers themselves. It was simple enough and indeed does work when I marshal by fixing the IntPtr array...
fixed(IntPtr* ptr = structArray){
InteropCall(ptr);
}
However, I soon realized that the c library is actually keeping a reference to the struct and after sometime, expectedly breaks when the array is moved or collected. So my solution was to allocate a block of memory using Marshal.AllocHGlobal and hold on to the reference throughout the time the c library needs that data.
So I run this:
IntPtr* pointerArray = (IntPtr*)Marshal.AllocHGlobal (IntPtr.Size * 4);
However, pointerArray always comes out as IntPtr.Zero... or i guess... just zero.
What am I doing wrong? Can I not cast a memory block into a pointer datatype? What if I wanted a byte* or int* so that I can operate on the memory block like an array?

How can I pass a 2-dimensional array from C# to C++?

The following method is in C++ (ATL COM dll)
Void Write( Const VARIANT *pData)
pData is a 2-dimensional array of data type Variant.
When I add this reference in a C# .NET project, the IDE shows the method as
Void Write( ref object pData);
How do I pass a 2-dimensional array from C#?
Array of VARIANTs fits itself well into VARIANT type. You can have it like this:
void Write(VARIANT vData)
where vData.vt == (VT_ARRAY | VT_VARIANT), and vData.parrray is array data (safe array - it can by anydimensional, the array descriptor itself contains bounds and number of dimensions). C# will be able to get it right.
There is a very good tutorial on how to exchange VARIANT's between unmanaged code (c++) and managed code (c# or .NET for instance) here , it may help you.
Edit
As promised long time ago (I'm sorry I forgot) I edit my answer :
You can declare your 2D array in c# like this :
object thearray = new object[,] {{2.0, 1.0},{-3.0, 9.0}} ;
and pass it to you com method. You could also do this :
object[,] thetempmatrix = {{2.0, 1.0},{-3.0, 9.0}} ;
object thearray = thetempmatrix ;
Note that would you have rather defined
object[,] thearray = {{2.0, 1.0},{-3.0, 9.0}} ;
it would have worked also, but thearray wouldn't have been passed by reference : assume no const in your COM method signature, it would have been updated by your COM method, but at the exit of your COM method call, you would have got the same thearray as the one you passed. Of course, this last remark does not concern you, as you do have a const in your COM method signature.

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

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.

Categories

Resources