Newsgroup: it.comp.programmare.win32
Titolo: Passaggio riferimenti con SendMessage

Ciao a tutti,
sto realizzando un programma Win32 (no atl, no MFC etc..il programma è 
al 90% in C++, in C "plain" è tutta la gestione della user interface con 
callback).
Il programma ascolta su una porta in rete con thread e alla ricezione di 
un determinato  messaggio crea un oggetto di una mia classe. Poi usa una 
SendMessage con un messaggio custom per inviare l'oggetto alla callback 
della finestra principale.
Praticamente nel thread avviene qualcosa di simile a:
{
	...
	MiaClasse *obj = new MiaClasse();
	SendMessage(hWnd, MIO_MESSAGGIO, (WPARAM)NULL, (LPARAM)obj);
	....
}
Nella callback
 
{
	...
	case MIO_MESSAGGIO:
		MiaClasse *obj = (MiaClasse *)lParam;
		// uso obj
 
		delete obj;
		break;
}
Ora la mia domanda è relativa alla delete, è corretto agire in questo 
modo? Esiste un meccanismo piu' elegante per far si che non si generino 
memory leak nel passaggio di oggetto tramite SendMessage ?
Grazie
Fabrizio

 Fabrizio
Per inviare messaggi, si può usare la SendMessage e la PostMessage.
La differenza tra le 2 funzioni (se utilizzate per inviare messaggi a 
finestre e/o controlli dello stesso programma) è la seguente:
SendMessage è sincrona, PostMessage no.
Ergo, SendMessage richiama direttamente la window/dialog procedure della 
finestra identificata dall'handle in caso che tale procedura e la 
procedura chiamante appartengano allo stesso thread, altrimenti in caso 
appartengano a thread differenti, mette il messaggio in coda alla 
finestra selezionata, sospende il thread chiamante e aspetta fintanto 
che la window/dialog procedure non elabora quel messaggio.
Difatti il valore di ritorno di una window procedure [o il valore 
settato da una SetWindowLong(hDlg,DWL_MSGRESULT,result) per le dialog 
procedure] viene restituito dalla SendMessage. In questo modo si possono 
creare messaggi che restituiscono valori (come ad esempio il messaggio 
WM_GETTEXTLENGTH).
PostMessage, invece, accoda semplicemente il messaggio e continua.
MSDN (e molti programmatori) suggeriscono di utilizzare puntatori 
solamente con le SendMessage e non con le PostMessage a meno che le aree 
di memoria non cambino nel lasso di tempo da quando viene eseguita la 
PostMessage a quando il messaggio viene effettivamente eseguito (puoi 
anche fare delle prove a riguardo).
Con la SendMessage (in quanto sincrona) tale problema non si presenta.
Quindi, per concludere (e spero di non essere stato troppo pedante) un 
modo per scrivere il codice elegante (e funzionante) potrebbe essere
MiaClasse *obj=new MiaClasse();
SendMessage(hWnd,MIO_MESSAGGIO,NULL,(LPARAM)obj);
delete obj;
Luca

Ti ringrazio sei stato esaustivo e hai risolto totalmente il mio dubbio....
Ancora grazie
Luca Pascali ha scritto:


anche fare delle prove a riguardo).
Non mi è molto chiaro cosa intendi.  Concordo sul fatto che un
indirizzo passato mediante uno dei parametri di PostMessage debba
appartenere allo stesso spazio di indirizzamento in cui lavora il
thread che esegue il fetch del messaggio, ma che vuoi dire con "a meno
che le aree di memoria non cambino[...]" ?
Per i messaggi utente (da WM_USER in poi) si possono usare benissimo
dei puntatori come parametri (lParam o wParam) della PostMessage.  Io
lo faccio, ad esempio, quando ho bisogno di far comunicare due thread
tra loro.  Ogni thread estrae i messaggi per se stesso mediante
GetMessage() e all'atto della comunicazione all'altro thread usa una
PostThreadMessage().  Le informazioni scambiate sono presenti in aree
di memoria di cui viene passato l'indirizzo.  Il thread A alloca
memoria, la popola con dei dati, manda l'indirizzo al thread B
mediante PostThreadMessage(); il thread B riceve il messaggio, preleva
l'indirizzo, accede ai dati, dealloca la memoria.

