这是我的 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)
你的 p/invoke 声明对应于一个双指针。丢失
ref
关键字。按值传递数组引用已经允许被调用者更改数组内容。
您的代码有几个问题和次优点。
你有一个无限循环
for(unsigned int i = 0; result.size(); i++)
您不需要需要复制您的数组(在本机中),而是希望将您的数组保留为全局对象,并将指针返回到向量的
data()
,然后对其进行编组。这是最通用的模式,特别是,如果你有不可复制的类型和数组,你就不能用不同的方式来做。
你实际上不仅仅是将一个结构从本机传递到托管,而是一个 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
备注:
你需要有一个全局实例,以保持对象存活;另一种方法是使用由
new MyClass()
创建的类实例的指针。
您的结构大小未考虑您的托管结构的功能;对于 blittable 类型,托管和本机的结构大小相同。