2015-10-09 8 views
8

Di seguito è tratto dalla sezione Osservazioni della documentazione MoveWindow():Calling MoveWindow() con bRepaint impostata su true

Se il parametro bRepaint è TRUE, il sistema invia il messaggio WM_PAINT alla routine di finestra immediatamente dopo aver spostato la finestra (ovvero, la funzione MoveWindow chiama la funzione UpdateWindow).

così ho pensato che quando chiamo MoveWindow() con bRepaint insieme al TRUE, la procedura di finestra sarà chiamato immediatamente e superato un messaggio WM_PAINT, ma questo è ciò che il mio test mostra:

  • Quando MoveWindow() è chiamato, la procedura della finestra si chiama immediatamente, ma viene inviato un messaggio WM_ERASEBKGND e non un messaggio WM_PAINT.
  • L'area non è ancora valida e quindi quando torno al ciclo dei messaggi e nessun messaggio si trova nella coda messaggi, viene inviato un messaggio WM_PAINT.

Ho interpretato la documentazione errata?

Nota: Sto parlando di chiamare il metodo MoveWindow() sull'oggetto finestra padre.


Edit:

Questo è il mio codice di prova:

/* Left mouse click on the window to call MoveWindow() */ 

#include <Windows.h> 

HWND hEdit; 

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    switch(message) 
    { 
    case WM_LBUTTONDOWN: 
     MoveWindow(hWnd, 200, 200, 700, 700, TRUE); 

     // Do not go back to message loop immediately 
     Sleep(3000); 
     break; 
    case WM_ERASEBKGND: 
     { 
      SendMessage(hEdit, WM_CHAR, (WPARAM)'e', 0); 
     } 
     break; 
    case WM_PAINT: 
     { 
      SendMessage(hEdit, WM_CHAR, (WPARAM)'p', 0); 

      PAINTSTRUCT ps; 
      HDC hdc = BeginPaint(hWnd, &ps); 

      EndPaint(hWnd, &ps); 
     } 
     break; 
    case WM_CLOSE: 
     DestroyWindow(hWnd); 
     break; 
    case WM_DESTROY: 
     PostQuitMessage(0); 
     break; 
    default: 
     return DefWindowProc(hWnd, message, wParam, lParam); 
    } 
    return 0; 
} 

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
    LPSTR lpCmdLine, int nCmdShow) 
{ 
    WNDCLASSEX wc; 
    wc.cbSize = sizeof(WNDCLASSEX); 
    wc.style = 0; 
    wc.lpfnWndProc = WndProc; 
    wc.cbClsExtra = 0; 
    wc.cbWndExtra = 0; 
    wc.hInstance = hInstance; 
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); 
    wc.hCursor = LoadCursor(NULL, IDC_ARROW); 
    wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); 
    wc.lpszMenuName = NULL; 
    wc.lpszClassName = "WinClass"; 
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); 
    RegisterClassEx(&wc); 

    HWND hWnd = CreateWindowEx(0, "WinClass", "", WS_OVERLAPPEDWINDOW, 261, 172, 594, 384, NULL, NULL, hInstance, NULL); 
    hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL, 0, 0, 400, 21, hWnd, NULL, hInstance, NULL); 

    ShowWindow(hWnd, nCmdShow); 
    UpdateWindow(hWnd); 

    MSG msg; 
    while(GetMessage(&msg, NULL, 0, 0) > 0) 
    { 
     TranslateMessage(&msg); 
     DispatchMessage(&msg); 
    } 
    return msg.wParam; 
} 
+0

Possiamo vedere un semplice codice di esempio che funziona come hai descritto? – cdonts

+1

È normale che 'WM_ERASEBKGND' preceda' WM_PAINT' come parte di un ciclo di ripetizione standard, i documenti non sono abbastanza dettagliati. –

+0

@Jonathan Potter Sto parlando del fatto che la procedura della finestra è chiamata direttamente per 'WM_ERASEBKGND', ma non è chiamata direttamente per' WM_PAINT'. La documentazione dice: * "la funzione MoveWindow chiama la funzione UpdateWindow" *, quindi questo significa che la procedura della finestra deve essere chiamata direttamente per 'WM_PAINT', ma questo non sta succedendo! –

risposta

2

Cosa succede probabilmente è che MoveWindow manda WM_ERASEBKGND utilizzando SendMessage (che chiamerà il WndProc richiamata immediatamente e attendere per la sua elaborazione) ma WM_PAINT tramite PostMessage (che inserirà il messaggio nella coda, quindi sarà processato d dopo aver dormito, quando viene chiamato DispatchMessage).

Non so se è solo un test o stai elaborando qualcosa dopo aver usato MoveWindow che blocca la coda dei messaggi. Se è così, allora dovresti considerare di spostare quel lavoro su un altro thread!

Spero che aiuti.

+0

