委托的默认封送处理
更新:2007 年 11 月
根据调用机制,托管委托被作为 COM 接口或函数指针进行封送:
对于平台调用,默认情况下委托作为非托管函数指针进行封送。
对于 COM 互操作,默认情况下委托作为 _Delegate 类型的 COM 接口进行封送。_Delegate 接口在 Mscorlib.tlb 类型库中定义并且包含 Delegate.DynamicInvoke 方法,该方法使您能够调用委托所引用的方法。
下表显示了托管委托数据类型的封送处理选项。MarshalAsAttribute 属性提供了若干个 UnmanagedType 枚举值来封送委托。
枚举类型 |
非托管格式的说明 |
---|---|
UnmanagedType.FunctionPtr |
非托管函数指针。 |
UnmanagedType.Interface |
_Delegate 类型的接口,在 Mscorlib.tlb 中定义。 |
请考虑下面的代码示例,其中 DelegateTestInterface 的方法被导出到 COM 类型库。请注意,只有用 ref(或 ByRef)关键字标记的委托才作为 In/Out 参数传递。
using System;
using System.Runtime.InteropServices;
public interface DelegateTest {
void m1(Delegate d);
void m2([MarshalAs(UnmanagedType.Interface)] Delegate d);
void m3([MarshalAs(UnmanagedType.Interface)] ref Delegate d);
void m4([MarshalAs(UnmanagedType.FunctionPtr)] Delegate d);
void m5([MarshalAs(UnmanagedType.FunctionPtr)] ref Delegate d);
}
类型库表示形式
importlib("mscorlib.tlb");
interface DelegateTest : IDispatch {
[id(…)] HRESULT m1([in] _Delegate* d);
[id(…)] HRESULT m2([in] _Delegate* d);
[id(…)] HRESULT m3([in, out] _Delegate** d);
[id()] HRESULT m4([in] int d);
[id()] HRESULT m5([in, out] int *d);
};
函数指针可以被取消引用,正如任何其他非托管函数指针可以被取消引用一样。
说明: |
---|
某些函数指针指向由非托管代码持有的托管委托,对这种函数指针进行引用并不会阻止公共语言运行库对托管对象执行垃圾回收。 |
例如,下面的代码是不正确的,其原因是对 cb 对象的引用(被传递给 SetChangeHandler 方法)不会在 Test 方法的生存期以外将 cb 保持为活动状态。一旦 cb 对象被执行垃圾回收,传递给 SetChangeHandler 的函数指针将不再有效。
public class ExternalAPI {
[DllImport("External.dll")]
public static extern void SetChangeHandler(
[MarshalAs(UnmanagedType.FunctionPtr)]ChangeDelegate d);
}
public delegate bool ChangeDelegate([MarshalAs(UnmanagedType.LPWStr) string S);
public class CallBackClass {
public bool OnChange(string S){ return true;}
}
internal class DelegateTest {
public static void Test() {
CallBackClass cb = new CallBackClass();
// Caution: The following reference on the cb object does not keep the
// object from being garbage collected after the Main method
// executes.
ExternalAPI.SetChangeHandler(new ChangeDelegate(cb.OnChange));
}
}
若要抵消意外的垃圾回收的影响,调用方必须确保在使用非托管函数指针的任何情况下都要将 cb 对象保持为活动状态。另外,也可以使非托管代码在不再需要函数指针时通知托管代码,如下面的示例所示。
internal class DelegateTest {
CallBackClass cb;
// Called before ever using the callback function.
public static void SetChangeHandler() {
cb = new CallBackClass();
ExternalAPI.SetChangeHandler(new ChangeDelegate(cb.OnChange));
}
// Called after using the callback function for the last time.
public static void RemoveChangeHandler() {
// The cb object can be collected now. The unmanaged code is
// finished with the callback function.
cb = null;
}
}