代表问题

时间:2010-02-17 12:30:13

标签: c# delegates lifetime

我已经让它从C代码调用非托管函数。 pCallback是一个函数指针,因此在托管端是委托。

[DllImport("MyDLL.dll")]

public static extern Result SetCallback(
            IntPtr handle,
            Delegate pCallback,
            CallbackType Type);

现在我正在设置

    public delegate void pfnCallback(uint PromptID, ttsEventType evt, IntPtr lData);

    public Form1()
    {
        pfnCallback cb = new pfnCallback(cback);
        (...)
        Wrapper.SetCallback(handle, cb, IntPtr.Zero, CallBackType.DEFAULT);
        (...)
        }

它给出了一个错误,说“......在将代理传递给非托管代码时,它们必须保持活着......”

任何人都可以帮助我吗?

此致

4 个答案:

答案 0 :(得分:3)

据我所知,你应该这样做:

public delegate void pfnCallback(uint PromptID, ttsEventType evt, IntPtr lData);

public pfnCallback cb = new pfnCallback(cback);

public Form1()
{
    (...)
    Wrapper.SetCallback(handle, cb, IntPtr.Zero, CallBackType.DEFAULT);
    (...)
}

答案 1 :(得分:3)

我认为你需要扩展cb的范围以确保该变量将继续存在并且只要非托管代码可能想要调用它就引用回调函数。非托管代码不参与.NET引用跟踪,因此如果您不强制在.NET代码中保留对回调的引用,框架将释放它,并且非托管代码将无法在之后正确调用它这一点。

答案 2 :(得分:2)

您将委托类型声明与委托实例混淆。是的,你公开了你的委托声明,这是错误的。只有Form1类使用它,它应该是私有的。这是委托的实例,这是您使用新语句创建的实例。

现在,您将实例存储在Form1构造函数的局部变量中。这样可以在实例上保留几微秒的引用。一旦构造函数完成,该引用就会消失,垃圾收集器可以在此之后的任何时刻收集委托实例。它无法看到非托管代码保留对它的引用,收集器只能发现托管代码所持有的引用。

当非托管代码在收集的委托实例上调用回调时,没有任何好处,你会听到一个响亮的kaboom。您必须更改代码,以便对实例进行托管引用。一种简单的方法是向Form1类添加一个私有成员来存储实例。

即使这可能还不够好,Form1对象也将在未来的某个时刻被垃圾收集。它也收集委托对象。在发生这种情况之后,您还必须确保非托管代码无法使用回调。给定类的名称(Form1),在这种特定情况下不太可能发生。但是代码可以防御性地进行调用,并在FormClosing事件处理程序中重置回调。

答案 3 :(得分:0)

全局尝试回调变量cb