Question

Cela semble être demandé le plus souvent C # Interop question et semble encore être difficile de trouver une solution de travail pour.

Je suis dans le besoin d'allouer un tableau de la matrice en C # structure de données passant à une DLL C qui remplit les données et retourne à l'appelant à traiter.

Sur la base de différentes pages sur le web, il me semble avoir réussi à obtenir des données et de la mémoire de C # en C ++, mais non, il apparaît, le dos ...

Code suit.

Merci d'avance pour toute aide Shyamal


I ont une structure C ++ de la manière suivante

typedef struct tagTMatrix
{
   int   Id;
   int   NumColumns;
   int   NumRows;
   double*  aData;
} TMatrix;

que je déclare en C # en tant que

[StructLayout(LayoutKind.Sequential)]
unsafe public struct TMatrix
{
public Int32 id;
public Int32 NumCols;
public Int32 NumRows;
public Int32 NumPlanes;
public IntPtr aData;
};



[DllImport("kernel32.dll")]
internal static extern IntPtr LoadLibrary(String dllname);
[DllImport("kernel32.dll")]
internal static extern IntPtr GetProcAddress(IntPtr hModule, String 
procname);

unsafe internal delegate void FillMatrices(IntPtr mats, long num);

[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")] // Saw this 
mentioned somewhere
static extern void CopyMemory(IntPtr dest, IntPtr[] src, int cb);

unsafe private void butTest_Click(object sender, EventArgs e)
{
    IntPtr library = LoadLibrary("TestDLL.dll");
    IntPtr procaddr = GetProcAddress(library, "FillMatrices");
    FillMatrices fm = 
(FillMatrices)Marshal.GetDelegateForFunctionPointer(procaddr, 
typeof(FillMatrices));
    TMatrix[] mats = new TMatrix[2];
    mats[0]=new TMatrix();
    mats[1]=new TMatrix();
    mats[0].id=1;
    mats[0].NumCols=2;mats[0].NumRows=1;mats[0].NumPlanes=0;
    mats[0].aData = Marshal.AllocHGlobal(sizeof(double) * 2);
    double [] array=new double[2];
    array[0]=12.5;array[1]=2.3;
    fixed (double* a = array)
{
IntPtr intPtr = new IntPtr((void*)a);
mats[1].aData = Marshal.AllocHGlobal(sizeof(double) * 2);
//mats[1].aData = 13;
mats[1].aData = intPtr;
mats[1].id = 2;
mats[1].NumCols = 1; mats[1].NumRows = 2; mats[1].NumPlanes = 0;
}
IntPtr[] ptrs = new IntPtr[2];
int total=0;
for (int i = 0; i < ptrs.Length; i++)
{
total = total + sizeof(IntPtr) * (4 + mats[i].NumCols * mats[i].NumRows);
ptrs[i] = 
Marshal.AllocHGlobal(sizeof(IntPtr)*(4+mats[i].NumCols*mats[i].NumRows));
}
Marshal.StructureToPtr(mats[0], ptrs[0], false);
Marshal.StructureToPtr(mats[1], ptrs[1], false);
//_list.test_list =
IntPtr pointer=Marshal.AllocHGlobal(total);
CopyMemory(pointer, ptrs, 2 * IntPtr.Size);
//TMatrix m1=new TMatrix();
//mats[0].aData = 10;// new double[20];
//TMatrix m2 = new TMatrix();
// mats[1].aData = 20;// new double[9];
//Marshal.StructureToPtr(m2, p1, false);
//mats.Add(m2);
//Marshal.StructureToPtr(mats, p1, false);
//IntPtr p2=Marshal.AllocHGlobal(270);
//Marshal.StructureToPtr(mats.ToArray(),p2,false);
fm(pointer,2);
// Now I want to get back this data ???
}


// C++ function
extern "C" void FillMatrices(TMatrix** mats, int matcount)
{
 FILE* fp=fopen("C:\\mats.txt","w+");
 fprintf(fp,"Number of matrices = %d\n",matcount);
 fflush(fp);
 for(int i=0;i<matcount;++i)
 {
  TMatrix* m=mats[i];
  fprintf(fp,"id = %d rows %d cols %d \n",m->Id,m->NumRows,m->NumColumns);
  fflush(fp);

  for(int j=0;j<m->NumRows;++j)
  {
   fprintf(fp,"%d ",j);
   fflush(fp);
   for(int k=0;k<m->NumColumns;++k)
   {
    fprintf(fp,"%f ", m->aData[k*m->NumRows+j]);
    // modify the data - it should be available back in C#
    m->aData[k*m->NumRows+j]=k;
    fflush(fp);
   }
   fprintf(fp,"\n");
   fflush(fp);
  }
  fprintf(fp,"--------------------------\n");

  fflush(fp);
 }
 fclose(fp);
}
Était-ce utile?

La solution

Voici une version modifiée de mon code initial qui fonctionne avec un tableau de matrices:

typedef struct Matrix
{
    int rowsCount;
    int colsCount;
    int* data;
} TMatrix;

extern "C" __declspec(dllexport) void InitializeMatrix(TMatrix** matrices, int count) 
{
    srand(time(NULL));
    printf("<unmanaged>\n");
    for(int i = 0; i < count; i++)
    {
        TMatrix* m = matrices[i];
        printf("rows %d cols %d\n", m->rowsCount, m->colsCount);

        for(int j = 0; j < m->rowsCount; j++)
        {
            for(int k = 0; k < m->colsCount; k++)
            {
                printf("%d ", m->data[k * m->rowsCount + j]);
                // modify the data - it should be available back in C#
                m->data[k * m->rowsCount + j] = rand() % 10;
            }
            printf("\n");
        }
    }
    printf("</unmanaged>\n\n");
}

Et la partie gérée:

[StructLayout(LayoutKind.Sequential)]
struct Matrix
{
    public int RowsCount;
    public int ColsCount;
    public IntPtr Data;
}

class Program
{
    [DllImport("TestLib.dll")]
    private static extern void InitializeMatrix(IntPtr ptr, int count);

    static void Main(string[] args)
    {
        const int count = 3;

        // Allocate memory
        IntPtr ptr = Marshal.AllocHGlobal(count * Marshal.SizeOf(typeof(IntPtr)));
        IntPtr[] matrices = new IntPtr[count];
        for (int i = 0; i < count; i++)
        {
            Matrix matrix = new Matrix();
            // Give some size to the matrix
            matrix.RowsCount = 4;
            matrix.ColsCount = 3;
            int size = matrix.RowsCount * matrix.ColsCount;
            int[] data = new int[size];
            matrix.Data = Marshal.AllocHGlobal(size * Marshal.SizeOf(typeof(int)));
            Marshal.Copy(data, 0, matrix.Data, size);

            matrices[i] = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Matrix)));
            Marshal.StructureToPtr(matrix, matrices[i], true);
        }
        Marshal.Copy(matrices, 0, ptr, count);


        // Call unmanaged routine
        InitializeMatrix(ptr, count);

        Console.WriteLine("<managed>");
        // Read data back
        Marshal.Copy(ptr, matrices, 0, count);
        for (int i = 0; i < count; i++)
        {
            Matrix m = (Matrix)Marshal.PtrToStructure(matrices[i], typeof(Matrix));
            int size = m.RowsCount * m.ColsCount;
            int[] data = new int[size];
            Marshal.Copy(m.Data, data, 0, size);

            // Pretty-print the matrix
            Console.WriteLine("rows: {0} cols: {1}", m.RowsCount, m.ColsCount);
            for (int j = 0; j < m.RowsCount; j++)
            {
                for (int k = 0; k < m.ColsCount; k++)
                {
                    Console.Write("{0} ", data[k * m.RowsCount + j]);
                }
                Console.WriteLine();
            }
        }
        Console.WriteLine("</managed>");


        // Clean the whole mess (try...finally block omitted for clarity)
        for (int i = 0; i < count; i++)
        {
            Matrix m = (Matrix)Marshal.PtrToStructure(matrices[i], typeof(Matrix));
            Marshal.FreeHGlobal(m.Data);
            Marshal.FreeHGlobal(matrices[i]);
        }
        Marshal.FreeHGlobal(ptr);
    }
}

