문제

이는 가장 일반적으로 묻는 C# 상호 운용성 질문인 것으로 보이지만 이에 대한 효과적인 솔루션을 찾기는 어려운 것 같습니다.

C#에서 행렬 데이터 구조 배열을 할당하여 데이터를 채우고 처리할 호출자에게 반환하는 C DLL에 전달해야 합니다.

웹의 다양한 페이지를 기반으로 C#에서 C++로 데이터와 메모리를 가져오는 데 성공한 것 같습니다. 그러나 다시는 그렇지 않은 것 같습니다.

코드는 다음과 같습니다.

Shyamal에 도움을 주신 것에 미리 감사드립니다


다음과 같은 C++ 구조가 있습니다.

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

나는 C#에서 다음과 같이 선언합니다.

[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);
}
도움이 되었습니까?

해결책

다음은 행렬 배열과 함께 작동하는 초기 코드의 수정된 버전입니다.

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

관리되는 부분은 다음과 같습니다.

[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

다른 팁

다음은 C# 클라이언트 애플리케이션에서 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);
    }

다음은 정수 행렬을 값으로 채울 C 루틴에 전달한 다음 관리 코드에서 값을 다시 가져와야 했던 이전 프로젝트에서 수행한 방법입니다.

정수 행렬을 일부 값으로 채우는 비관리 코드 루틴이 있습니다.

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

그런 다음 관리 코드에서 다음 줄 중에서 작업을 수행했습니다.

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는 PITA입니다.가능한 한 이를 피하는 것이 좋으며, 가능하지 않은 경우에는 마샬링 데이터를 최소화하는 방식으로 함수를 구성하는 것이 좋습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top