Маршалинг по умолчанию для делегатов
Управляемый делегат маршалируется как COM-интерфейс или как указатель на функцию, используя следующий механизм вызовов:
Для вызова неуправляемого кода делегат по умолчанию маршалируется как указатель на функцию.
Для COM-взаимодействия делегат по умолчанию маршалируется как COM-интерфейс типа _Delegate. Интерфейс _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);
};
Указатель на функцию может быть разыменован, точно так же как может быть разыменован любой другой указатель на неуправляемую функцию.
Примечание |
---|
Ссылка на указатель на функцию в управляемом делегате, хранимая в неуправляемом коде, не препятствует выполнению средой CLR сборки мусора для управляемого объекта. |
Например, следующий код некорректен, потому что ссылка на объект cb, переданная в метод SetChangeHandler, не сохраняет объект cb в активном состоянии по окончании жизненного цикла метода Test. После применения к объекту 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;
}
}
См. также
Основные понятия
Преобразуемые и непреобразуемые типы