【问题标题】:Unmanaged callback causing stack to overflow非托管回调导致堆栈溢出
【发布时间】:2011-12-12 16:15:57
【问题描述】:

我正在使用 C# 处理非托管资源。该资源公开了一个回调,可以为硬件内可能发生的某些事件设置该回调。要访问非托管函数,我执行以下操作:

[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);

我首先安装回调,并引用 IntFuncPtr 委托之后的方法。然后我让硬件去做它的事情。在大约 4700 次回调调用后,应用程序崩溃。如果我用 c/c++ 编写代码,回调可以正常工作,但是我可以通过从回调函数中删除 __stdcall 来复制它。从 C# 我无法捕捉到表明应用程序在非托管资源中死亡的错误。使用 c/c++ 应用程序,我可以看到堆栈在没有 __stdcall 的情况下溢出。

我认为委托可能不适用于调用约定 stdcall,所以我尝试了以下方法:

[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);

这也不起作用。

总而言之,我有一个非托管回调,它需要一个指向定义为 __stdcall 的函数的函数指针。如果函数指针指向非 __stdcall 函数,则堆栈增长并溢出。我正在尝试使用 C# 中的回调,使用 DllImport 和具有 stdcall 调用约定的 UnmanagedFunctionPointer 委托。当我这样做时,C# 应用程序就像使用非 __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))); }
    }
}

【问题讨论】:

  • 您是否检查过您的委托没有被垃圾回收,导致您的非托管代码调用不再存在的函数?
  • 我现在正在验证这一点。回调适用于大约 4700 次调用,并且始终围绕该数字。我相信如果这是一个 GC 问题,它就不会那么一致了。一旦我运行了更多测试,我将提供更新。

标签: c# c .net-4.0 callback unmanaged


【解决方案1】:

似乎是内存泄漏问题。您是否知道是否需要释放与接收到的对象(如 LogEntry)相关的任何内存?

我有一个类似的场景,我需要释放传递给我的回调方法的每个对象的内存。

查看您的 C# 代码并尝试找出您所做的与 c/c++ 代码不同的地方。

【讨论】:

  • 我发布了答案,因为我还不能发表评论。如果我在这个答案中获得 5 票,我将能够发表评论:-)
  • LogEntry 结构不需要清理。
【解决方案2】:

您是否尝试明确指定调用约定

[DllImportAttribute("testDLL.dll", EntryPoint = "InstallCallback", CallingConvention=CallingConvention.StdCall) ]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-03-17
    • 2015-05-21
    • 2014-02-14
    • 1970-01-01
    • 1970-01-01
    • 2020-12-17
    • 1970-01-01
    • 2012-11-20
    相关资源
    最近更新 更多