Se MessageBox () / correlato sono sincroni, perché non il mio ciclo di messaggi congelare?
-
12-09-2019 - |
Domanda
Perché è che se io chiamo una funzione apparentemente sincrono di Windows come MessageBox()
interna del mio ciclo di messaggi, il ciclo stesso non congelare come se ho chiamato Sleep()
(o una funzione simile) invece? Per illustrare il mio punto, prendere il seguente WndProc
scheletrico:
int counter = 0;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
SetTimer(hwnd, 1, 1000, NULL); //start a 1 second timer
break;
case WM_PAINT:
// paint/display counter variable onto window
break;
case WM_TIMER: //occurs every second
counter++;
InvalidateRect(hwnd, NULL, TRUE); //force window to repaint itself
break;
case WM_LBUTTONDOWN: //someone clicks the window
MessageBox(hwnd, "", "", 0);
MessageBeep(MB_OK); //play a sound after MessageBox returns
break;
//default ....
}
return 0;
}
Nell'esempio precedente, la funzione principale del programma è quello di eseguire un timer e visualizzare il valore del contatore ogni secondo. Tuttavia, se l'utente fa clic su nostra finestra, il programma visualizza una finestra di messaggio e quindi emette un segnale acustico dopo la scatola è chiusa.
Ecco dove si fa interessante: possiamo dire MessageBox()
è una funzione sincrona perché MessageBeep()
non viene eseguito fino a quando la finestra di messaggio si chiude. Tuttavia, il timer continua a funzionare, e la finestra viene ridisegnata ogni secondo anche mentre si visualizza la finestra di messaggio. Così, mentre MessageBox()
è apparentemente una chiamata di funzione di blocco, gli altri messaggi (WM_TIMER
/ WM_PAINT
) può ancora essere elaborati. Va bene, a meno che sostituisco MessageBox per un altro blocco delle chiamate come Sleep()
case WM_LBUTTONDOWN:
Sleep(10000); //wait 10 seconds
MessageBeep(MB_OK);
break;
Questa blocchi la mia domanda del tutto, e non l'elaborazione dei messaggi avviene per i 10 secondi (WM_TIMER
/ WM_PAINT
non sono trattati, il contatore non viene aggiornato, il programma 'congela', ecc). Allora perché è che MessageBox()
permette l'elaborazione dei messaggi di continuare mentre Sleep()
non lo fa? Dato che la mia domanda è single-threaded, che cos'è che fa MessageBox()
per consentire questa funzionalità? Il sistema di 'replicare' il mio thread dell'applicazione, in modo che modo si può finire il codice WM_LBUTTONDOWN
volta MessageBox()
è fatto, pur consentendo il thread originale per elaborare gli altri messaggi nel frattempo? (Che era la mia ipotesi ignoranti)
Grazie in anticipo
Soluzione
Il MessageBox()
e simili funzioni API Windows non blocchino l'esecuzione, come un'operazione IO o mutexing farebbe. La funzione MessageBox()
crea una finestra di dialogo di solito con un pulsante OK - in modo che ci si aspetta la gestione automatica dei messaggi di Windows relative alla finestra di messaggio. Questo viene implementato con il proprio ciclo di messaggi - non viene creato nessun nuovo thread, ma l'applicazione rimane sensibile, perché i messaggi selezionati come Paint sono gestite chiamando ricorsivamente la funzione WndProc()
, e alcuni messaggi non vengono trasmesse a, a causa del tipo modale del creato finestra.
Il sonno () e altre funzioni chiamate direttamente dal proprio la gestione di un messaggio di Windows WndProc()
, sarebbe in realtà bloccare l'esecuzione del singolo messaggio filettato Loop, nessun altro messaggio verrà elaborato.
Altri suggerimenti
MessageBox gestisce il proprio ciclo di messaggi Win32 (in modo da non congelare chiama app).
Attenzione utilizzarlo in funzioni non rientranti ...
EDIT: elaborare: ciclo di messaggi su Windows è qualcosa di simile (rubato da MSDN):
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
DispatchMessage chiamerà qualunque procedura di finestra è necessario. Quella finestra proc può iniziare il proprio ciclo (sullo stesso thread), e chiamerà DispatchMessage sé, che chiamerà qualsiasi gestori di messaggi.
Se volete vederlo, lanciare la vostra applicazione in debugger, pop-up finestra di messaggio e pausa. Sarete caduto da qualche parte all'interno del suo ciclo. Guardate la stack e vedere se è possibile trovare ciclo genitore.