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.
Related
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.
I am passing an array of C# objects to a managed C++ DLL. The C# class is defined by:
public class LatLonPointType
{
public double Lat; // Latitude.
public double Lon; // Longitude.
}
I pass an array of LatLonPointTypes to a function in a managed C++ DLL. By trial and error I found that I can pass this to the C++ function using a function argument declared as:
array <LatLonPointType^> ^PolygonPoints
The following C++ code will extract an element of type LatLonPointType ^.
LatLonPointType ^a = PolygonPoints[0];
My questions are:
I have used C++ a number of years ago, this seems to be similar to the reference declaration &. Is there a good reference to ^?
In the above example, how would I convert a (LatLonPointType ^) to a (LatLonPointType) value?
To clarify:
If I have:
LatLonPointType ^a = PolygonPoints[i];
How would I get a.Lat and a.Lon?
Would I use a->Lat and a->Lon?
According to this answer, this hat character, aka ^, creates a managed pointer, that will be garbage collected, not necessarily by you. For example, if you were to create a native object using new, it would look like this:
Object* obj = new Object();
But if you were to use a managed pointer, the syntax would change to:
MObject^ mobj = gcnew MObject();
Currently, I couldn't find how to convert a managed pointer to the object itself, but if you want to convert it to a non managed pointer to the object, see this answer.
EDIT
To access members of the class, such as Lon, you can do this:
//Get method
double i = PolygonPoints[n]->Lon;
//Set method
PolygonPoints[n]->Lon = number;
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
I have some legacy code I want to port to C#. I cannot modify the C++ code, I just have to make do with what I'm given.
So, the situation. I'm using SwIG, and I came across this function:
void MarshalMe(int iNum, FooClass** ioFooClassArray);
If I ran SWIG over this, it wouldn't know what to do with the array, so it will create a SWIGTYPE_p_pFooClass. Fair enough!
C# code for this would look like
void MarshalMe(int iNum, SWIGTYPE_p_p_FooClass ioFooClassArray); // Not ideal!
There are some techniques for marshalling this kind of code correctly, so I tried a few of them:
%typemap(ctype) FooClass** "FooClass**"
%typemap(cstype) FooClass** "FooClass[]"
%typemap(imtype, inattributes="[In, Out, MarshalAs(UnmanagedType.LPArray)]") FooClass** "FooClass[]"
%typemap(csin) FooClass** "$csinput"
%typemap(in) FooClass** "$1 = $input;"
%typemap(freearg) FooClass** ""
%typemap(argout) FooClass** ""
This effectively creates a nicer signature:
void MarshalMe(int iNum, FooClass[] ioFooClassArray); // Looks good! Would it work?
However, when I try to run it, I get the following error:
{"Exception of type 'System.ExecutionEngineException' was thrown."}
Any ideas about the actual typemap?
The exception tells you that the function has corrupted the garbage collected heap. It writing past the end of the array. If it is actually a FooClass[], big question, then you'll have to create the array first:
FooClass[] array = new FooClass[666];
MarshalMe(42, array);
Which assumes that the function will fill the array with FooClass objects. The size of the array really matters here, you'll have to have some kind of idea how many elements you'll get back. That can only work reliable if "iNum" is an argument that says how long the array is. Pass array.Length.
It could also mean that the function will create the array itself and return a pointer to it. You're really screwed if that's the case, you cannot release the memory for the array.
[Swig] Java: Another way to pass pointer-to-pointer
I had a similar problem with a C-function (API) which returned a pointer-to-pointer as an input argument. I was trying to call the C-function from JAVA and I had no way modify the API.
The API.h header file contained:
extern int ReadMessage(HEADER **hdr);
The original C-call looked like:
HEADER *hdr;
int status;
status = ReadMessage(&hdr);
The function of the API was to store data at the memory location specified by the pointer-to-pointer.
I tried to use SWIG to create the appropriate interface file. SWIG.i created the file SWIGTYPE_p_p_header.java from API.h. The problem is the SWIGTYPE_p_p_header constructor initialized swigCPtr to 0.
The JAVA call looked like:
SWIGTYPE_p_p_header hdr = new SWIGTYPE_p_p_header();
status = SWIG.ReadMessage(hdr);
But when I called the API from JAVA the ptr was always 0.
I finally gave up passing the pointer-to-pointer as an input argument. Instead I defined another C-function in SWIG.i to return the pointer-to-pointer in a return value. I thought it was a Kludge ... but it worked!
You may want to try this:
SWIG.i looks like:
// return pointer-to-pointer
%inline %{
HEADER *ReadMessageHelper() {
HEADER *hdr;
int returnValue;
returnValue = ReadMessage(&hdr);
if (returnValue!= 1) hdr = NULL;
return hdr;
}%}
The inline function above could leak memory as Java won't take ownership of the memory created by ReadMessageHelper, since the HEADER instance iscreated on the heap.
The fix for the memory leak is to define ReadMessageHelper as a newobject in order for Java to take control of the memory.
%newobject ReadMessageHelper();
JAVA call now would look like:
HEADER hdr;
hdr = SWIG.ReadMessageHelper();
If you are lucky, as I was, you may have another API available to release the message buffer. In which case, you wouldn’t have to do step 4.
William Fulton, the SWIG guru, had this to say about the approach above:
“I wouldn't see the helper function as a kludge, more the simplest solution to a tricky problem. Consider what the equivalent pure 100% Java code would be for ReadMessage(). I don't think there is an equivalent as Java classes are passed by reference and there is no such thing as a reference to a reference, or pointer to a pointer in Java. In the C function you have, a HEADER instances is created by ReadMessage and passed back to the caller. I don't see how one can do the equivalent in Java without providing some wrapper class around HEADER and passing the wrapper to the ReadMessage function. At the end of the day, ReadMessage returns a newly created HEADER and the Java way of returning newly created objects is to return it in the return value, not via a parameter.”
Using SWIG typemap to pass pointer-to-pointer:
Here is another approach using typemaps. It is targetting Perl, not Java, but the concepts are the same. And I finally managed to get it working using typemaps and no helper functions:
For this function:
typedef void * MyType;
int getblock( int a, int b, MyType *block );
I have 2 typemaps:
%typemap(perl5, in, numinputs=0) void ** data( void * scrap )
{
$1 = &scrap;
}
%typemap(perl5, argout) void ** data
{
SV* tempsv = sv_newmortal();
if ( argvi >= items ) EXTEND(sp,1);
SWIG_MakePtr( tempsv, (void *)*$1, $descriptor(void *), 0);
$result = tempsv;
argvi++;
}
And the function is defined as:
int getblock( int a, int b, void ** data );
In my swig .i file. Now, this passes back an opaque pointer in the argout typemap, becaust that's what useful for this particular situation, however, you could replace the SWIG_MakePtr line with stuff to actually do stuff with the data in the pointer if you wanted to. Also, when I want to pass the pointer into a function, I have a typemap that looks like this:
%typemap(perl5, in) void * data
{
if ( !(SvROK($input)) croak( "Not a reference...\n" );
if ( SWIG_ConvertPtr($input, (void **) &$1, $1_descriptor, 0 ) == -1 )
croak( "Couldn't convert $1 to $1_descriptor\n");
}
And the function is defined as:
int useblock( void * data );
In my swig .i file.
Obviously, this is all perl, but should map pretty directly to Java as far as the typemap architecture goes. Hope it helps...
I managed to solve this using Swig Managed Arrays and Pinning documentation.
Given a function in C++
void myArrayCopy(int *sourceArray, int *targetArray, int nitems);
Declare the method as unsafe in C#
%csmethodmodifiers myArrayCopy "public unsafe";
Add the appropriate typemaps
%include "arrays_csharp.i"
%apply int FIXED[] {int *sourceArray}
%apply int FIXED[] {int *targetArray}
As a result, we get the following method in the module class:
public unsafe static void myArrayCopy(int[] sourceArray, int[] targetArray, int nitems)
{
fixed ( int *swig_ptrTo_sourceArray = sourceArray )
{
fixed ( int *swig_ptrTo_targetArray = targetArray )
{
examplePINVOKE.myArrayCopy((IntPtr)swig_ptrTo_sourceArray,
(IntPtr)swig_ptrTo_targetArray,
nitems);
}
}
}
In practice this might differ a little with FooClass** but Swig does support direct pointer to pointer marshalling, which also avoids a copy as well so can be considered better performance
I have a VB6 application that uses a C# compiled Dll. I have been successfull in making this work by means of COM.
But my problem is that I have a Variant array with String and Double data types inside it. I need to pass this array to my C# Dll, which is receiving the array as an Object.
So, all I need to do is to convert the Variant array into an Object array "understandable" by C#. Anyone has any clue on it?
This should do the trick
ArrayList a = new ArrayList(YourObjectArrayHere);
This has to be done right from C# side of things; if it's not, then there isn't much you can do from VB6. That said, by default, a method declared like this:
void Foo(object[] a);
will be seen from VB6 as taking an array of Variant (or, on IDL level, as SAFEARRAY(VARIANT)).
If it doesn't work that way for you, then there's something wrong with your C# declarations - please post them so they may be reviewed.
object[] System.Runtime.InteropServices.Marshal.GetObjectsForNativeVariants(IntPtr aSrcNativeVariant, int cVars)
Have you tried this method?
object[] result;
unsafe
{
pin_ptr<object> pinObj = &obj;
result = Marshal.GetObjectsForNativeVariants(new IntPtr(pinObj), objSize);
}
Haven't tried it myself but seems like it would do the trick.
This is the C# function declaration:
public double[][] CalcMatching( object[][] data1, object[][] data2, long dataLen1, long dataLen2, string matchingType )
This is the VB6 call:
result = matchingCalcObj.CalcMatching(data1, data2, dataLen1, dataLen2, Matching)
where data1 and data2 are arrays of Variant.
I don't think I can do much at the C#, like you guys are saying, once the error I get at the function calling is "Invalid procedure call or argument". Any option at the VB6 side?
Thanks for all the replies.