如何正确地将结构向量从 C++ 传递到 C#?

问题描述 投票:0回答:2

这是我的 C++ 代码

struct myStruct
{
    int cid;
    float c;
    float r;
};

int Predict(myStruct* p, const char* path2D)
{
    std::vector<myStruct> result = SomeFunction(path2D);//e.g., result.size() = 2 here
    for(unsigned int i = 0; result.size(); i++)
    {
        p[i] = result[i];
    }
    return 0;
}
extern "C"{
   int __declspec(dllexport) Predict(myStruct* predictions, const char* path2D);
          }

在 C# 中调用

[StructLayout(LayoutKind.Sequential)]
public struct myStruct
{
    public int cid;
    public float c;
    public float r;
}
    
[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Predict([MarshalAs(UnmanagedType.LPArray)] ref myStruct[] results, [MarshalAs(UnmanagedType.LPStr)] string path2Image);

此代码执行失败。任何想法/建议都会有所帮助,这里可能出了什么问题?

另外,当我调试代码时,它会通过 C++ 函数 Predict 直到返回 0;之后它会抛出错误。

Fatal error. Internal CLR error. (0x80131506)
c# c++ unmanaged managed
2个回答
0
投票

你的 p/invoke 声明对应于一个双指针。丢失

ref
关键字。按值传递数组引用已经允许被调用者更改数组内容。


0
投票

您的代码有几个问题和次优点。

  1. 你有一个无限循环

    for(unsigned int i = 0; result.size(); i++)

  2. 您不需要需要复制您的数组(在本机中),而是希望将您的数组保留为全局对象,并将指针返回到向量的

    data()
    ,然后对其进行编组。这是最通用的模式,特别是,如果你有不可复制的类型和数组,你就不能用不同的方式来做。

  3. 你实际上不仅仅是将一个结构从本机传递到托管,而是一个 array of struct,这有点苛刻。

如果您只将一个结构从本机传递到托管,则只需要

Marshal.PtrToStructure<myStruct>(pointer);

如果您需要获得结构向量,我建议您在托管端一次一个地编组结构。

这里是功能修改后的代码:

母语:

struct myStruct
{
    int cid;
    float c;
    float r;
};

std::vector<myStruct> SomeFunction(const char* path_2d)
{
    std::vector<myStruct> result{};
    for (int i=0; i <10; i++)
    {
        myStruct o{};
        o.cid = i;
        o.c = 1.f / (float)i;
        o.r = 1.f - o.c;
        result.push_back(o);
    }
    return result;
}

std::vector<myStruct> result{};

extern "C" __declspec(dllexport) myStruct* Predict(int* size, const char* path2D)
{
    result = SomeFunction(path2D);
    *size = result.size();
    return result.data();
}

管理:

[StructLayout(LayoutKind.Sequential)]
public struct myStruct
{
    public int cid;
    public float c;
    public float r;
    
    public override string ToString() => $"cid,r,c: {cid} - {r} - {c}";
}

public static class NativeLibrary
{
    [DllImport("Native.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr Predict(out int size, [MarshalAs(UnmanagedType.LPStr)] string path2Image);

    public static List<myStruct> GetPredict(string path = "somepath")
    {
        var ptr = Predict(out int size, path);
        List<myStruct> results = new List<myStruct>();
        var structSize = Marshal.SizeOf(typeof(myStruct));
        for (var i = 0; i < size; i++)
        {
            var o = Marshal.PtrToStructure<myStruct>(ptr);
            results.Add(o);
            ptr += structSize;
        }
        return results;
    }
}

class Program
{
    static void Main()
    {
        var result = NativeLibrary.GetPredict();
        Console.WriteLine($"list of {result.Count} structures");
        foreach (var o in result)
            Console.WriteLine(o);
    }
}

结果:

list of 10 structures
cid,r,c: 0 - -∞ - ∞
cid,r,c: 1 - 0 - 1
cid,r,c: 2 - 0,5 - 0,5
cid,r,c: 3 - 0,6666666 - 0,33333334
cid,r,c: 4 - 0,75 - 0,25
cid,r,c: 5 - 0,8 - 0,2
cid,r,c: 6 - 0,8333333 - 0,16666667
cid,r,c: 7 - 0,85714287 - 0,14285715
cid,r,c: 8 - 0,875 - 0,125
cid,r,c: 9 - 0,8888889 - 0,11111111

备注:

  1. 你需要有一个全局实例,以保持对象存活;另一种方法是使用由

    new MyClass()
    创建的类实例的指针。

  2. 您的结构大小未考虑您的托管结构的功能;对于 blittable 类型,托管和本机的结构大小相同。

© www.soinside.com 2019 - 2024. All rights reserved.