在用作Win32回调时是否需要固定C#方法? [英] Does a C# method need to be pinned when used as Win32 callback?

查看:233
本文介绍了在用作Win32回调时是否需要固定C#方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将C#实例方法传递给Win32 API调用,该调用以后将用作Windows中的回调函数到我的应用程序中.当我传递对对象的引用时,该引用会被临时固定,直到调用返回为止(请参阅

I am passing a C# instance method to a Win32 API call that will later be used as a callback function from Windows into my application. When I pass a reference to an object, that reference is temporarily pinned until the call returns (see this article by Jason Clark).

如果API调用将在调用返回后保留该地址供以后使用,则我必须在调用之前显式固定对象(我可以通过 GCHandle.Alloc )对象.

If the API call will retain the address for later use after the call returns, I have to pin the object explicitly before the call (I can allocate it from unmanaged memory via Marshal.AllocHGlobal, or I can pin down a managed object via GCHandle.Alloc).

但是保留Win32 API用作回调的方法呢?具体来说,我有以下代码:

But what about methods retained for use as callbacks by the Win32 API? Specifically, I have this code:

    protected const int CALLBACK_FUNCTION = 0x30000;

    private delegate void MidiInProc(
        int handle,
        uint msg,
        int instance,
        int param1,
        int param2);

    [DllImport("winmm.dll")]
    private static extern int midiInOpen(
        out int handle,
        int deviceID,
        MidiInProc proc,
        int instance,
        int flags);

    private void MidiInProcess(
        int hMidiIn,
        uint uMsg,
        int dwInstance,
        int dwParam1,
        int dwParam2)

    {
    }

    ...

    int hResult = midiInOpen(
        out hHandle,
        deviceID,
        MidiInProcess, // Might this move after the call returns?
        0,
        CALLBACK_FUNCTION);

存档教程中,微软说:"...确保委托实例的生命期覆盖非托管代码的生命期;否则,委托被垃圾回收后将不可用."这样做很合理,因为如果没有托管代码引用,该类可能会被卸载,从而导致方法(委托")不再在内存中.

At an archived tutorial, Microsoft says, "...make sure the lifetime of the delegate instance covers the lifetime of the unmanaged code; otherwise, the delegate will not be available after it is garbage-collected." That makes perfect sense, as the class might be unloaded if there are no managed-code references to it, resulting in the method ("delegate") no longer being in memory.

但是,由于在堆上分配了对象,因此方法重定位的可能性如何?该方法的地址可能会在其生命周期内发生变化?

But what about the possibility of relocation of the method, as there is of objects allocated on the heap? Might the method's address change during its lifetime?

换句话说:假设定义MidiInProcess的类保持加载状态,可以确定上述MidiInProcess方法在midiInOpen返回后不会更改地址,还是必须采取一些步骤?固定吗?

In other words: Provided that the class defining MidiInProcess remains loaded, can I be certain that the MidiInProcess method, above, will not change address after midiInOpen returns, or must I take some step(s) to pin it?

更新

根据Hans的第一条评论,传递给上述midiInOpen的委托是短暂的,并且不能保证以后被调用时可用(因为在托管代码方面没有持久引用).我相信,只要在可能需要回调的情况下,在应用程序的其他位置保留对封闭实例本身的引用,在封闭实例的私有成员中保留对它的引用就足以使其保持活动状态.尽管不完整,但这看起来可能是这样的:

As per Hans's first comment, the delegate passed to midiInOpen, above, is ephemeral and is not guaranteed to be available later when called (because there's no persistent reference to it on the managed code side). I believe retaining a reference to it in a private member of the enclosing instance should be enough to keep it alive, provided that a reference to the enclosing instance itself is kept somewhere else in the application for as long as the callback might be needed. Although incomplete, this is how that might look:

    private MidiInProc midiInProc;

    ...

    midiInProc = MidiInProcess;

    int hResult = midiInOpen(
        out hHandle,
        deviceID,
        midiInProc, // Pass the reference you retained.
        0,
        CALLBACK_FUNCTION);

推荐答案

来自MSDN文章

请注意,有可能(但不是必须)使用pin_ptr(C ++/CLI)固定委托,以防止垃圾回收器将其重新放置或处置.需要保护免受过早的垃圾收集,但是固定提供的保护超过了必需的,因为它既可以防止收集,又可以防止重定位.

Notice that is it possible, but not necessary, to pin the delegate using pin_ptr (C++/CLI) to prevent it from being re-located or disposed of by the garbage collector. Protection from premature garbage collection is needed, but pinning provides more protection than is necessary, as it prevents collection but also prevents relocation.

如果通过垃圾回收将委托重新放置,则不会影响底层托管回调,因此,Alloc用于添加对委托的引用,允许委托的重新放置,但是防止处置.使用GCHandle代替pin_ptr可以减少托管堆的碎片可能性.

If a delegate is re-located by a garbage collection, it will not affect the underlaying managed callback, so Alloc is used to add a reference to the delegate, allowing relocation of the delegate, but preventing disposal. Using GCHandle instead of pin_ptr reduces fragmentation potential of the managed heap.

强调是我的.

当然,正如Hand在评论和

Sure, it's relevant to using P/Invoke and not only using C++ IJW interop, as Hand said in the comments and Chris Brumme said in the blog post I linked in the comments, but that's the best the documentation has, I think.

如果您足够注意的话,可以向文档提交错误.它现在托管在GitHub上,因此比以前更容易了.

You can file a bug with the documentation if you care enough. It's hosted on GitHub now so it's probably easier in than in the old days.

这篇关于在用作Win32回调时是否需要固定C#方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