modo?
Sě.

 che le aree di memoria non cambino[...]" ?
[...]
Provo a spiegarmi meglio:
Che si possano usare i puntatori, si possono sempre usare (nel senso che 
il compilatore non si lamenta), in alcuni casi, però, l'effetto non è 
quello desiderato e nel peggiore dei casi può funzionare qualche volta.
La PostMessage non è sincrona, quindi quando la PostMessage viene 
invocata e quando il messaggio viene processato sono 2 momenti differenti.
Il fatto che l'indirizzo debba appartenere allo stesso spazio di 
allocazione è abbastanza ovvio, ma è anche necessario che l'area di 
memoria sia ancora valida.
Infatti con un codice del tipo
void test(HWND hWnd) {
   int stackvar[5]={1,2,3,4,5};
   //Accodiamo senza fermarci
   PostMessage(hWnd,WM_USER+1,NULL,(LPARAM)stackvar);
}
cosa riceve la window/dialog procedure quando esegue il messaggio?
Stessa cosa se il puntatore invece che essere nello stack si trova nello 
  heap e nel frattempo qualcuno ha chiamato una free o una delete.
Con la SendMessage in caso dello stesso thread, il SO invoca 
direttamente la window/dialog procedure con i parametri passati (il 
tutto si comporta come una chiamata a funzione). Nel caso di thread 
differenti (il fatto che stia parlando dello stesso processo è 
implicito) il thread che invia il messaggio si sospende in attesa che il 
messaggio sia processato.
Spero di essere stato più chiaro.
LP

  heap e nel frattempo qualcuno ha chiamato una free o una delete.
Certo, non capivo perché questo lo stavo dando per scontato.  E' solo
che pensavo stessi affermando che con PostMessage, in generale, è bene
non usare puntatori, cosa che non è ovviamente vera.  Tanto per
rifarci al post originale: la tecnica usata dall'OP è corretta.
In ogni caso, ci tengo a precisare che con SendMessage() non si
esclude in assoluto il problema, difatti qualsiasi funzione sincrona
(come SendMessage()) può essere resa asincrona (come PostMessage()),
spostandola in un thread parallelo.  Nulla vieta che io allochi
memoria, invochi SendMessage() in un thread secondario e deallochi la
memoria.  In questo caso avrei trasformato SendMessage() in una
pseudo-PostMessage() ("pseudo", anche perché PostMessage(), a
differenza di SendMessage(), non assicura che il messaggio sia
recapitato) e potrei cadere vittima dello stesso problema.

 Sì.
Se non ci si vuol far male con i thread è bene lasciare da parte la
SendMessage() ed usare la PostMessage().
Se si deve gestire un'area di memoria, la si alloca nel thread, si
posta e poi spetta all'altro thread deallocarla (come giustamente detto
da Laforgia).
Se si ha paura di eventuali memory leak, anziché postare l'indirizzo,
si può usare una coda gestita tramite sezioni critiche per l'accesso
concorrenziale e postare solo il messaggio di avviso (o magari
segnalare tramite un evento se l'altro thread è di lavoro e non gui).

Sě.
Una piccola precisazione sulle allocazioni:
"Some of the functions in the single-threaded runtime library may
not work properly in a multithreaded program. This includes
functions malloc() and free() (or new and delete in C++), any of
the functions in stdio.h or io.h, and functions such as
asctime(), strtok(), and rand()."
Tratto da "Modern Multithreading".

functions malloc() and free()[...]
Che alcune funzioni della libreria standard del C non siano
thread-safe è purtroppo risaputo da sempre.  In ogni caso, per
l'allocazione/deallocazione di memoria, in questi contesti,
personalmente utilizzo sempre e solo le funzioni dirette del sistema
operativo.

SendMessage()[...].
Se il thread parallelo č uno solo (come in questo caso), non c'č alcun
problema.