C# 导致堆栈溢出的非托管回调
我正在使用C#处理非托管资源。该资源公开一个回调,可以为硬件中可能发生的某些事件设置该回调。要访问非托管函数,请执行以下操作:C# 导致堆栈溢出的非托管回调,c#,c,.net-4.0,callback,unmanaged,C#,C,.net 4.0,Callback,Unmanaged,我正在使用C#处理非托管资源。该资源公开一个回调,可以为硬件中可能发生的某些事件设置该回调。要访问非托管函数,请执行以下操作: [DllImportAttribute("testDLL.dll", EntryPoint = "InstallCallback")] public static extern short InstallCallback(uint handle, byte x, byte y, IntFuncPtr ptr); [UnmanagedFunctionPointer(Ca
[DllImportAttribute("testDLL.dll", EntryPoint = "InstallCallback")]
public static extern short InstallCallback(uint handle, byte x, byte y, IntFuncPtr ptr);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void IntFuncPtr(uint handle, byte x, byte y, LogEntry info);
[DllImportAttribute("testDLL.dll", EntryPoint = "InstallCallback")]
public static extern short InstallCallback(uint handle, byte x, byte y, IntPtr ptr);
public delegate void IntFuncPtr(uint handle, byte x, byte y, LogEntry info);
var callBackDelegate = new IntFuncPtr(Callback);
var callBackPtr = Marshal.GetFunctionPointerForDelegate(callBackDelegate);
InstallCallback(handle, 1, 1, callBackPtr);
首先,我安装回调时引用了IntFuncPtr委托后面的方法。然后我让硬件去做它的事情。在大约4700次回调调用之后,应用程序崩溃。如果我用c/c++编写代码,回调可以正常工作,但是我可以通过从回调函数中删除u stdcall来复制它。从C#中,我无法捕捉到表明应用程序在非托管资源中正在消亡的错误。使用c/c++应用程序,我可以看到堆栈在没有stdcall的情况下溢出
我认为委托可能无法使用调用约定stdcall,因此我尝试了以下方法:
[DllImportAttribute("testDLL.dll", EntryPoint = "InstallCallback")]
public static extern short InstallCallback(uint handle, byte x, byte y, IntFuncPtr ptr);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void IntFuncPtr(uint handle, byte x, byte y, LogEntry info);
[DllImportAttribute("testDLL.dll", EntryPoint = "InstallCallback")]
public static extern short InstallCallback(uint handle, byte x, byte y, IntPtr ptr);
public delegate void IntFuncPtr(uint handle, byte x, byte y, LogEntry info);
var callBackDelegate = new IntFuncPtr(Callback);
var callBackPtr = Marshal.GetFunctionPointerForDelegate(callBackDelegate);
InstallCallback(handle, 1, 1, callBackPtr);
这也不起作用
总之,我有一个非托管回调,它需要一个指向定义为u stdcall的函数的函数指针。如果函数指针指向非stdcall函数,则堆栈将增长并溢出。我正在尝试使用C#中的回调,使用DllImport和一个具有stdcall调用约定的非托管FunctionPointer委托。当我这样做时,C#应用程序就像使用非#U stdcall函数的C/C++应用程序一样
我怎样才能让它完全在C#中工作
编辑1:
这里是本机方法定义和结构信息,包括C#结构信息
extern "C" __declspec( dllexport ) short __stdcall InstallCallback(unsigned int handle, unsigned char x, unsigned char y, LOG_ENTRY info );
typedef union
{
unsigned int ul_All;
struct
{
unsigned int ul_Info:24;
unsigned int uc_IntType:8;
}t;
struct
{
unsigned int ul_Info:24;
unsigned int uc_Biu1:1;
unsigned int uc_Biu2:1;
unsigned int uc_Dma:1;
unsigned int uc_Target:1;
unsigned int uc_Cmd:1;
unsigned int uc_Biu3:1;
unsigned int uc_Biu4:1;
unsigned int res:1;
}b;
} LOG_ENTRY_C;
typedef union
{
unsigned int All;
struct
{
AiUInt32 Index:16;
AiUInt32 Res:8;
AiUInt32 IntSrc:8;
}t;
} LOG_ENTRY_D;
typedef struct log_entry
{
unsigned int a;
unsigned int b;
LOG_ENTRY_C c;
LOG_ENTRY_D d;
} LOG_ENTRY;
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct LogEntry {
public uint Lla;
public uint Llb;
public LogEntryC Llc;
public LogEntryD Lld;
}
[StructLayoutAttribute(LayoutKind.Explicit)]
public struct LogEntryC {
[FieldOffsetAttribute(0)]
public uint All;
[FieldOffsetAttribute(0)]
public LogEntryCT t;
[FieldOffsetAttribute(0)]
public LogEntryCB b;
}
[StructLayoutAttribute(LayoutKind.Explicit)]
public struct LogEntryD {
[FieldOffsetAttribute(0)]
public uint All;
[FieldOffsetAttribute(0)]
public LogEntryDT t;
}
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct LogEntryCT {
public uint bitvector1;
public uint IntType {
get { return ((uint)((this.bitvector1 & 255u))); }
set { this.bitvector1 = ((uint)((value | this.bitvector1))); }
}
public uint Info {
get { return ((uint)(((this.bitvector1 & 4294967040u) / 256))); }
set { this.bitvector1 = ((uint)(((value * 256) | this.bitvector1))); }
}
}
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct LogEntryCB {
public uint bitvector1;
public uint res {
get { return ((uint)((this.bitvector1 & 1u))); }
set { this.bitvector1 = ((uint)((value | this.bitvector1))); }
}
public uint Biu4 {
get { return ((uint)(((this.bitvector1 & 2u) / 2))); }
set { this.bitvector1 = ((uint)(((value * 2) | this.bitvector1))); }
}
public uint Biu3 {
get { return ((uint)(((this.bitvector1 & 4u) / 4))); }
set { this.bitvector1 = ((uint)(((value * 4) | this.bitvector1))); }
}
public uint Cmd {
get { return ((uint)(((this.bitvector1 & 8u) / 8))); }
set { this.bitvector1 = ((uint)(((value * 8) | this.bitvector1))); }
}
public uint Target {
get { return ((uint)(((this.bitvector1 & 16u) / 16))); }
set { this.bitvector1 = ((uint)(((value * 16) | this.bitvector1))); }
}
public uint Dma {
get { return ((uint)(((this.bitvector1 & 32u) / 32))); }
set { this.bitvector1 = ((uint)(((value * 32) | this.bitvector1))); }
}
public uint Biu2 {
get { return ((uint)(((this.bitvector1 & 64u) / 64))); }
set { this.bitvector1 = ((uint)(((value * 64) | this.bitvector1))); }
}
public uint Biu1 {
get { return ((uint)(((this.bitvector1 & 128u) / 128))); }
set { this.bitvector1 = ((uint)(((value * 128) | this.bitvector1))); }
}
public uint Info {
get { return ((uint)(((this.bitvector1 & 4294967040u) / 256))); }
set { this.bitvector1 = ((uint)(((value * 256) | this.bitvector1))); }
}
}
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct LogEntryDT {
public uint bitvector1;
public uint IntSrc {
get { return ((uint)((this.bitvector1 & 255u))); }
set { this.bitvector1 = ((uint)((value | this.bitvector1))); }
}
public uint Res {
get { return ((uint)(((this.bitvector1 & 65280u) / 256))); }
set { this.bitvector1 = ((uint)(((value * 256) | this.bitvector1))); }
}
public uint Index {
get { return ((uint)(((this.bitvector1 & 4294901760u) / 65536))); }
set { this.bitvector1 = ((uint)(((value * 65536) | this.bitvector1))); }
}
}
似乎是内存泄漏问题。您知道是否需要释放与接收对象相关的内存(如日志条目) 我有一个类似的场景,需要释放传递给回调方法的每个对象的内存
查看您的C#代码,并尝试确定您所做的与C/C++代码的不同之处。您是否尝试明确指定调用约定
[DllImportAttribute("testDLL.dll", EntryPoint = "InstallCallback", CallingConvention=CallingConvention.StdCall) ]
您是否检查过您的委托未被垃圾收集,从而导致非托管代码调用不再存在的函数?我现在正在验证这一点。回调可以处理大约4700个调用,并且始终保持在这个数字附近。我相信如果这是一个GC问题,它就不会如此一致。一旦我运行了一些测试,我会给出一个更新。我发布了一个答案,因为我还不能发表评论。如果我在这个答案中获得5票,我将能够评论:-)日志条目结构不需要清理。