Yiing Kai Ответов: 1

Как я могу гарантировать, управляемые, комбинированные неуправляемого кода в мой код?


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using ReturnCPPArrayToCSharpExample_CSharpSide_;

namespace DLLCall
{
    class Program
    {
        [DllImport("E:\\C++Projects\\ReturnC++ArrayToDLLExample(1D)\\DLLFromCPPToCSharp\\Debug\\ArrayDLLFromCPPToCSharp")]
        public static extern IntPtr[] convertMatrix(int height, int width, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(JaggedArrayMarshaler))]byte[][] inputArr);

        [DllImport("E:\\C++Projects\\ReturnC++ArrayToDLLExample(1D)\\DLLFromCPPToCSharp\\Debug\\ArrayDLLFromCPPToCSharp", CallingConvention = CallingConvention.Cdecl)]
        public static extern int ReleaseMemory(IntPtr ptr);

        static void Main(string[] args)
        {
            int height = 3;
            int width = 3;
            byte[][] inputArr = new byte[3][];
            inputArr[0] = new byte[] { 1, 1, 1 };
            inputArr[1] = new byte[] { 1, 1, 1 };
            inputArr[2] = new byte[] { 1, 1, 1 };

            IntPtr[] obj = new IntPtr[3];
            obj = convertMatrix(height, width, inputArr);
            IntPtr[] outputMatrixOfPointers = new IntPtr[3];
            //Marshal.Copy(obj, outputMatrixOfPointers, 0, 3);

            byte[,] result = new byte[3, 3];
            //Marshal.Copy(outputMatrixOfPointers, result, 0, 5);
            for (int i = 0; i < result.Length; i++)
            {
                //Console.WriteLine(result[i]);
            }
            Console.ReadKey();
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace ReturnCPPArrayToCSharpExample_CSharpSide_
{
    class JaggedArrayMarshaler : ICustomMarshaler
    {
        static ICustomMarshaler GetInstance(string cookie)
        {
            return new JaggedArrayMarshaler();
        }

        GCHandle[] handles;
        GCHandle buffer;
        Array[] array;
        public void CleanUpManagedData(object ManagedObj)
        {
        }
        public void CleanUpNativeData(IntPtr pNativeData)
        {
            buffer.Free();
            foreach (GCHandle handle in handles)
            {
                handle.Free();
            }
        }
        public int GetNativeDataSize()
        {
            return 4;
        }
        public IntPtr MarshalManagedToNative(object ManagedObj)
        {
            array = (Array[])ManagedObj;
            handles = new GCHandle[array.Length];
            for (int i = 0; i < array.Length; i++)
            {
                handles[i] = GCHandle.Alloc(array[i], GCHandleType.Pinned);
            }
            IntPtr[] pointers = new IntPtr[handles.Length];
            for (int i = 0; i < handles.Length; i++)
            {
                pointers[i] = handles[i].AddrOfPinnedObject();
            }
            buffer = GCHandle.Alloc(pointers, GCHandleType.Pinned);
            return buffer.AddrOfPinnedObject();
        }
        public object MarshalNativeToManaged(IntPtr pNativeData)
        {
            return array;
        }
    }

#include <iostream>

using namespace std;
using byte = unsigned char;


extern "C" __declspec(dllexport) unsigned char** convertMatrix(int height, int width, byte inputArr[][3])
{
	byte** arr = 0;
	arr = new byte*[height];
	for (int i = 0; i < height; i++) 
	{
		arr[i] = new byte[width];
		for (int j = 0; j < width; j++) 
		{
			arr[i][j] = 0;
		}
	}
	return arr;
}

extern "C" __declspec(dllexport) int ReleaseMemory(int* pArray)
{
	delete[] pArray;
	return 0;
}


Что я уже пробовал:

Что я уже пробовал:

-Я сам не уверен в этом, но, основываясь на том, что я прочитал, IntPtr[] может использоваться для вызова методов, даже если тип возвращаемого метода отличается, поскольку он может содержать разные объекты, поэтому я чувствовал, что это не должно быть проблемой.
- Добавьте пользовательский класс для маршалирования 2D-массивов, как указано в codeproject as (a.k.a JagggedArrayMarshaller), поскольку я мог понять, что обычные методы маршалинга могут обслуживать только 2D-массив.
- Однако были проблемы, когда я пытался вызвать метод, но я не понимаю, почему маршаллинг неуправляемого кода в управляемый не работает:
IntPtr[] obj = new IntPtr[3];
obj = convertMatrix(height, width, inputArr);

В результате он возвращает ошибку:

Необработанное исключение типа ' System. Runtime.InteropServices.MarshalDirectiveException ' произошло в ReturnCPPArrayToCSharpExample(CSharpSide). exe

Дополнительная информация: невозможно маршалировать "возвращаемое значение": недопустимая комбинация управляемого/неуправляемого типа.

1 Ответов

Рейтинг:
1

KarstenK

Ошибка правильная: не стоит выделять память в среде выполнения C++ и использовать ее в C# в качестве объекта.

Двойные указатели (байт**) не имеют никакого смысла. Используйте простой указатель.

Решение: выделите в памяти C# и передайте ее в качестве параметра C++. Взгляните на мой статья который имеет дело с такими вещами простым способом.

Кстати: не используйте полные пути, но скопируйте dll в правильный каталог. Это плохой стиль, и когда-нибудь у вас будут сбои