Il mio codice è solo un test per vedere come funziona 'MoveWindow()'. La documentazione dice: * "la funzione MoveWindow chiama la funzione UpdateWindow" *, ora 'UpdateWindow()' dovrebbe chiamare direttamente la procedura della finestra e passargli un messaggio 'WM_PAINT', ma ciò non sta accadendo, quindi significa che la documentazione è sbagliato o mi sto perdendo qualcosa? Inoltre, non è sbagliato passare 'WM_PAINT' a' PostMessage() ', e la cosa giusta da fare è semplicemente invalida la regione? –

+0

La citazione che hai citato proviene dalla documentazione di 'UpdateWindow()'. Sì, è vero quello che hai detto, se non invalido una regione prima di chiamare 'UpdateWindow()', non verrà inviato alcun messaggio. Ma quando ho chiamato 'MoveWindow()' nel mio codice, ha aumentato la dimensione della finestra, e quindi una parte della finestra è stata invalidata. Così ora quando 'MoveWindow()' chiama 'UpdateWindow()' (come menzionato nella documentazione 'MoveWindow()'), la procedura della finestra dovrebbe essere chiamata direttamente e un messaggio 'WM_PAINT' dovrebbe essere passato ad esso, ma questo non è accadendo! –

+0

@rony_t Proprio così. Ho testato 'UpdateWindow' con un' InvalidateRect' precedente e 'WM_PAINT' è stato inviato usando' SendMessage'. Quindi probabilmente la documentazione è sbagliata quando dice che 'MoveWindow' chiama' UpdateWindow' (l'ho smontato e non ho visto nessuna chiamata). – cdonts

6

Ho interpretato la documentazione errata?

Fondamentalmente sì. Hai scoperto un piccolo aspetto della documentazione MSDN sulle funzioni winapi che è molto, molto importante sapere. Non è scritto per essere un tutorial. Presuppone una comprensione di base di come funziona winapi, il tipo di conoscenza che si ottiene leggendo il libro "Programmazione di Windows" di Petzold.

Quel libro può insegnarti che il ciclo di pittura di Windows include sempre WM_ERASEBKGND. Quindi lo sfondo è stato dipinto per primo, e in qualsiasi momento lo si disegna sopra con WM_PAINT.

Diversi motivi per cui tali dettagli di implementazione vengono saltati nella documentazione MSDN. Prima di tutto, ce n'è molta e tutto, anche solo, rende difficile approfondire l'articolo. Successivamente, è piuttosto inusuale scrivere un gestore di messaggi per WM_ERASEBKGND. Si normalmente basta passarlo a DefWindowProc(). Che utilizza il WNDCLASSEX.hbrBackground selezionato, il 99% delle volte abbastanza buono per portare a termine il lavoro. Nota come la tua finestra sembra rovinata perché è quello che non hai fatto. Dato che hai scritto un gestore di messaggi, ora è compito tuo occupartene. Facile da fare, basta chiamare DefWindowProc().

Infine, la documentazione MSDN omette i dettagli perché inchiodarli rende molto difficile migliorare il funzionamento di Windows. C'è un altro dettaglio di implementazione che puoi vedere dal tuo programma di test. Molto spesso, chiamando MoveWindow con bPaint = TRUE fa non dipingi qualsiasi cosa. Facile da vedere spostando la finestra trascinandola con la barra del titolo dopo averla prima cliccata. Si noti come facendo di nuovo clic la finestra ritorna indietro ma non si ottiene né WM_ERASEBKGND né WM_PAINT.

Questa è un'ottimizzazione al lavoro, che fa funzionare meglio Windows. E non menzionato nell'articolo MSDN. Se la finestra non si sposta dallo schermo e torna indietro e la dimensione della finestra non cambia, allora può prendere una scorciatoia. Semplicemente copia i pixel nel buffer del frame video dalla vecchia posizione alla nuova posizione. Molto più efficiente di lasciare che l'app ridipinga tutto. Se si esegue con Aero abilitato, è ancora più ottimizzato, non è necessario copiare i pixel.

Ultimo ma non meno importante, mentre la scrittura di codice come questo per il reverse engineering di Windows è piuttosto istruttiva e consigliata, non è necessario. È molto più semplice usare lo Spy++ utility.

+0

Grande spiegazione sull'aspetto dell'ottimizzazione (come Windows può a volte dipingere la mia finestra senza chiedergli di farlo). Ma questo non spiega perché la procedura della finestra non è stata chiamata immediatamente e ha passato un messaggio 'WM_PAINT' (come indicato nella documentazione), ma è stato chiamato più tardi come parte del ciclo del messaggio, voglio dire se Windows vuole usare l'ottimizzazione in In questo caso, non avrebbe dovuto inviare un messaggio 'WM_PAINT' in primo luogo, ma dato che lo faceva comunque, avrebbe dovuto farlo chiamando' UpdateWindow() '! –

+0

Indica se pubblicare o inviare i messaggi è un altro dettaglio di implementazione pesante che non viene affrontato nei documenti MSDN. Controllare [questo post] (http://stackoverflow.com/a/33100217/17034), prestare attenzione all'avviso. Il caso molto più interessante per MoveWindow è quando utilizzare FALSE. Il caso insolito. Lo fai quando muovi più finestre, riordinando il layout per esempio. –

+0

Ma in effetti è indirizzato nei documenti MSDN: * "Se il parametro bRepaint è TRUE, il sistema invia il messaggio WM_PAINT alla procedura della finestra immediatamente dopo lo spostamento della finestra (ovvero, ** la funzione MoveWindow chiama la funzione UpdateWindow **) "*. –

1

Ho interpretato la documentazione errata?

Sì e no.

No, la documentazione è abbastanza chiara su questo.

Sì, come ha detto Hans Passant, non è possibile fare affidamento su tali dettagli da MSDN.

Esistono molte funzioni WinAPI con "trucchi", comportamento non documentato o stato dell'ambiente previsto (inclusi i tempi) e così via. Potrebbe essere che si è comportato come specificato in alcune versioni di Windows. Forse lo fa ancora, in alcune circostanze.

In pratica, MS testerà molte applicazioni per vedere se funzionano dopo aver apportato una modifica come questa. In questo caso, poiché in genere si elabora WM_PAINT "quando succede" e si esegue solo la pittura, è facile vedere come nella maggior parte delle applicazioni questa modifica non influenzi il risultato dell'utente finale.

Quindi, prendere sempre MSDN come "descrizione generale". Usa i tuoi test e altre fonti per ottenere i dettagli del comportamento reale. Sii contento che tu stia lavorando con l'API relativa a Windows, se mai utilizzi delle API meno utilizzate, sarai molto peggio (ho sofferto molto con le API relative a USB/HID).

1

non volevo ignorare il codice di test fornito da Rony, quindi ho deciso di pubblicare la mia alternativa, che ritengo sia più adatta per eseguire il debug di Windows Message, l'unico requisito per gli strumenti esterni di visualizzare il messaggio di debug DebugView. questo ovviamente può essere sostituito da una listbox se qualcuno lo desidera.

#include <Windows.h> 

/* 
    get DebugView from here https://download.sysinternals.com/files/DebugView.zip 
*/ 



/*___________________________________________________________________________________ 
*/ 
void __cdecl DebugPrint(TCHAR *fmt,...){ 
    va_list args = NULL; 
    va_start(args, fmt); 
    static TCHAR _buff[512]=""; 
    TCHAR buff[512]; 
    wvsprintf(buff,fmt,args); 
    va_end(args); 
    if(lstrcmp(buff,_buff)) 
     OutputDebugString(buff); 
    lstrcpy(_buff,buff); 
    return ; 

} 

/*___________________________________________________________________________________ 
*/ 
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    switch(message) 
    { 
     case WM_RBUTTONDOWN: 
      MoveWindow(hWnd, 200, 200, 700, 700, TRUE); 
      break; 
     case WM_MOVE: 
      DebugPrint("WM_MOVE"); 
      break;  
     case WM_SIZE: 
     DebugPrint("WM_SIZE"); 
     break; 

    case WM_ERASEBKGND: 
     DebugPrint("WM_ERASEBKGND"); 
     break; 
    case WM_PAINT: 
     DebugPrint("WM_PAINT"); 

     if(1){ 
      PAINTSTRUCT ps; 
      TCHAR buff[]="Right mouse click on the window to call MoveWindow()"; 
      HFONT hf=(HFONT)GetStockObject(DEFAULT_GUI_FONT); 
      HDC hdc = BeginPaint(hWnd, &ps); 
       hf=SelectObject(hdc,hf); 
       TextOut(hdc,8,12,buff, sizeof(buff)-sizeof(TCHAR)); 
       hf=SelectObject(hdc,hf); 
      EndPaint(hWnd, &ps); 
     }else{ 
      return DefWindowProc(hWnd, message, wParam, lParam); 
      } 
     break; 

    case WM_DESTROY: 
     PostQuitMessage(0); 
      break; 
     default: 
      return DefWindowProc(hWnd, message, wParam, lParam); 

    } 

    return 0; 
} 
/*___________________________________________________________________________________ 
*/ 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
    LPSTR lpCmdLine, int nCmdShow) 
{ 
    WNDCLASSEX wc; 
    ZeroMemory(&wc,sizeof(wc)); 

    wc.cbSize = sizeof(WNDCLASSEX); 

    wc.lpfnWndProc = WndProc; 
    wc.hInstance = hInstance; 
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); 
    wc.hCursor = LoadCursor(NULL, IDC_ARROW); 
    wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); 
    wc.lpszClassName = "WinClass"; 
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); 
    RegisterClassEx(&wc); 

    HWND hWnd = CreateWindowEx(0, "WinClass", "",WS_OVERLAPPEDWINDOW|WS_VISIBLE, 
     261, 172, 594, 384, NULL, NULL, hInstance, NULL); 


    MSG msg; 
    while(GetMessage(&msg, NULL, 0, 0) > 0) 
    { 
     TranslateMessage(&msg); 
     DispatchMessage(&msg); 
    } 
    return msg.wParam; 
} 
+0

utilizza il pulsante destro del mouse invece del pulsante sinistro del mouse per richiamare MoveWindow() – milevyo

Problemi correlati