HTH

Autres conseils

Voici quelques méthodes que je l'habitude de maréchal C ++ struct réseau sur une application client C #:

    public static T Get<T>(byte[] msg, int offset)
    {

        T[] t = new T[] { default(T) };
        int len = Marshal.SizeOf(typeof(T));
        GCHandle th = GCHandle.Alloc(t, GCHandleType.Pinned);
        GCHandle mh = GCHandle.Alloc(msg, GCHandleType.Pinned);
        try
        {
            unsafe
            {
                byte* pb = (byte*)mh.AddrOfPinnedObject();
                byte* srcptr = pb + offset;
                byte* dest = ((byte*)th.AddrOfPinnedObject());
                for (int i = 0; i < len; i++)
                {
                    dest[i] = srcptr[i];
                }
            }
        }
        finally
        {
            mh.Free();
            th.Free();
        }


        return t[0];
    }


    public static string GetString(byte[] msg, int offset, int length)
    {
        StringBuilder retVal = new StringBuilder(length);
        unsafe
        {
            fixed (byte* pb = msg)
            {
                byte* pc = (byte*)(pb + offset);
                for (int x = 0; x < length; x++)
                {
                    if (pc[x] == 0) break;
                    retVal.Append((char)pc[x]);
                }
            }
        }
        return retVal.ToString(0, retVal.Length);
    }

