Win32 CALLBACK Functions
How to create and use Win32 callback functions in C/C++
Una funzione callback è una funzione il cui prototipo è dichiarato in una data DLL e la
cui implementazione è invece delegata al processo che chiama tale funzione in quella libreria.
La DLL, quando chiamata, a sua volta, invocherà la funzione callback, implementata nel processo chiamante.
Tecnicamente parlando si tratta di creare un puntatore all'indirizzo di memoria di una funzione (che "vive" nel processo chiamante) e passare tale indirizzo
alla DLL, la quale, a sua volta, chiamerà la funzione all'indirizzo che ha ricevuto.
Schema:
La dichiarazione del prototipo di una funzione callback:
typedef return_type(CALLBACK *callback_func_name)(arguments_if_any);
Nota:
coloro che conoscono il C avranno già capito che è identica a quella di un classico puntatore a funzione del C.
A cosa serve una funzione callback
Gli scopi delle funzioni callback sono molteplici; quelli più comuni sono notificare al processo
chiamante che un evento atteso è avvenuto (es: il download di un file è terminato) od ancora a notificare
che dei dati sono stati modificati e così via.
Un'applicazione di esempio
Faremo un'applicazione di esempio per vedere il meccanismo callback in funzione.
Il progetto consisterà in una DLL (Win32) ed un exe (altrettanto Win32). Quest'ultimo per comodità sarà basato
su un custom template wizard di Visual Studio 2017 Win32 Dialog Project che potete scaricare da questo sito
al link:
Win32 Dialog Project Template per Visual Studio 2017
Creiamo dunque un nuovo progetto in visual studio e scegliamo Visual C++ → Windows Desktop → Dynamic-Link Library(DLL),
diamo un nome al progetto ( io l'ho chiamato CallBackDemo ).
Aggiungiamo un file header (CallBackDemo.h) al progetto in cui dichiareremo la funzione callback.
Definiamo una struct che useremo come argomento della funzione callback:
#ifndef CALLBACK_EXPORTS
#define CALLBACK_EXPORTS
#endif
#ifdef CALLBACK_EXPORTS
#define CALLBACK_API extern "C" __declspec(dllexport)
#else
#define CALLBACK_API extern "C" __declspec(dllimport)
#endif
//Struct define
typedef struct _tagNOTIFY_ARGS
{
LPCWSTR lpszMessage;
BOOL bFlag;
DWORD dwCookie;
}NOTIFY_ARGS, *LPNOTIFY_ARGS;
Nota:
Per comodità ho definito __declspec(dllexport/dllimport) in maniera condizionale nelle direttive per il preprocessore.
Definiamo la nostra funzione callback:
/*
CALLBACK function declaration
This function will be implemented by a caller process
*/
typedef void(CALLBACK *SendNotification)(LPNOTIFY_ARGS arg);
Dichiariamo una funzione da esportare
/*
Export function
This function will be exported
*/
CALLBACK_API void CallMe(SendNotification DllNotification, LPNOTIFY_ARGS InArgs);
Implementiamo (nel file cpp) la funzione esportata:
// CallBackDemo.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#include "CallBackDemo.h"
//Exported function
CALLBACK_API void CallMe(SendNotification DllNotification, LPNOTIFY_ARGS InArgs)
{
NOTIFY_ARGS arg = { 0 };
if (!InArgs->bFlag)
{
arg.lpszMessage = L"CALLBACK Message 1 from DLL,\r\nthe in bool flag was false, now is true,\r\nthe dwCookie value now is 0";
arg.bFlag = TRUE;
arg.dwCookie = 0;
}
else
{
arg.lpszMessage = L"CALLBACK Message 2 from DLL,\r\nthe in bool flag was true, now is false,\r\nthe dwCookie value now is 1";
arg.bFlag = FALSE;
arg.dwCookie = 1;
}
DllNotification(&arg);
}
Cosa faccia questa funzione è piuttosto semplice, prende in input la struttura InArgs,
ne valuta i valori (assegnati dal processo chiamante) e li modifica settandoli al contrario di come
li ha ricevuti (es: il flag bool viene ricevuto true e viene settato a false).
Quindi chiama la funzione CALLBACK del processo chiamante passando a questo i nuovi valori.
Implementiamo un'applicazione che chiamerà la nostra DLL
Aggiungiamo un progetto Win32 exe alla soluzione di Visual Studio.
Io per comodità ho usato un template (Win32 Dialog Project Template) creato da me (vedi sopra).
Nella dialog già presente nelle risorse aggiungiamo un Button, un EditControl (multiline) ed un CheckBox.
Dovremmo ottenere qualcosa di simile:
Dichiariamo la funzione CALLBACK nel file header dell'applicazione
/*
CALLBACK function implementation
*/
void CALLBACK CallFunctionCallMe(LPNOTIFY_ARGS arg);
Implementiamo la funzione CALLBACK che la nostra DLL chiamerà al momento opportuno
/*
This function will be called from DLL
*/
void CALLBACK CallFunctionCallMe(LPNOTIFY_ARGS arg)
{
HWND hwndEditControl = GetDlgItem(g_hDlg, IDC_EDIT1);
HWND hwndCheck = GetDlgItem(g_hDlg, IDC_CHECK1);
Button_SetCheck(hwndCheck, arg->bFlag);
SetWindowTextW(hwndEditControl, arg->lpszMessage);
}
Cliccando sul button Callback nella dialog attiveremo il callback
case IDC_CALLBACK_BUTTON:
{
HWND hwndCheck = GetDlgItem(hDlg, IDC_CHECK1);
NOTIFY_ARGS args = { 0 };
args.bFlag = Button_GetCheck(hwndCheck);
CallMe(CallFunctionCallMe, &args);
}
return TRUE;
Cosa succederà?
Premendo il button Callback , se il checkbox è flaggato la DLL rileverà che lo è e lo disattiverà attraverso la funzione CALLBACK definita nell'exe
o viceversa: se non è flaggato al momento del click sul button Callback la DLL lo attiverà.
Per fare una prova, eseguire l'applicazione e premere solo il button Callback. Quindi ripremerlo una seconda volta.
Giuseppe Pischedda 2018
Nella parte 2 di questo tutorial scriveremo un'applicazione client .Net che usa la funzione callback nativa Win32:
Processing request, please wait...