【发布时间】:2017-01-07 09:40:12
【问题描述】:
我在this one 等线程中遵循了建议
关于如何将值数组从 c++ 传递到 c#。在@CodyGray 对我最近的问题here为什么我使用指针的评论之后,我尝试在没有指针和编组代码的情况下传递值,也遵循this
它也有效!我很困惑,正确/最好的方法是什么?为什么要在这里使用指针?
这里有一些代码:
考虑这个导出的 c++ 函数
extern "C" { __declspec(dllexport) void PointerTest(char ** text, float* fArray, int * iArray); }
这是我迄今为止在 c# 中所做的:
[System.Runtime.InteropServices.DllImport("myTest.dll")]
private static extern void PointerTest(IntPtr text, IntPtr fArray, IntPtr iArray);
public static IntPtr NativeUtf8FromString(string managedString) {
int len = Encoding.UTF8.GetByteCount(managedString);
byte[] buffer = new byte[len + 1];
Encoding.UTF8.GetBytes(managedString, 0, managedString.Length, buffer, 0);
IntPtr nativeUtf8 = Marshal.AllocHGlobal(buffer.Length);
Marshal.Copy(buffer, 0, nativeUtf8, buffer.Length);
return nativeUtf8;
}
public void DoSomething(String text, float[] F, int[] I, int numFloats, int numInts){
IntPtr sPtr = NativeUtf8FromString(text);
IntPtr fPtr = Marshal.AllocHGlobal(numFloats*sizeof(float));
IntPtr iPtr = Marshal.AllocHGlobal(numInts*sizeof(int));
try
{
Marshal.Copy(F, 0, fPtr, numFloats);
Marshal.Copy(I, 0, iPtr, numInts);
PointerTest(sPtr, fPtr, iPtr);
}
finally
{
Marshal.FreeHGlobal(sPtr);
Marshal.FreeHGlobal(fPtr);
Marshal.FreeHGlobal(iPtr);
}
}
这在将值从 c# 传递到 c++ 时工作正常;但是,使用我发现的指针(可靠地)传回值的唯一方法是我的函数的返回值,这导致我将各种值填充到一个大数组中并返回(再次通过引用)。
现在考虑这段代码:
[System.Runtime.InteropServices.DllImport("myTest.dll")]
private static extern void PointerTest([In, Out] String text, [In, Out] float[] fArray, [In, Out] int[] iArray);
public void DoSomething(String text, float[] F, int[] I){
PointerTest(text, F, I);
}
据我所知,这也是同样的事情。另外,通过使用 [In, Out] 语义,我可以在调用 PointerTest 后轻松访问修改后的数组。
现在我在这里缺少什么?显然,对于像我这样的初学者来说,第二种方法更干净,更容易理解,并且在线的标准答案仍然是使用指针/第一种方法....这使我得出结论,第二种方法肯定有问题,但是我是从 msdn 那里得到的……
请帮助我理解这一点。提前致谢。
【问题讨论】:
-
c和这个有关系吗? -
C++ dll是如何构建的?也就是说,使用什么调用约定(
stdcall、cdecl等)? C# 的默认值是stdcall,但是如果你使用cdecl来处理C++ dll(即用/Gd编译),你需要让你的DllImport看起来像这样[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)].. 调用约定更改堆栈的清理方式(调用者/被调用者),这可能会影响您编组代码的方式 -
hm,感谢您清除...它是用 /Gd 构建的,我现在将其更改为 stdcall 并保留 c#。有趣的是,这个错误并没有导致错误.....