在用作Win32回调时是否需要固定C#方法? [英] Does a C# method need to be pinned when used as Win32 callback?
问题描述
我正在将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);