Marshalling von Delegaten als Rückrufmethode

In diesem Beispiel wird veranschaulicht, wie Delegaten an eine nicht verwaltete Funktion übergeben werden, die Funktionszeiger erwartet. Ein Delegat ist eine Klasse, die einen Verweis auf eine Methode enthalten kann, und gleichbedeutend mit einem typsicheren Funktionszeiger oder einer Rückruffunktion ist.

Hinweis

Bei Verwendung eines Delegaten in einem Aufruf schützt die Common Language Runtime den Delegaten für die Dauer dieses Aufrufs vor der Garbage Collection. Wenn die nicht verwaltete Funktion jedoch den Delegaten speichert, um ihn nach dem Abschluss des Aufrufs zu verwenden, müssen Sie die Garbage Collection manuell verhindern, bis die nicht verwaltete Funktion mit dem Delegaten beendet wird. Weitere Informationen finden Sie unter HandleRef-Beispiel und GCHandle-Beispiel.

Das Rückrufbeispiel verwendet die folgenden nicht verwalteten Funktionen, die jeweils zusammen mit ihrer ursprünglichen Funktionsdeklaration aufgeführt werden:

  • TestCallBack aus „PinvokeLib.dll“ exportiert.

    void TestCallBack(FPTR pf, int value);
    
  • TestCallBack2 aus „PinvokeLib.dll“ exportiert.

    void TestCallBack2(FPTR2 pf2, char* value);
    

PinvokeLib.dll ist eine benutzerdefinierte, nicht verwaltete Bibliothek, die eine Implementierung für die zuvor aufgelistete Funktionen enthält.

Die NativeMethods-Klasse in diesem Beispiel enthält verwaltete Prototypen für die TestCallBack- und TestCallBack2-Methoden. Beide Methoden übergeben einen Delegaten als Parameter an eine Rückruffunktion. Die Signatur des Delegaten muss mit der Signatur der Methode übereinstimmen, auf die er verweist. Die FPtr- und FPtr2-Delegaten verfügen beispielsweise über Signaturen, die identisch mit den DoSomething- und DoSomething2-Methoden sind.

Deklarieren von Prototypen

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

Aufrufen von Funktionen

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

Siehe auch