3

I have struct which is defined in Win32 DLL like the following:

typedef struct matrix
{
  double** data;
  int m;
  int n;
} Matrix;

And there is a function:

Matrix getMatrix(void);

Matrix getMatrix()
{
    Matrix mat;

    mat.m = 2;
    mat.n = 2;

    mat.data    = (double**)  malloc (sizeof(double*) * 4);

    mat.data[0] = (double* )  malloc (sizeof(double) * 2);
    mat.data[1] = (double* )  malloc (sizeof(double) * 2);

    mat.data [0][0]=1;
    mat.data [0][1]=2;
    mat.data [1][0]=3;
    mat.data [1][1]=4;

    return mat;
}

How can I capture the return value of this function If I'm using P/Invoke from a C# Application

Sameh K. Mohamed
  • 2,323
  • 4
  • 29
  • 55

2 Answers2

5

I am not sure if it works, but from memory: Declare data as IntPtr and use this :

static double[][] FromNative (IntPtr data, int m,int n)
{
   var matrix=new double[m][];

   for(int i=0;i<m;i++)
   {
       matrix[i]=new double[n];
       Marshal.Copy(Marshal.ReadIntPtr(data),matrix[i],0,n);
       data =(IntPtr)(data.ToInt64()+IntPtr.Size);
   }

   return matrix;
}
Lorenz Lo Sauer
  • 23,698
  • 16
  • 85
  • 87
user629926
  • 1,910
  • 2
  • 18
  • 43
  • The 'new double[]' would be in managed memory. And double arrays in C# are objects with different layout. Unfortunately, your approach won't work. – Viktor Latypov Sep 24 '12 at 09:22
  • Sorry if someone can format my answer,my mobile editor wont format it right. If he wants double[,] instead of double[][] I can edit the question? – user629926 Sep 24 '12 at 09:31
  • It's better, but the problem is still with the `2D-ness` of the array. The zig-zag array you propose won't map the '**' pointer. – Viktor Latypov Sep 24 '12 at 09:36
  • I just asumed that by double** he wanted array of double arrays. – user629926 Sep 24 '12 at 09:45
  • @ViktorLatypov What are you talking about? This code takes as input a pointer to the native structure, and converts it into an equivalent managed structure. This answer is excellent. +1 – David Heffernan Sep 24 '12 at 11:32
  • @user629926 I think `double[,]` would be more appropriate here. But this answer is very good. If you are using .net 4 the pointer increment can be written more simply as: `data += IntPtr.Size` – David Heffernan Sep 24 '12 at 11:36
  • You need to edit the array index in the loop to be the loop index not the array lenght, Thanks a lot for your help but unfortunately it's not working and throw "accessing protected memory exception" in the first iteration at the step of Marshal.Copy > – Sameh K. Mohamed Sep 24 '12 at 12:16
  • @SamehKamal OK, I edited the answer to fix the obvious index mistake. This answer assumes that the native code allocates the memory. Is that correct, or is the caller expected to allocate the memory? To be critical of your question, you did not provide enough information to answer the question. It's never enough to provide just the C++ signature. You also need to provide the memory allocation protocol etc. for calling the function. – David Heffernan Sep 24 '12 at 12:18
  • Yes, the conversion to CLR's double[,] is OK. I don't mind this solution. I just say this way you do not avoid some memory moving, unlike double[], which can be passed as the unmanaged array. If m and n are big, Marshal.Copy would make the usage of native code useless in terms of speed gain. At least this would be a memory waste. – Viktor Latypov Sep 24 '12 at 12:26
  • I have added an edit to the question with the inner code of the function if that would help ! – Sameh K. Mohamed Sep 24 '12 at 12:29
  • @ViktorLatypov You didn't mention any of those points in your answer. – David Heffernan Sep 24 '12 at 12:40
  • Thanks A lot, It worked >>, and by the way you can use > data += IntPtr.Size instead of > data =(IntPtr)(data.ToInt64()+IntPtr.Size); – Sameh K. Mohamed Sep 24 '12 at 12:40
  • But when I try to free the **data-IntPtr** ,it throws exception **Invalid access to memory location** . – Sameh K. Mohamed Sep 24 '12 at 15:26
  • 1
    What do you mean by free. If you wish to free memory alocated by struct from c# . You should make your own free routine ,export it and call lt from c#. – user629926 Sep 24 '12 at 15:28
1

The answer is short but disappointing: you have to convert the multidimensional array to the single-dimensional one.

There is an answer using AllocHGlobal: Pass multi - dimensional array from managed code to unmanaged code

This solution does similar thing, but in the C# definition in your case you have to make the data field to be of IntPtr type and in unmanaged code you have to assume it is a single-dimensional array.

There is another solution using Marshal.UnsafeAddrOfPinnedArrayElement (MSDN and StackOverflow), but it still assumes you are using the one-dimensional arrays.

Better options would follow if you tell what is the problem you are trying to solve. Is it a pure return-from-one-very-important-function-and-forget-this-PInvoke or are you trying to do a constant bidirectional marshalling ?

Community
  • 1
  • 1
Viktor Latypov
  • 14,289
  • 3
  • 40
  • 55