Voici comment je l'ai fait dans un ancien projet dans lequel je devais passer une matrice d'entiers à une routine C qui le remplir avec des valeurs et je devais récupérer les valeurs en code managé.

J'ai eu une routine dans le code non géré qui comblerait une matrice d'entiers avec des valeurs:

#include "stdafx.h"
#include "TestLib.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

extern "C" __declspec(dllexport) void InitializeMatrix(int** matrix, int rows, int cols) 
{
    srand(time(NULL));
    printf("rows: %d\ncols: %d\n", rows, cols);
    for (int i = 0; i < rows; i++) 
    {
        for (int j = 0; j < cols; j++)
        {
            matrix[i][j] = rand() % 10;
        }
    }
}

Et puis en code managé je fait quelque chose entre les lignes:

class Program
{
    [DllImport(@"TestLib.dll")]
    private static extern void InitializeMatrix(IntPtr ptr, int rows, int cols);

    static void Main(string[] args)
    {
        const int rowsCount = 3;
        const int colsCount = 4;

        // Allocate memory for the matrix: (rowsCount * sizeof(IntPtr)) * (colsCount * sizeof(int))
        IntPtr ptr = AllocateMatrix(rowsCount, colsCount);
        try
        {
            // Call unmanaged routine to fill the allocated memory with data
            InitializeMatrix(ptr, rowsCount, colsCount);

            // Marshal back data
            int[][] matrix = GetMatrixFromPointer(ptr, rowsCount, colsCount);

            // Pretty-print the matrix
            for (int i = 0; i < rowsCount; i++)
            {
                for (int j = 0; j < colsCount; j++)
                {
                    Console.Write("{0} ", matrix[i][j]);
                }
                Console.WriteLine();
            }
        }
        finally
        {
            // Release allocated memory
            FreeMatrix(ptr, rowsCount, colsCount);
        }
    }

    private static IntPtr AllocateMatrix(int rowsCount, int colsCount)
    {
        IntPtr ptr = Marshal.AllocHGlobal(rowsCount * Marshal.SizeOf(typeof(IntPtr)));
        IntPtr[] rows = new IntPtr[rowsCount];
        for (int i = 0; i < rowsCount; i++)
        {
            int[] cols = new int[colsCount];
            rows[i] = Marshal.AllocHGlobal(colsCount * Marshal.SizeOf(typeof(int)));
            Marshal.Copy(cols, 0, rows[i], colsCount);
        }
        Marshal.Copy(rows, 0, ptr, rows.Length);
        return ptr;
    }

    private static int[][] GetMatrixFromPointer(IntPtr ptr, int rowsCount, int colsCount)
    {
        int[][] result = new int[rowsCount][];
        IntPtr[] rows = new IntPtr[rowsCount];
        Marshal.Copy(ptr, rows, 0, rowsCount);
        for (int i = 0; i < rowsCount; i++)
        {
            int[] cols = new int[colsCount];
            Marshal.Copy(rows[i], cols, 0, colsCount);
            result[i] = cols;
        }
        return result;
    }

    private static void FreeMatrix(IntPtr ptr, int rowsCount, int colsCount)
    {
        IntPtr[] rows = new IntPtr[rowsCount];
        Marshal.Copy(ptr, rows, 0, rowsCount);
        for (int i = 0; i < rowsCount; i++)
        {
            Marshal.FreeHGlobal(rows[i]);
        }
        Marshal.FreeHGlobal(ptr);
    }
}

P / Invoke est un pain pita. Je recommande fortement d'éviter autant que possible et dans les cas où il est possible, essayez d'organiser vos fonctions de manière à réduire au minimum les données marshaling.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top