我收到了一些用于其他项目的C / C ++代码。我将它放入DLL中,然后从C ++测试工具中调用DLL。它工作得很好,并且与代码只是函数调用时的结果相匹配。
然而,我尝试从C#应用程序中获取DLL。我转换了测试工具,并进行了DLL调用,但我收到了堆栈溢出异常。
在C ++中我添加了:
#include "proxy_rec_02.h"
#pragma comment(lib,"proxy_rec_02.lib")
并且调用了这样的函数:
proxy_rec_main(simtime,mx$gl,mz$gl,ry,start_dig,blytarg_on,blytarg,spread_on,last_call,outputs);
标题包含:
void DLL_EXPORT proxy_rec_main(double simtime, double mx$gl, double mz$gl, double ry, int start_dig,
int blytarg_on, double blytarg, int spread_on, int last_call, double *outputs);
在C#中我使用:
using System.Runtime.InteropServices;
和
[DllImport("proxy_rec_02.dll")]
unsafe static extern void proxy_rec_main(double simtime, double mxSgl, double mzSgl, double ry, int start_dig,
int blytarg_on, double blytarg, int spread_on, int last_call, ref double[] outputs);
使用如下函数调用:
proxy_rec_main(simtime,mxSgl,mzSgl,ry,start_dig,blytarg_on,blytarg,spread_on,last_call,ref outputs);
在for循环中多次调用DLL函数。 C ++代码运行得很好。 C#代码抛出堆栈溢出错误。我在proxy_rec_main函数中添加了一些调试状态,它似乎在函数返回之前命中了每个语句。但似乎从函数返回时抛出错误。任何见解都会受到欢迎。
感谢。
答案 0 :(得分:2)
[DllImport]声明中缺少CallingConvention属性。没有迹象表明你在C声明中使用了__stdcall,因此可能需要使用CallingConvention.Cdecl。这确实可以导致SO,堆栈无法清理。
Debug + Windows + Registers并在调用前后观察ESP寄存器的值,它应该是相同的。如果禁用了PInvokeStackImbalance受管调试器警告,请务必将其重新打开。忽略这个警告不是一个好主意。
调试本机代码,验证传递的参数值。有这么多的论点,其中一个的一个坏声明足以射击脚。
答案 1 :(得分:1)
ref double数组似乎有问题, 将[MarshalAs]添加到它并传递IntPtr,而不是双数组。
.net数组不是指针,因为它们在C ++中。
将c#方法标记为私有, 使用使用Marshal.Copy将返回指针传递到.net数组的公共方法进行换行。
在.net中分配时传输数组的示例:
[DllImport(EntryPoint="ExternalMethod"]
private static void ExternalMethodInvoke(
[MarshalAs(UnmanagedType.SysInt), In] IntPtr);
public void ManagedWrapper(ref double[] array)
{
IntPtr unmanagedMem = Marshal.AllocHGlobal(1000);
Marshal.Copy(array, unmanagedMem, 0, 1000);
ExternalMethodInvoke(unmanagedMem); // use try finally for freeing
Marshal.Copy(unmanagedMem, array, 1, 1000);
Marshal.FreeHGlobal(unmanagedMem);
}
在native中分配时传输数组的示例:
[DllImport(EntryPoint="ExternalMethod"]
private static void ExternalMethodInvoke(
[MarshalAs(UnmanagedType.SysInt), Out] out IntPtr);
[DllImport(EntryPoint="ExternalDeleteArray"]
private static void ExternalDeleteArrayInvoke(
[MarshalAs(UnmanagedType.SysInt), Out] out IntPtr);
public void ManagedWrapper(ref double[] array)
{
IntPtr unmanagedMem;
ExternalMethodInvoke(out unmanagedMem); // use try finally for freeing
Marshal.Copy(unmanagedMem, array, 1, 1000);
ExternalDeleteArrayInvoke(unmanagedMem);
}
如果c#side分配数组,不要忘记在c#中分配和解除分配。 (使用marshal(de)分配h全局方法。)
在C ++中分配,调用C ++方法取消分配。
答案 2 :(得分:0)
假设胶水代码是正确的,可能只是DLL函数占用了大量的堆栈。我自己遇到了类似的情况,结果发现在C ++堆栈上分配了一些非常大的对象。从本机代码调用时,在调用之前只使用了一点堆栈,因此堆栈上有足够的剩余空间。从托管代码调用时,已经消耗了大量堆栈,因此没有足够的空间。如果你看一下导致溢出的C ++函数,你可能会发现它正在尝试将大对象放在堆栈上。