代码之家  ›  专栏  ›  技术社区  ›  jose

委托有问题

  •  2
  • jose  · 技术社区  · 16 年前

    我这样做是为了从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 回复  |  直到 16 年前
        1
  •  2
  •   Hans Passant    16 年前

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

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

    当非托管代码在收集的委托实例上调用回调时,没有什么好的事情发生,您将听到一个响亮的kaboom。您必须更改代码,以便对实例进行托管引用。一个简单的方法是将私有成员添加到Form1类以存储实例。

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

        2
  •  3
  •   Bobby    16 年前

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

    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);
        (...)
    }
    
        3
  •  3
  •   BlueMonkMN    16 年前

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

        4
  •  0
  •   Adeel    16 年前

    尝试回调变量 cb 全球地

    推荐文章