Implementazione di funzioni di callback

Una funzione di callback è il codice di una applicazione gestita che agevola una funzione di DLL non gestita nel completamento di un'attività. Le chiamate di una funzione di callback passano indirettamente da un'applicazione gestita a una funzione di DLL, per tornare poi nuovamente all'implementazione gestita. Alcune delle numerose funzioni di DLL a cui si accede tramite chiamata al sistema operativo richiedono, per essere eseguite in modo corretto, una funzione di callback nel codice gestito. In questo argomento vengono descritti gli elementi di una funzione di callback e le tecniche per implementarne e chiamarne una dal codice gestito.

Nozioni fondamentali sulle funzioni di callback

Nella maggior parte dei casi, per chiamare una funzione di DLL dal codice gestito, si crea e si utilizza una definizione gestita della funzione. Il processo è semplice.

L'utilizzo di una funzione di DLL che richiede una funzione di callback prevede alcuni passaggi aggiuntivi. In primo luogo è necessario determinare se la funzione richiede un callback, controllando la documentazione relativa alla funzione. Successivamente occorre creare la funzione di callback nella propria applicazione gestita. Infine si chiama la funzione di DLL passandole come argomento un puntatore alla funzione di callback. Tali passaggi sono riepilogati nella figura che segue.

Funzione di callback e implementazione

Le funzioni di callback sono ideali per i casi in cui un'attività viene eseguita ripetutamente. Un altro utilizzo comune è quello con le funzioni di enumerazione, quali EnumFontFamilies, EnumPrinters e EnumWindows, contenute nell'API Win32. Come viene illustrato nell'esempio riportato nella sezione successiva, la funzione EnumWindows scorre tutte le finestre esistenti sul computer, chiamando la funzione di callback a svolgere un'attività su ciascuna finestra.

Implementazione di una funzione di callback

Nella procedura che segue viene mostrato come una applicazione gestita, utilizzando la chiamata al sistema operativo, può stampare l'handle di tutte le finestre del computer locale. In particolare, nell'esempio viene utilizzata la funzione EnumWindows per scorrere l'elenco delle finestre e una funzione di callback gestita (denominata CallBack) per stampare il valore dell'handle della finestra.

Per implementare una funzione di callback

  1. Prima di procedere con l'implementazione della funzione EnumWindows, osservarne la firma. EnumWindows ha la seguente firma:

    BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam)
    

    La presenza dell'argomento lpEnumFunc indica che questa funzione richiede un callback. È frequente vedere il prefisso lp (puntatore long) combinato con il suffisso Func nel nome di argomenti che accettano un puntatore a una funzione di callback. Per la documentazione sulle funzioni di Win32, vedere Microsoft Platform SDK.

  2. Creare la funzione di callback gestita. Nell'esempio viene dichiarato un tipo delegato, denominato CallBack, che accetta due argomenti (hwnd e lparam). Il primo argomento è un handle di finestra. Il secondo argomento è definito dall'applicazione. In questa versione entrambi gli argomenti devono essere integer.

    Le funzioni di callback restituiscono in genere valori diversi da zero per indicare l'esito positivo e zero per indicare l'esito negativo. In questo esempio il valore restituito viene impostato in modo esplicito su true per continuare l'enumerazione.

  3. Creare un delegato e passarlo come argomento alla funzione EnumWindows. Con la chiamata al sistema operativo, il delegato viene convertito automaticamente in un formato di callback conosciuto.

  4. Assicurarsi che Garbage Collector non reclami il delegato prima che la funzione di callback abbia completato il proprio compito. Quando si passa un delegato come parametro o si passa un delegato contenuto come campo in una struttura, questo non viene interessato dalla procedura di collection fino al termine della chiamata. Pertanto, come avviene nell'esempio di enumerazione riportato di seguito, la funzione di callback completa il proprio compito prima del termine della chiamata e non richiede ulteriori operazioni da parte del chiamante gestito.

    Se la funzione di callback può essere chiamata dopo il termine della chiamata, il chiamante gestito dovrà comunque predisporre quanto necessario affinché il delegato non sia interessato dalla procedura di collection fino al termine della funzione di callback. Per informazioni dettagliate sulla prevenzioni delle procedure di Garbage Collection, vedere Marshalling di interoperabilità con la chiamata al sistema operativo.

Esempio

Imports System
Imports System.Runtime.InteropServices

Public Delegate Function CallBack( _
hwnd As Integer, lParam As Integer) As Boolean

Public Class EnumReportApp

    Declare Function EnumWindows Lib "user32" ( _
       x As CallBack, y As Integer) As Integer

    Public Shared Sub Main()
        EnumWindows(AddressOf EnumReportApp.Report, 0)
    End Sub 'Main

    Public Shared Function Report(hwnd As Integer, lParam As Integer) _
    As Boolean
        Console.Write("Window handle is ")
        Console.WriteLine(hwnd)
        Return True
    End Function 'Report
End Class 'EnumReportApp
[C#]
using System;
using System.Runtime.InteropServices;

public delegate bool CallBack(int hwnd, int lParam);

public class EnumReportApp {

    [DllImport("user32")]
    public static extern int EnumWindows(CallBack x, int y); 

    public static void Main() 
    {
        CallBack myCallBack = new CallBack(EnumReportApp.Report);
        EnumWindows(myCallBack, 0);
    }

   public static bool Report(int hwnd, int lParam) { 
        Console.Write("Window handle is ");
        Console.WriteLine(hwnd);
        return true;
    }
}
[C++]
using namespace System::Runtime::InteropServices;

// A delegate type.
__delegate bool CallBack(int hwnd, int lParam);

// Managed type with the method to call.
__gc class EnumReport 
{
// Report the window handle.
public:
    bool Report(int hwnd, int lParam) {
       Console::Write(L"Window handle is ");
       Console::WriteLine(hwnd);
       return true;
   }
};

[DllImport("user32")] 
extern "C" int EnumWindows(CallBack* x, int y); 

void main(void) { 
    EnumReport* er = new EnumReport;
    CallBack* myCallBack = new CallBack(er, &EnumReport::Report);
    EnumWindows(myCallBack, 0); 
}

Vedere anche

Chiamata di una funzione di DLL