3

I have a c++ dll from a third party, I can't modify. I've made a c++ wrapper to call the functions inside that c++ dll. That wrapper is called from a C# class with IJW.

It works well with natives types, but I ran into a struct array problem.

This is the c++ wrapper class :

int Helper::GetCameras(cameraStruct * cameraArray, size_t * size)
    {
        return originalC++Dll_getCameraList(cameraArray, size);
    }

And the C# call :

var ret = m_Helper.GetCameras(pointer, size);

Do I need to redefine the "cameraStruct" in c# like this ?

   [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
   public class aivusdk_cameraInfo
     {
      public int blabla;
     }

To use it like with a pointer in the C# function, or is it another way of doing it (mashalling), or am I completly wrong ?

Forgive my bad english and thanks for your response.

1
  • Try ref pointer in C# call and dllimport Commented Feb 23, 2018 at 16:15

2 Answers 2

2

The primary reason to use IJW is to avoid all the hassle of trying to define parallel structures in your C# code using StructLayout and calling all that stuff in the Marshal class. If you are going to go to that trouble, you may as well do everything using P/Invoke.

One approach would be to define, in your C# assembly, an interface that will be implemented by the c++/clr assembly and a managed class containing properties corresponding to the interesting fields of the cameraStruct structure.

public interface IHelper
{
    public void GetCameras(IList<Camera> cameras);
    public Picture TakePicture(Camera camera);
    public void LoseCamera(Camera camera);
}

public class Camera
{
    public string Name { get; set; }
    public int BlaBla { get; set; }
}

The Helper class in your c++/clr assembly would implement the IHelper interface. The implementation of the GetCameras method would call originalDLL_getCameraList with a locally allocated array of cameraStruct. It would then iterate over the returned cameras and gcnew a Camera object for each one. It would copy the information from the cameraStruct to the Camera object and then add the Camera object to the cameras list. The locally allocated cameraStruct array can then be freed.

One issue is that the c++/clr project must have a build dependency (reference) to the C# project. The C# project cannot have a reference to the c++/clr project. For your C# code to get a Helper object, you could dynamically load the c++/clr assembly from the C# assembly and use reflection to create a Helper object that you cast to an IHelper.

You can turn that relationship around, but that means defining more in c++/clr and less in C#. (For example, you would need to define the Camera class in the c++/clr assembly.) I prefer to define things using C# when practical.

Sign up to request clarification or add additional context in comments.

2 Comments

I like this answer, but will point out that you could create an assembly which both the C++/CLR and C# reference, in either language, which contains the necessary bridge interfaces or classes. This would mean you can also reference the C++/CLR assembly in the C# to get the camera objects, without having a circular reference.
I liked the answer, implemented it and "It Just Works"...Thanks a lot ! I'm now struggling with an empty struct to pass back and forth but it's another story.
0

You need to allocate unmanaged memory for the array that you can pass to the API.

This is how I would do that:

// the input here is a managed C# array
public IntPtr AllocateNativeArray(CameraInfo[] myArray)
{
    if (myArray == null || myArray.Count == 0)
        return IntPtr.Zero;

    // building native structures
    var nativeArray = new Aivusdk_CameraInfo[myArray.Length];
    for (int i = 0; i < myArray.Length; i++)
    {
        // TODO: fill properties
    }

    // allocating unmanaged memory and marshaling elements
    int elementSize = Marshal.SizeOf(typeof(Aivusdk_CameraInfo));
    IntPtr result = Marshal.AllocHGlobal(nativeArray.Length * elementSize);
    for (int i = 0; i < nativeArray.Length; i++)
    {
        Marshal.StructureToPtr(nativeArray[i], new IntPtr((long)result + i * elementSize), false);
    }

    return result;
}

After calling the C++ API you might need to free the array (unless the C++ part does that):

private static void FreeNativeArray(IntPtr address, uint length)
{
    if (address== IntPtr.Zero)
        return;

    int elementSize = Marshal.SizeOf(typeof(Aivusdk_CameraInfo));
    for (int i = 0; i < count; i++)
    {
        Marshal.DestroyStructure(new IntPtr((long)address + i * elementSize), typeof(Aivusdk_CameraInfo));
    }

    Marshal.FreeHGlobal(address);
}

1 Comment

Thanks for responsing, but previous my attempts never succeeded when I used IntPtr. And I think it's a hassle for me to manage the memory.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.