委派的預設封送處理
根據呼叫的機制,Managed 委派會封送處理為 COM 介面或函式指標:
對於平台叫用,委派預設會封送處理為 Unmanaged 函式指標。
對於 COM Interop,委派預設會封送處理為 _Delegate 型別的 COM 介面。 _Delegate 介面是定義在 Mscorlib.tlb 型別程式庫中,而且包含 Delegate.DynamicInvoke 方法,可以讓您呼叫委派所參考的方法。
下表顯示 Managed 委派資料型別的封送處理選項。 MarshalAsAttribute 屬性提供多個 UnmanagedType 列舉值封送處理委派。
列舉型別 |
Unmanaged 格式的說明 |
---|---|
UnmanagedType.FunctionPtr |
Unmanaged 函式指標 |
UnmanagedType.Interface |
_Delegate 型別的介面,如 Mscorlib.tlb 中的定義 |
請考慮以下的範例程式碼,DelegateTestInterface 的方法是匯出至 COM 型別程式庫。 請注意,只有標示 ref (或 ByRef) 關鍵字的委派 (Delegate) 會做為 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);
};
函式指標可以被解除參考,如同任何其他 Unmanaged 函式指標可以被解除參考一樣。
注意事項 |
---|
Unmanaged 程式碼所擁有的 Managed 委派 (Delegate) 之函式指標的參考,無法防止 Common Language Runtime 在 Managed 物件上執行記憶體回收。 |
例如,下列程式碼是不正確的,因為除了 Test 方法的存留期間之外,cb 物件的參考 (已傳遞給 SetChangeHandler 方法) 無法使 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));
}
}
為了彌補未預期的記憶體回收,呼叫端必須確保只要 Unmanaged 函式指標正在使用中,cb 物件會持續作用。 如果不再需要函式指標的話,您可以選擇性地擁有告知 Managed 程式碼的 Unmanaged 程式碼,如下列範例所示。
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;
}
}