Click here to Skip to main content
15,891,473 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
C#
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();
        }
    }
}

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

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


What I have tried:

What I have tried:

-I'm not sure about this myself, but based on what I have read, IntPtr[] can be used to invoke methods even if the method return type is different as it could hold different objects, so I felt that should'nt be an issue.
-Add in a custom class to marshal 2D-arrays as given from codeproject as (a.k.a JagggedArrayMarshaller) as I could understand that normal marshaling methods could only cater to a 2D array.
-However there were issues when I tried to invoke the method but I do not understand why the marshalling to unmanaged code to managed does not work:
IntPtr[] obj = new IntPtr[3];
obj = convertMatrix(height, width, inputArr);

As a result, it returns the error:

An unhandled exception of type 'System.Runtime.InteropServices.MarshalDirectiveException' occurred in ReturnCPPArrayToCSharpExample(CSharpSide).exe

Additional information: Cannot marshal 'return value': Invalid managed/unmanaged type combination.
Posted
Updated 21-Jun-17 21:09pm
v2

1 solution

The error is correct: It is not a good idea to allocate memory in the C++ runtime and use it in C# as object.

The double pointers (byte**) making no sense. Use a simple pointer.

Solution: allocate in the memory in C# and give it as parameter to C++. Take a look at my article which deals such stuff in a simple way.

BTW: dont use complete pathes, but copy the dll in the correct directory. It is bad style and someday you will have crashes
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900