Impedisce la riverniciatura della finestra in C++
Domanda
Sto scrivendo una DLL di hook globale che deve eseguire alcuni disegni utilizzando GDI+ su una finestra in risposta a un evento.Il mio problema è che la finestra che viene disegnata continua a ridipingersi, quindi ciò che disegno viene cancellato prima che lo voglia.C'è un modo per evitare che la finestra dipinga qualcosa per tutto il tempo necessario?
Il mio gancio attualmente è a WH_CALLWNDPROC
gancio.Il disegno viene eseguito utilizzando GDI+ in risposta al messaggio WM_SIZING
.Disegno utilizzando GDI+ sui file window DC
(cioè. GetWindowDC
).Ciò che sto disegnando viene disegnato correttamente, ma viene cancellato quasi istantaneamente quando l'area client della finestra viene ridipinta.Il programma che ha creato la finestra su cui sto disegnando è Blocco note.Mentre il cursore lampeggia, ciò che ho disegnato viene cancellato.
Qualcuno conosce un modo per sospendere temporaneamente la verniciatura della finestra?
Grazie!
Soluzione
Vorrei suggerire mettere le immagini in una finestra a più livelli sovrapposti la finestra di destinazione. Che sembra essere il modo più pulito. Dopo tutto, a un certo livello del concepimento, questo è lo scopo del window manager:)
Altri suggerimenti
la risposta di tenfour è migliore secondo me, ma lui/lei Anche dovevo spiegare Come farlo, no Appena dire Che cosa fare, quindi adesso IO spieghero Come:
NOTA: Se vuoi il idea principale nella mia soluzione, puoi passare al file ultimo passaggio 9 molto qui sotto (inizia a cercarlo dal basso verso l'alto finché non lo trovi).
Scusa se ho esagerato con la risposta e ho spiegato e dettagliato troppo, ma ti prometto che se leggerai bene, capirai e sarai soddisfatto.
Passo 1: Creare nuovo WNDCLASSEX
struttura, quindi impostarla cbSize
membro alla dimensione di questa struttura, in byte, utilizzando il metodo sizeof
parola chiave e impostare lpszClassName
membro di quello che vuoi.Imposta anche il membro hbrBackground sul valore restituito di O GetStockObject
funzione da ottenere O il nero, O bianco O pennello grigio, O CreateSolidBrush
funzione per ottenere un pennello del colore che desideri.È molto importante per scegliere il colore che fanno tutti i tuoi disegni non usalo affatto!!!E non solo per il tuo piacere!
Per esempio:
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpszClassName = "myNewClassName";
HBRUSH background_brush = GetStockObject(BLACK_BRUSH);
wcex.hbrBackground = background_brush;
Passo 2: Definire una nuova funzione che è una procedura finestra e quindi creare un nuovo puntatore a funzione di essa.Quindi impostare il lpfnWndProc
membro di wcex
a quel puntatore a funzione.
Passaggio 3: Nella definizione della funzione della nuova procedura finestra, aggiungere return 0;
qual è scorso riga di codice.
Sopra cambia il parametro uMsg e almeno aggiungi il file WM_PAINT
caso.In tal caso, definire la nuova variabile di PAINTSTRUCT
, e chiama il BeginPaint
E EndPaint
funzioni.In entrambe le funzioni, fai riferimento alla variabile like &ps
, Se ps
è il nome del PAINTSTRUCT
variabile.
Dopo chiami BeginPaint
funzione, aggiungi tutte le funzioni di disegno che desideri nella finestra di destinazione, ma assicurati che il file HDC
in tutte le funzioni di disegno sono non appartengono alla finestra di destinazione, ma alla finestra in cui hai il relativo handle Primo parametro della procedura finestra, cioè hWnd
!!!Chiamata O GetDC
O GetWindowDC
funzione per recuperare l'hDC di hWnd Primo di tutti.
NOTA: Non è necessario recuperare l'hDc della finestra di destinazione!
Non dimenticare di aggiungere la seguente riga di codice: default: return DefWindowProc(hWnd, uMsg, wParam, lParam);
all'interno del blocco del switch
dichiarazione.
Passaggio 4: Registra la lezione utilizzando il file RegisterClassEx
funzione.
Per esempio: RegisterClassEx(&wcex);
Passaggio 5: Nella tua applicazione, crea new stratificato finestra utilizzando il file CreateWindowEx
funzione.È molto importante che hai impostato il Primo parametro di questa funzione a WS_EX_LAYERED
per dichiarare che la nuova finestra creata è stratificato.
Impostare il secondo parametro al nome della nuova classe registrata, ad esempio "myNewClassName"
, ignora il terzo parametro (impostatelo su NULL
), e nel il quarto parametro, impostare i seguenti flag: WS_POPUP | WS_VISIBLE
NOTA: WS_POPUP
creerà la nuova finestra senza confine.Questo è il motivo per cui ho ignorato il hIcon
membro di wcex
e anche il terzo parametro impostato su NULL
Puoi sostituire WS_POPUP
con WS_POPUPWINDOW
Invece.Il risultato sarà lo stesso, ma WS_POPUPWINDOW
anche disegna delineato rettangolo grigio, sottile con solo 1 pixel di spessore su tutta l'area client. WS_POPUP
fa non disegnare qualcosa in contrasto con WS_POPUPWINDOW
, Appena rimuove il bordo in questo modo.
Ho impostato anche il WS_VISIBLE
flag per mostrare la finestra.Se non imposti questo flag, dovrai chiamare ShowWindow
funzione dopo CreateWindowEx
, per mostrare la finestra a più livelli.
Passaggio 6: Chiamata SetLayeredWindowAttributes
funzione dopo CreateWindowEx
.Impostare il Primo parametro all'handle della nuova finestra a più livelli creata restituita dal file CreateWindowEx
funzione.
Impostare il secondo parametro per il colore di sfondo del client della tua finestra, ad es.al colore del pennello solido conservato nel background_brush
variabile dell'esempio, utilizzata per impostare il membro hbrBackground del wcex il cui tipo è WNDCLASSEX
.
Nota che è il tipo di dati di questo parametro COLORREF
, e non HBRUSH
, cioè.lo fa non accetta un pennello solido, ma semplicemente un colore!
Se conosci il colore del pennello solido che hai scelto nel file GetStockObject
funzione, allora sai come crearla COLORREF
struttura.
Se hai chiamato CreateSolidBrush
funzione invece, quindi prima di chiamarla, definisci la nuova variabile of COLORREF
struttura che memorizzerà il colore del pennello solido e il crKey in futuro.Successivamente puoi utilizzare la variabile in entrambe le funzioni: CreateSolidBrush
E SetLayeredWindowAttributes
(nel secondo parametro, cioè crKey
).
Se hai seguito il mio esempio sopra, e tu soltanto avere il background_brush
variabile il cui tipo è HBRUSH
che memorizza il pennello solido per il membro hbrBackground di wcex, e tu lo fai non avere un COLORREF appropriato per il crKey
parametro, quindi:
Per ottenere il colore del pennello solido di cui è il tipo HBRUSH
A COLORREF
struttura quindi:
È possibile utilizzare la funzione getObject per ottenere le informazioni di logbrush del Brush, il cui componente LBColor fornirà il valore del colore in colorf.Puoi usare le funzioni getRValue, getGValue e getBValue per ottenere anche i singoli componenti del colore.
La risposta di Pravin a qualcuno che doveva sapere come arrivare COLORREF
Di HBRUSH
come pennello solido.
Ecco il link per maggiori informazioni:
http://forums.codeguru.com/showthread.php?423605-Color-of-HBRUSH
Se desideri un altro modo per recuperare il file corretto COLORREF
struttura per il crKey
parametro di SetLayeredWindowAttributes
funzione, allora puoi provare la funzione GetBkColor
funzione dopo aver recuperato il file hDc
prima della nuova finestra a più livelli creata.O chiamata SelectObject
funzione invece.Impostato Primo parametro sul dc della nuova finestra a più livelli e impostare secondo parametro al hbrBackground
membro del wcex o bakcground_brush
dell'esempio sopra.
Allora basta chiamare il GetDCBrushColor
funzione per ottenere il COLORREF
struttura per il crKey
parametro del SetLayeredWindowAttributes
funzione.
Ignora il terzo parametro (impostaz bAlpha
A NULL
), e nel il quarto il parametro imposta solo il LWA_COLORKEY
bandiera.crKey è il colore di hbrBackground, l'intero client della finestra a livelli non verrà disegnato, ad eccezione degli altri pixel il cui colore non è crKey
.Ora tutto ciò che devi fare è delimitare la finestra a più livelli sul client della finestra di destinazione.
Passaggio 7: Chiamata O FindWindow
O FindWindowEx
funzione per recuperare l'handle della finestra di destinazione, se non lo hai ancora.
Passaggio 8: Scrivi il loop del messaggio (o seleziona e copia il pezzo di codice qui sotto e incollalo nel tuo):
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Passaggio 9 (ultimo): Nel ciclo di messaggi (all'interno del blocco del ciclo while) non importa dove (prima di TranslateMessage o dopo DispatchMessage o tra di loro), scrivi un altro pezzo di codice che ogni momento ha associato la finestra a più livelli al client della finestra di destinazione:
Prima di tutto creare nuova struttura POINT e imposta le sue coordinate su (0;0) (Imposta il suo x
E y
membri a 0).
Allora chiama ClientToScreen
funzione.Impostare il Primo parametro all'handle della finestra di destinazione (restituito dal file FindWindow
O FindWindowEx
funzione) e nel secondo il parametro fa riferimento alla struttura POINT creata in precedenza.
Dopo la chiamata a ClientToScreen
funzione, la struttura POINT memorizza le coordinate del bordo SUPERIORE più SINISTRO della finestra di destinazione (nelle coordinate dello schermo, misurate in unità di pixel).
Ora devi recuperare il file misurare del client della finestra di destinazione.Per farlo dopo definire nuova variabile il cui tipo è the RECT
struttura.
Allora chiama GetClientRect
funzione.Impostare il Primo parametro all'handle della finestra di destinazione e nel file secondo Il parametro fa riferimento alla variabile il cui tipo è il RECT
struttura.
Dopo la chiamata a GetClientRect
funzione, il RECT
La variabile memorizza la larghezza e l'altezza del client della finestra di destinazione, misurata in unità di pixel.IL right
il membro memorizza il larghezza, e il bottom
il membro memorizza il altezza.Ignora il left
E top
membri della variabile il cui tipo è the RECT
struttura.Non vengono utilizzati né impostati e avranno sempre il loro valore predefinito, ad es.0 solo in questo caso.
Dopo aver ottenuto la posizione del client della finestra di destinazione (la posizione e la dimensione), ora puoi associare la finestra a più livelli al client della finestra di destinazione chiamando O MoveWindow
O SetWindowPos
funzione, in questo modo:
MoveWindow(hWnd, point.x, point.y, rect.right, rect.bottom, TRUE);
//If you want, you can set the last parameter to FALSE.
//OR
SetWindowPos(hWnd, hTargetWnd, point.x, point.y, rect.right, rect.bottom, NULL);
//If you want, you can specify some flags in the last parameter.
//Where hWnd is the handle of the layered window returned from CreateWindowEx function, and hTargetWnd is the handle of the target window returned from either FindWindow or FindWindowEx function.
Se hai deciso di utilizzare il file MoveWindow
funzione, e hai problemi a non vedere i tuoi dipinti nella finestra di destinazione, perché la finestra a più livelli lo è sotto la finestra di destinazione.Dovrai cambiare la chiamata in SetWindowPos
funzione invece per risolvere questo problema allora.Nel secondo parametro ho invocato il sistema in cui voglio posizionare la finestra a strati delimitata Sopra la finestra di destinazione.Se il problema persiste, puoi modificare questo parametro e impostarlo su O HWND_TOP
O HWND_TOPMOST
bandiera.
Sono sicuro che dopo dovrebbe funzionare bene.Non dimenticare a volte di invalidare, ridisegnare o aggiornare la finestra a più livelli per aggiornare i tuoi disegni sulla finestra di destinazione.Puoi chiamare O InvalidateRect
O RedrawWindow
O UpdateWindow
funzione per farlo.
Per esempio:
POINT point;
ClientToScreen(hTargetWnd, &point);
RECT rect;
GetClientRect(hTargetWnd, &rect);
MoveWindow(hWnd, point.x, point.y, rect.right, rect.bottom, TRUE);
//OR
SetWindowPos(hWnd, hTargetWnd, point.x, point.y, rect.right, rect.bottom, NULL);
InvalidateRect(hWnd, NULL, TRUE);
//If you want, you can set the third parameter to FALSE.
(Puoi selezionare e copiare il codice di esempio qui sopra e incollarlo anche nel tuo):
Il codice di esempio può trovarsi nel loop di messaggi, significa che la finestra a più livelli verrà associata e aggiornata ogni momento, ma se lo fai non vuoi ogni momento, ma ogni volta che fai qualcosa, o ogni volta che succede qualcosa, con la finestra di destinazione, che non appartiene alla tua applicazione, come Blocco note, allora dovrai chiamare il SetWindowsHookEx
funzione.È possibile ottenere ulteriori informazioni su tale funzione sul sito MSDN e su altri siti sul Web.
Se vuoi che ciò accada in ogni momento, ma non nel ciclo dei messaggi, puoi farlo in un altro ciclo while di un altro thread, ma dovrai creare una funzione per questo e chiamare CreateThread
funzione.Ancora una volta, puoi anche ottenere informazioni su tale funzione sul sito MSDN e su altri siti sul Web
Assicurati che il ciclo while del thread termini quando termina il processo della tua applicazione O uscito O terminato.
Puoi anche provare entrambi IsWindow
O IsWindowVisible
funzione nella condizione del ciclo while per determinare se fermarsi quando la finestra a più livelli e/o la finestra di destinazione vengono attivate O distrutto O chiuso (finito, non sarà minimizzato o iconico).
No.
Invece, perché non gancio WM_PAINT
, quindi si disegna quando disegnano?
Si può provare l'invio di messaggi WM_SETREDRAW alla finestra, con wParam
set per FALSE
di sospendere la pittura, quindi impostare a TRUE
per ripristinare un comportamento normale.
Questo è piuttosto un soluzione invadente, però.