Маршаллирование делегата в качестве метода обратного вызова

В этом примере показан способ передачи делегатов в неуправляемую функцию, ожидающую указатели на функции. Делегат — это класс, который может содержать ссылку на метод. Делегат эквивалентен типобезопасному указателю на функцию или функции обратного вызова.

Примечание.

При использовании делегата в вызове функции общеязыковая среда выполнения защищает делегат от сборщика мусора в течение этого вызова. Однако если неуправляемая функция сохраняет делегат для использования после завершения вызова, необходимо вручную запретить сбор мусора до того, как неуправляемая функция завершит обработку делегата. Дополнительные сведения см. в разделах Пример HandleRef и Пример GCHandle.

В примере обратного вызова используются следующие неуправляемые функции со своими первоначальными объявлениями:

  • TestCallBack экспортируется из PinvokeLib.dll.

    void TestCallBack(FPTR pf, int value);
    
  • TestCallBack2 экспортируется из PinvokeLib.dll.

    void TestCallBack2(FPTR2 pf2, char* value);
    

PinvokeLib.dll — это пользовательская неуправляемая библиотека, содержащая реализацию вышеуказанных функций.

В этом примере класс NativeMethods содержит управляемые прототипы методов TestCallBack и TestCallBack2. Оба метода передают делегат функции обратного вызова в качестве параметра. Сигнатура делегата должна соответствовать сигнатуре метода, на который ссылается делегат. Например, подписи делегатов для FPtr и FPtr2 аналогичны методам DoSomething и DoSomething2.

Объявление прототипов

public delegate bool FPtr(int value);
public delegate bool FPtr2(String^ value);

private ref class NativeMethods
{
public:
    // Declares managed prototypes for unmanaged functions.
    [DllImport("..\\LIB\\PinvokeLib.dll")]
    static void TestCallBack(FPtr^ cb, int value);

    [DllImport("..\\LIB\\PinvokeLib.dll")]
    static void TestCallBack2(FPtr2^ cb2, String^ value);
};
public delegate bool FPtr(int value);
public delegate bool FPtr2(string value);

internal static class NativeMethods
{
    // Declares managed prototypes for unmanaged functions.
    [DllImport("..\\LIB\\PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern void TestCallBack(FPtr cb, int value);

    [DllImport("..\\LIB\\PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern void TestCallBack2(FPtr2 cb2, string value);
}

Public Delegate Function FPtr(ByVal value As Integer) As Boolean
Public Delegate Function FPtr2(ByVal value As String) As Boolean

Friend Class NativeMethods
    ' Declares managed prototypes for unmanaged functions.
    <DllImport("..\LIB\PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Sub TestCallBack(
        ByVal cb As FPtr, ByVal value As Integer)
    End Sub

    <DllImport("..\LIB\PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Sub TestCallBack2(
        ByVal cb2 As FPtr2, ByVal value As String)
    End Sub
End Class

Вызов функций

public ref class App
{
public:
    static void Main()
    {
        FPtr^ cb = gcnew FPtr(&App::DoSomething);
        NativeMethods::TestCallBack(cb, 99);
        FPtr2^ cb2 = gcnew FPtr2(&App::DoSomething2);
        NativeMethods::TestCallBack2(cb2, "abc");
    }

    static bool DoSomething(int value)
    {
        Console::WriteLine("\nCallback called with param: {0}", value);
        // ...
        return true;
    }

    static bool DoSomething2(String^ value)
    {
        Console::WriteLine("\nCallback called with param: {0}", value);
        // ...
        return true;
    }
};
public class App
{
    public static void Main()
    {
        FPtr cb = new FPtr(App.DoSomething);
        NativeMethods.TestCallBack(cb, 99);
        FPtr2 cb2 = new FPtr2(App.DoSomething2);
        NativeMethods.TestCallBack2(cb2, "abc");
    }

    public static bool DoSomething(int value)
    {
        Console.WriteLine($"\nCallback called with param: {value}");
        // ...
        return true;
    }

    public static bool DoSomething2(string value)
    {
        Console.WriteLine($"\nCallback called with param: {value}");
        // ...
        return true;
    }
}
Public Class App
    Public Shared Sub Main()
        Dim cb As FPtr = AddressOf App.DoSomething
        Dim cb2 As FPtr2 = AddressOf App.DoSomething2
        NativeMethods.TestCallBack(cb, 99)
        NativeMethods.TestCallBack2(cb2, "abc")
    End Sub

    Public Shared Function DoSomething(ByVal value As Integer) As Boolean
        Console.WriteLine(ControlChars.CrLf + $"Callback called with param: {value}")
        ' ...
        Return True
    End Function

    Public Shared Function DoSomething2(ByVal value As String) As Boolean
        Console.WriteLine(ControlChars.CrLf + $"Callback called with param: {value}")
        ' ...
        Return True
    End Function
End Class

См. также