Win32 CALLBACK Functions

How to create and use Win32 callback functions in C/C++



Una funzione callback è una funzione il cui prototipo è stato dichiarato in una certa DLL la cui implementazione è invece delegata al processo chiamante.
La DLL, quando chiamata, a sua volta, potrà chiamare 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:

Win32 DLL CALLBACK 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.

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:


Win32 DLL CALLBACK


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.

Win32 DLL CALLBACK Win32 DLL CALLBACK Win32 DLL CALLBACK




Giuseppe Pischedda 2018



Nella parte 2 di questo tutorial scriveremo un'applicazione client .Net che usa la funzione callback nativa Win32:







Se il post ti è utile puoi fare una donazione all'autore, l'importo è a tua libera scelta.

Grazie