View Full Version : Controllare eventi visual c
Salve a tutti
sto cercando un metodo che mi permetta, a un certo punto del programma, di controllare se è stato premuto un tasto sulla tastiera (e quale) e se è stato fatto click sul mouse (e su quale pulsante).
In pratica credo di dover capire quale evento c'è stato, oppure di leggere quale messaggio c'è nella coda.
Potete darmi una mano?
Sto usando Visual C++ 6.0 enterprise
Ma stai usando le API ?!?
credo di no, sto usando le mfc.
Allora con MFC ci sono gli eventi per gestire: WM_MBUTTONDOWN...per il mouse...WM_KEYDOWN per la tastiera...
Stai usando un Dialog ? Basta associare una funzione a quegli eventi e nei parametri leggi il tasto premuto...
ok.
quello che voglio fare è questo: ad un certo punto del programma (fine di un ciclo for) voglio controllare se è stato premuto qualcosa (uno dei pulsanti sull'interfaccia grafica, tramite mouse o tastiera).
Se è stato premuto qualcosa, gestire il codice associato (ad esempio : stop).
Se no, ricomnciare un ciclo.
Il fatto è che nn so come "carpire" gli eventi, ed eseguire il cocice associato, nel bel mezzo della funzione.
Allora devi far evolvere il tuo codice... L'interfaccia deve essere gestita nel thread principale...mentre il tuo for (suppongo sia un calcolo lungo) in un altro thread... Come fai ora l'interfaccia non reagirà mai ad un comando perchè non verrà mai eseguito il codice che processa i messaggi (esiste un solo thread ed è occupato nel for)...
Guarda CreateThread per capire megliio...
Allora devi far evolvere il tuo codice... L'interfaccia deve essere gestita nel thread principale...mentre il tuo for (suppongo sia un calcolo lungo) in un altro thread... Come fai ora l'interfaccia non reagirà mai ad un comando perchè non verrà mai eseguito il codice che processa i messaggi (esiste un solo thread ed è occupato nel for)...
Guarda CreateThread per capire megliio...
vado studio e riferirò.
Avevo cmq intuito di dover procedere in tal modo, spero nn sia difficile... :(
mi basterà l'help per capire?
PS: cmq a me va bene anche che il controllo sia fatto alla fine di un ciclo, cioè nn durante, visto che io voglio che gli effetti di un cambiamento del valore di una variabile (ad esempi)s sia visibile nel ciclo successivo....
Credo di sì...dopo tutto un thread dal punto di vista del codice è una funzione...
Qui c'è un esempio: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/creating_threads.asp
Ma a te ti basta un solo thread...
PS: cmq a me va bene anche che il controllo sia fatto alla fine di un ciclo, cioè nn durante, visto che io voglio che gli effetti di un cambiamento del valore di una variabile (ad esempi)s sia visibile nel ciclo successivo....
Ti consiglio comunque di usare un thread... Dopo ti basterà controllare il valore di una variabile... Se è solo l'interfaccia a scrivere in quella variabile non dovresti nemmeno avere bisogno di primitive di sincronizzazione fra i thread (come le mutex)...
credo anche io...
mi spaventa un pò la gestione dei parametri/variaili che il thread vede ecc....
Il fatto è che nn vorrei doverci mettere molto tempo, sto per finire la tesi e sto probl nn ci voleva... :cry:
Ad esempio:
DWORD WINAPI ProvaThread(LPVOID pExitCheck)
{
bool &exitCheck = (bool)*pExitCheck;
for(......)
{
if(exitCheck)
break;
}
return 0;
}
Per creare il worker thread:
bool exitCheck = false;
HANDLE hThread = CreateThread(NULL, 0, ProvaThread, &exitCheck, 0, NULL);
Puoi semplicmente interromepere l'esecuzione del thread settando exitCheck a true...
Se vuoi che ProvaThread faccia parte di una classe deve essere dichiarato come static... In tal caso ti conviene passare al posto di exitCheck un puntatore a this (visto che è static altrimenti può accedere a soli membri static) e con quello accedere a tutta la classe...
Tanto per farti un esempio:
DWORD WINAPI MiaClasse::ProvaThread(LPVOID pMiaClasse)
{
MiaClasse &miaClasse = (MiaClasse)*pMiaClasse;
miaClasse.eseguiCiclo();
return 0;
}
ti rigrazio moltissimo, ma devo leggere un pò per capire cosa fare...
il fatto è che io conosco benino il c++, ma nn bene il visual c++....
Ad esempio:
DWORD WINAPI ProvaThread(LPVOID pExitCheck)
{
bool exitCheck = (bool)*pExitCheck;
for(......)
{
if(exitCheck)
break;
}
return 0;
}
No, altrimenti testa sempre lo stesso bool "locale".
Meglio:
DWORD WINAPI ProvaThread(LPVOID pExitCheck)
{
for(......)
{
if(*(bool*) pExitCheck)
break;
}
return 0;
}
No, altrimenti testa sempre lo stesso bool "locale".
Mi sonos cordato il riferimento ;)
Ad esempio:
bool &exitCheck = (bool)*pExitCheck;
Ok ma al massimo:
bool &exitCheck = *(bool*)pExitCheck;Sono un po' pignolo, eh? ;)
raga, allora cerco di riepilogare:
la mia intenzione è di fare in modo che, nella finestra di dialogo in cui far eseguire il for, (applicazione dialog based), se premo un tasto "avvia", parte il ciclo for, quando premo "stop" si ferma (questo per cominciare.... :mc: )
Per fare in modo che il pulsante avvia faccia eseguire il thread con il ciclo for, devo fare così: :confused:
- dichiarare la funzione che il thread deve eseguire (con DWORD WINAPI ProvaThread(LPVOID pExitCheck)
- creare il thread con CreateThread (dove devo metterlo? all'interno della funzione che parte quando premo il pulsante avvia?)
- devo dichiarare l'handle al thread? (tipo HANDLE Thread1)?
sono in alto mare.... :cry:
Faccio il pignolo anche io. Sei partito con il loop, ti ho detto: o timer o loop in un (altro) thread e qui invece riparti dal loop nel thread principale... nun ce siamo popio capiti :mbe: sarò io che spiego male. Grazie andbin & cionci :oink:
Faccio il pignolo anche io. Sei partito con il loop, ti ho detto: o timer o loop in un (altro) thread e qui invece riparti dal loop nel thread principale... nun ce siamo popio capiti :mbe: sarò io che spiego male. Grazie andbin & cionci :oink:
hai pienamente ragione, ma io con queste cose ho poca dimestichezza e poco tempo....
sto cercando la soluzione + semplice, ma il programma deve funzionare....
nn dirmi nulla, ti prego :(
Ok ma al massimo:
bool &exitCheck = *(bool*)pExitCheck;Sono un po' pignolo, eh? ;)
Nono, fai bene... E' che ho scritto tutto in fretta...
HANDLE hThread = CreateThread(NULL, 0, ProvaThread, &exitCheck, 0, NULL);
dove va messa questa dichiarazione?
HANDLE hThread = CreateThread(NULL, 0, ProvaThread, &exitCheck, 0, NULL);
dove va messa questa dichiarazione?
lo metti nel punto in cui ricevi l'evento di "avvia", in modo che il thread venga appunto avviato.
ho fatto così
void CTestdsp3Dlg::OnDisegnaBUTTON()
{
// TODO: Add your control notification handler code here
HANDLE hThread = CreateThread(NULL, 0, VelMediaThread,
&m_bStopPlot, 0, NULL);
}
ho dichiarato una variabile membro booleana m_bStopPlot.
Ho inserito la funzione membro nell'header così
DWORD WINAPI VelMediaThread(LPVOID pExitCheck);
e poi ho dichirarto così la funzione
DWORD WINAPI CTestdsp3Dlg::VelMediaThread(LPVOID pExitCheck)
{
.....
}
mi da questo errore
C:\Documents and Settings\squillante\Desktop\testdsp3\testdsp3Dlg.cpp(419) : error C2664: 'CreateThread' : cannot convert parameter 3 from 'unsigned long (void *)' to 'unsigned long (__stdcall *)(void *)'
che ho combinato?
Se vuoi che ProvaThread faccia parte di una classe deve essere dichiarato come static... In tal caso ti conviene passare al posto di exitCheck un puntatore a this (visto che è static altrimenti può accedere a soli membri static) e con quello accedere a tutta la classe...
Tanto per farti un esempio:
DWORD WINAPI MiaClasse::ProvaThread(LPVOID pMiaClasse)
{
MiaClasse &miaClasse = *(MiaClasse*)pMiaClasse;
miaClasse.eseguiCiclo();
return 0;
}
;)
me tapino... nn ci ho capito molto....
allora...
nell'header devo dichiarare la funzione come static DWORD WINAPI CMiaClasse::ProvaThread(..) ?
ma la chiamata nel pulsante "avvia" resta uguale?
La chaimata sarà così:
void CTestdsp3Dlg:nDisegnaBUTTON()
{
HANDLE hThread = CreateThread(NULL, 0, VelMediaThread,
this, 0, NULL);
}
DWORD WINAPI CTestdsp3Dlg::VelMediaThread(LPVOID pTestdsp3Dlg)
{
CTestdsp3Dlg &testdsp3Dlg = *(CTestdsp3Dlg *)pTestdsp3Dlg;
for(...)
{
.....
if(testdsp3Dlg.m_bStopPlot)
break;
}
return 0;
}
In pratica usi testdsp3Dlg per accedere all'istanza delal classe che ha lanciato il thread...
nell'header devo dichiarare la funzione come static DWORD WINAPI CMiaClasse::ProvaThread(..) ?
Nell'header avrai:
class CTestdsp3Dlg
{
...
...
static DWORD WINAPI VelMediaThread(LPVOID pTestdsp3Dlg);
}
sto facendo una prova
in pratica alla funzione VelMediaThread quando viene chiamata faccio comparire un mess a video con MessageBox
perchè mi dice
C:\Documents and Settings\squillante\Desktop\testdsp3\testdsp3Dlg.cpp(264) : error C2352: 'CWnd::MessageBoxA' : illegal call of non-static member function
ops, forse ho capito cosa sbagliavo...
ora ho fatto un programmino prova, in cui il thread dichiarato inùcrementa ll'infinito un numero (visualizzato a video)
nell'header
static DWORD WINAPI ThreadProc(LPVOID pThrClasse);
nel file .cpp della finestra di dialogo
DWORD WINAPI CThreadsDlg::ThreadProc(LPVOID pThrClasse)
{
CThreadsDlg &thread = *(CThreadsDlg *)pThrClasse;
thread.Incrementa();
return 0;
}
ho poi definito sta funzione (guarda come sono grezzo nel programmare)
void CThreadsDlg::Incrementa()
{
int i;
char p[10];
for (i=0;;i++)
{
itoa(i,p,10);
m_iThr1 = p;
UpdateData(FALSE);
}
}
quindi, nella funzione chiamata dal pulsante avvia
void CThreadsDlg::OnAvviaBUTTON()
{
// TODO: Add your control notification handler code here
HANDLE hThread = CreateThread(NULL, 0, ThreadProc, this, 0, NULL);
}
quando premo avvia, mi da errore di debug (debug assertion failed, file wincore.cpp, line 879)
che sbaglio?
(ovviamente, se setto Release come configurazione va bene... :oink: )
????
sembra che il problema insorga quando chiamo la funzione UpdateData.
Lascio perdere?
Ci guardo domani mattina...ora non posso ;)
Ci guardo domani mattina...ora non posso ;)
pregherò per una tua illuminazione :D
GRAZIEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
Ottengo anche io lo stesso errore...eppure lo usato in altre occasioni questo metodo... Ora vado a vedere i codici vecchi...
confido in te..... :help:
nn sarà che sbaglio a fare UpdateData ogni volta?
Ah...cacchio...ora mi ricordo... Puoi condividere dati, ma l'interfacciamento con la GUI lo devi fare a scambio di messaggi !!! ;)
Ah...cacchio...ora mi ricordo... Puoi condividere dati, ma l'interfacciamento con la GUI lo devi fare a scambio di messaggi !!! ;)
cioè? devo definire dei messaggi apposta?
usare funzioni tipo PostMessage?
cavolaccio...... :mc: :muro:
E' facile !!! Basta che tu faccia un messaggio che fa chiamare UpdateData...sui dati ci lavori in maniera trasparente !!!
//definisco un messaggio che è valido solo all'interno dell'applicazione
#define WM_UPDATEDATA WM_USER+10000
.....
.....
BEGIN_MESSAGE_MAP(CprovaMFCDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
ON_BN_CLICKED(IDC_BUTTON1, OnBnClickedButton1)
ON_MESSAGE(WM_UPDATEDATA, OnUpdateData)
END_MESSAGE_MAP()
.....
.....
void CprovaMFCDlg::OnBnClickedButton1()
{
mStopThread = mStopThread != true;
if(mStopThread == false)
{
CreateThread(NULL, 0, ThreadFunction, this, 0, NULL);
}
}
DWORD WINAPI CprovaMFCDlg::ThreadFunction(LPVOID objPointer)
{
CprovaMFCDlg *dlg = (CprovaMFCDlg *)objPointer;
while(!dlg->mStopThread)
{
Sleep(1000);
dlg->mEditValue++;
dlg->SendMessage(WM_UPDATEDATA);
}
return 0;
}
//questo gestisce il messaggio WM_UPDATEDATA
LRESULT CprovaMFCDlg::OnUpdateData(WPARAM param1, LPARAM param2)
{
UpdateData(FALSE);
return 0;
}
La prova che ho fatto ha un button che avvia e fa terminare il thread... Durante l'esecuzione il thread aggiorna il valore di un int che è mappato su una textbox...
Ogni volta che fa l'aggiornamento invia un messaggio che fa eseguire l'update...
sembra abba chiaro (per me, nn che iltuo esempio nn lo sia....)
domande:
perche fai così
mStopThread = mStopThread !=true;
poi nel while, a che serve Sleep(1000)? un ritardo(di 1 sec)?
questa procedura che mi hai mostrato la ripeto per ogni messaggio che mi serve?
Grazie, sei un grande...
La sleep l'ho messa solo per far incrementare la variabile una volta al secondo...
mStopThread = mStopThread != true;
mi è rimasto lì per sbaglio, volevo intendere questo:
mStopThread = !mStopThread;
In pratica nego il valore corrente di mStopThread...e lo riassegno a mStopThread...ma questo te lo puoi gestire come più ti aggrada, l'importante è che quando viene eseguito per le prima volta il ciclo (o il controllo del tuo for) nel thread mStopThread sia false...
Mi sembra di aver capito il tuo thread deve disegnare un grafico...in tal caso io comunque sospenderei il thread fino a quando il pixel non è stato disegnato...
Quindi la sequenza da fare sarebbe:
dlg->drawn = false;
setti tutti i dati per disegnare il pixel...
dlg->SendMessage(WM_UPDATEDATA); //per aggiornare eventuali variabili dell'interfaccia
dlg->SendMessage(WM_DRAWDATA); //per disegnare il grafico
while(!dlg->drawn)
Sleep(1);
E' un'attesa quasi attiva...non mi piace per niente...ma è sicuramente più semplice di usare primitive di sincronizzazione...
Ovviamente la funzione che gestisce il messaggio deve settare drawn a true prima del return...
grazie!
questo nuovo strumento (per me :fagiano: ) dei messaggi mi risolve molti problemi....
credo che ora posso finalmente andare avanti....
spero di nn doverti disturbare ancora (almeno nn subito :D :D :D )
Buona giornata!
un ultima cosa: perchè alla funzione UOnUpdateData hai passato due parametri? (WPARAM e LPARAM)
a che possono servire?
Tieni presente che l'esecuzione delle funzioni che gestiscono i messaggi non è detto che avvenga prima dell'istruzione successiva del thread...
dlg->SendMessage(WM_XXXXXX);
dlg->pippo = 1;
Non è detto che la funzione che gestisce il messaggio WM_XXXXXX venga eichiamata prima che pippo venga messo a 1...quindi se la funzione usa pippo, potrebbe succedere un patatrack ;)
Tieni presente che l'esecuzione delle funzioni che gestiscono i messaggi non è detto che avvenga prima dell'istruzione successiva del thread...
dlg->SendMessage(WM_XXXXXX);
dlg->pippo = 1;
Non è detto che la funzione che gestisce il messaggio WM_XXXXXX venga eichiamata prima che pippo venga messo a 1...quindi se la funzione usa pippo, potrebbe succedere un patatrack ;)
in pratica se la variabile pippo è usata dalla funzione che gestisce il messaggio, conviene passarla come parametro alla funzione?
No...l'esempio è come quello sopra relativo al disegno... Se non attendessi che la funzione di diesgno avesse finito il suo lavoro (drawn a true) gli potrei cambiare i dati su cui lavora mentre ci sta lavorando (nuova iterazione del ciclo) e questo porterebbe chiaramente a dei casini...
ultima cosa.....
mi fai un esempio di uso dei parametri?
te lo chiedo perchè i "casini" di cui stai parlando erano proprio quelli che credevo di evitare facilmente con i messaggi!
I parametri secondo me non li devi nemmeno usare... Puoi mettere tutto nella funzione che gestisce il messaggio che invio sotto...
Prova a farlo così il ciclo:
for(int i=0; i<N; ++i)
{
dlg->drawComplete = false;
dlg->SendMessage(WM_DODRAWSTUFF);
while(!dlg->drawComplete)
Sleep(1);
if(dlg->stopThread)
break;
}
I parametri secondo me non li devi nemmeno usare... Puoi mettere tutto nella funzione che gestisce il messaggio che invio sotto...
Prova a farlo così il ciclo:
for(int i=0; i<N; ++i)
{
dlg->drawComplete = false;
dlg->SendMessage(WM_DODRAWSTUFF);
while(!dlg->drawComplete)
Sleep(1);
if(dlg->stopThread)
break;
}
perfetto, proprio come pensavo
ciao e ancora thanks very much!!!!
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.