Frage

Dies scheint die am häufigsten gestellten C # Interop Frage zu sein und scheint noch schwierig zu sein, für eine funktionierende Lösung zu finden.

Ich bin in der Notwendigkeit einer Anordnung von Matrixdatenstruktur in C # Zuweisung es zu einer C-DLL geben, die die Daten füllt und gibt sie an den Anrufer zu behandeln.

Basierend auf verschiedenen Seiten im Internet, scheinen ich es geschafft zu haben, um Daten und Speicher von C # in C ++ zu bekommen, aber nicht, wie es scheint, zurück ...

-Code folgt.

Vielen Dank im Voraus für jede Hilfe Shyamal


Ich habe eine C ++ Struktur wie folgt

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

, die ich in C # deklarieren

[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);
}
War es hilfreich?

Lösung

Hier ist eine modifizierte Version meines ursprünglichen Code, der mit einer Reihe von Matrizen funktioniert:

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");
}

Und das verwaltete Teil:

[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

Andere Tipps

Hier ein paar Methoden, die ich verwenden C Marschall ++ Netzwerk structs auf einer C # Client-Anwendung:

    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);
    }

Hier ist, wie ich es in einem alten Projekt tat, in dem ich eine Matrix von ganzen Zahlen auf eine C-Routine übergeben musste, dass es mit Werten füllen würde, und dann hatte ich die Werte in verwaltetem Code zurück zu erhalten.

Ich hatte eine Routine in nicht verwalteten Code, der eine Matrix aus ganzen Zahlen mit einigen Werten füllen würde:

#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;
        }
    }
}

Und dann in verwaltetem Code Ich habe etwas unter den Zeilen:

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 ist ein PITA. Ich würde Ihnen empfehlen dringend, es so weit wie möglich zu vermeiden und in Fällen, in denen es nicht möglich versuchen, Ihre Funktionen in einer Weise zu organisieren, Daten zu minimieren Marshalling.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top