从C#传递结构体数组指针到C++

3

我是一名有用的助手,可以为您进行文本翻译。

我有一个来自第三方的c++ dll,我无法修改。 我已经创建了一个c++包装器来调用该c++ dll中的函数。 该包装器由一个带有IJW的C#类调用。

它可以很好地处理本地类型,但我遇到了一个结构体数组问题。

这是c++包装器类:

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

以下是C#的调用示例:

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

我需要在C#中重新定义“cameraStruct”吗?
   [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
   public class aivusdk_cameraInfo
     {
      public int blabla;
     }

要在C#函数中像使用指针一样使用它,还是有其他方法(如mashalling)可以实现,或者我完全错了?请原谅我的糟糕英语,感谢您的回复。

尝试在C#调用和dllimport中使用ref指针。 - sramalingam24
2个回答

2
主要使用IJW的原因是避免在C#代码中使用StructLayout定义并在Marshal类中调用所有这些内容的麻烦。如果你要去做这个麻烦的事情,那么你可能会想使用P/Invoke来完成所有工作。
一种方法是在C#程序集中定义一个接口,由c++/clr程序集实现,并包含与cameraStruct结构中有趣字段对应的属性的托管类。
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; }
}

您的c++/clr程序集中的Helper类将实现IHelper接口。GetCameras方法的实现将使用本地分配的cameraStruct数组调用originalDLL_getCameraList。然后,它将遍历返回的相机,并为每个相机创建一个Camera对象。它将把cameraStruct中的信息复制到Camera对象中,然后将Camera对象添加到相机列表中。本地分配的cameraStruct数组可以被释放。
一个问题是c++/clr项目必须具有对C#项目的构建依赖项(引用)。C#项目不能引用c++/clr项目。为了让您的C#代码获取Helper对象,您可以从C#程序集动态加载c++/clr程序集,并使用反射创建一个Helper对象,然后将其转换为IHelper。
您也可以改变这种关系,但这意味着在c++/clr中定义更多内容,在C#中定义较少的内容。(例如,您需要在c++/clr程序集中定义Camera类。) 我更喜欢在实际情况下使用C#来定义事物。

我喜欢这个答案,但是要指出你可以创建一个程序集,C++/CLR和C#都可以引用,无论使用哪种语言,其中包含必要的桥接口或类。这意味着你也可以在C#中引用C++/CLR程序集来获取相机对象,而不需要循环引用。 - Zarenor
我很喜欢这个答案,已经实现了,"它就工作了"......非常感谢!现在我正在努力解决一个空结构体来回传递的问题,但那是另一回事了。 - Romuald Sicard

0

你需要为数组分配非托管内存,以便将其传递给API。

以下是我会这样做的方法:

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

调用 C++ API 后,您可能需要释放数组(除非 C++ 部分已经这样做):
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);
}

谢谢回复,但是我之前使用IntPtr尝试过但从未成功。而且我认为内存管理对我来说是很麻烦的。 - Romuald Sicard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接