Win32: Ha una finestra ha la stessa HDC per tutta la sua vita?
-
20-09-2019 - |
Domanda
Sono autorizzato a utilizzare un esterno DC di un ciclo di verniciatura? È DC di mia finestra garantito per essere valida per sempre?
Sto cercando di capire quanto tempo il mio controllo Device Context (DC) è valido.
So che posso chiamare:
GetDC(hWnd);
per ottenere il contesto di periferica della finestra di mio controllo, ma è che ha permesso?
Quando Windows mi manda un messaggio WM_PAINT, dovrei chiamare BeginPaint / EndPaint di riconoscere correttamente che Ho dipinto, e al suo interno chiara la regione non valida:
BeginPaint(hWnd, {out}paintStruct);
try
//Do my painting
finally
EndPaint(hWnd, paintStruct);
end;
Ma chiamando BeginPaint mi restituisce anche un DC all'interno della struttura PAINTSTRUCT. Questa è la DC che i dovrebbe da pittura su.
Non riesco a trovare nulla nella documentazione che dice che la DC restituito da BeginPaint () è la stessa DC che avrei ricevuto da GetDC ().
Soprattutto ora, nei giorni di Desktop Composition, è valida per dipingere su una CC che ottengo al di fuori di BeginPaint?
Sembra che ci siano 2 modi posso ottenere una corrente continua a dipingere durante un ciclo di verniciatura:
-
GetDC (hWnd);
-
BeginPaint (& PAINTSTRUCT);
C'è un terzo modo, ma sembra essere un bug con il Borland Delphi che sviluppo con.
WM_PAINT elaborazione, Delphi crede che il wParam è una corrente continua, e procede a dipingere su di esso. Considerando che il MSDN dice che il wParam di un messaggio WM_PAINT è inutilizzato.
L'Perché
Il mio vero obiettivo è quello di cercare di mantenere un GDI + persistente oggetto Graphics contro un HDC, in modo che possa utilizzare alcune caratteristiche più performanti di GDI + che dipendono da avere una CC persistente.
Nel corso della gestione dei messaggi WM_PAINT voglio disegnare un'immagine GDI + per la tela. La seguente versione Nieve è molto lento:
WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(m_hwnd, ps);
Graphics g = new Graphics(ps.hdc);
g.DrawImage(m_someBitmap, 0, 0);
g.Destroy();
EndPaint(h_hwnd, ps);
}
GDI contiene una bitmap l'esecuzione più veloce, un CachedBitmap. Ma il suo utilizzo senza pensare dà alcun beneficio delle prestazioni:
WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(m_hwnd, ps);
Graphics g = new Graphics(ps.hdc);
CachedBitmap bm = new CachedBitmap(m_someBitmap, g);
g.DrawCachedBitmap(m_bm, 0, 0);
bm.Destroy();
g.Destroy();
EndPaint(h_hwnd, ps);
}
Il guadagno di prestazioni viene dalla creazione del CachedBitmap una volta, quindi il programma di inizializzazione:
m_graphics = new Graphics(GetDC(m_hwnd));
m_cachedBitmap = new CachedBitmap(b_someBitmap, m_graphcis);
E ora sul ciclo di verniciatura:
WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(m_hwnd, ps);
m_graphics.DrawCachedBitmap(m_cachedBitmap, 0, 0);
EndPaint(h_hwnd, ps);
}
Eccetto che ora Sono fiducioso che la DC I ottenuto dopo il programma initializtion sarà la stessa DC per la mia finestra finché l'applicazione è in esecuzione. Ciò significa che sopravvive attraverso:
-
interruttori
- utente veloce
- composizione abilitato / disabilitato
- commutazione tema
- tema disabilitazione
Non trovo nulla in MSDN che garantisce che la stessa DC sarà utilizzato per una particolare finestra per tutto il tempo esiste la finestra.
Nota: non sto usando doppio buffering, perché voglio essere un buon sviluppatore, e fare la cosa giusta . A volte questo significa che doppio buffering è male.
Soluzione
Ci sono delle eccezioni, ma in generale, si può ottenere un diverso DC ogni volta che si chiama o GetDC
BeginPaint
. Quindi non si dovrebbe cercare di salvare lo stato in DC. (Se si deve fare questo per le prestazioni, ci sono i controller speciali che si possono creare per una classe di finestre o una particolare istanza finestra, ma non suona come questo è quello che si ha realmente bisogno o volete.)
La maggior parte del tempo, tuttavia, i PVS saranno compatibili. Essi rappresenteranno la stessa modalità grafica, in modo che il bitmap compatibile dovrebbe funzionare, anche se si ottiene un diverso DC.
Ci sono messaggi di Windows che indicano quando cambia modalità grafica, come WM_DISPLAYCHANGE
e WM_PALETTECHANGED
. È possibile ascoltare per questi, e ricreare il bitmap memorizzata nella cache. Dal momento che questi sono eventi rari, non ti devi preoccupare l'impatto sulle prestazioni di ricreare la tua bitmap memorizzata nella cache in quel punto.
È inoltre possibile ottenere le notifiche per le cose come le modifiche a tema. Coloro che non cambiano la modalità grafica - they're un concetto più alto livello - in modo che il bitmap memorizzata nella cache dovrebbe essere ancora compatibile con qualsiasi DC si ottiene. Ma se si vuole cambiare bitmap quando il tema cambia, è possibile ascoltare per WM_THEMECHANGED
pure.
Altri suggerimenti
L'unico modo che conosco che può (o non può) fare quello che state cercando è quello di creare la finestra con il CS_OWNDC stile di classe.
Ciò che fa è assegna un contesto di periferica univoco per ogni finestra della classe.
Modifica
Dalla articolo di MSDN collegato:
Un contesto di dispositivo è un insieme speciale di valori utilizzati dalle applicazioni per disegno nell'area client della loro finestre. Il sistema richiede un dispositivo contesto per ogni finestra sul display ma consente una certa flessibilità nel modo in cui il sistema immagazzina e tratta che dispositivi contesto.
Se nessuno stile dispositivo al contesto è esplicitamente dato, il sistema presuppone ogni finestra utilizza un contesto di periferica recuperato da un pool di contesti gestito dal sistema. in tale casi, ogni finestra deve recuperare e inizializzare il contesto di periferica prima pittura e liberarla dopo la verniciatura.
Per evitare il recupero di un contesto di periferica ogni volta che ha bisogno di dipingere all'interno di un finestra, un'applicazione può specificare il stile CS_OWNDC per la classe finestra. Questo stile di classe dirige il sistema creare un contesto di periferica privata - che è, per allocare un dispositivo unico contesto per ogni finestra della classe. L'applicazione deve recuperare solo il contesto una volta e poi utilizzarlo per tutti successiva verniciatura.
Windows 95/98 / Me: Anche se il stile CS_OWNDC è conveniente, usarlo attenzione, perché ogni contesto di dispositivo utilizza una parte significativa di 64K GDI mucchio.
Forse questo esempio illustrerà l'utilizzo di CS_OWNDC meglio:
#include <windows.h>
static TCHAR ClassName[] = TEXT("BitmapWindow");
static TCHAR WindowTitle[] = TEXT("Bitmap Window");
HDC m_hDC;
HWND m_hWnd;
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static PAINTSTRUCT ps;
switch (msg)
{
case WM_PAINT:
{
BeginPaint(hWnd, &ps);
if (ps.hdc == m_hDC)
MessageBox(NULL, L"ps.hdc == m_hDC", WindowTitle, MB_OK);
else
MessageBox(NULL, L"ps.hdc != m_hDC", WindowTitle, MB_OK);
if (ps.hdc == GetDC(hWnd))
MessageBox(NULL, L"ps.hdc == GetDC(hWnd)", WindowTitle, MB_OK);
else
MessageBox(NULL, L"ps.hdc != GetDC(hWnd)", WindowTitle, MB_OK);
RECT r;
SetRect(&r, 10, 10, 50, 50);
FillRect(m_hDC, &r, (HBRUSH) GetStockObject( BLACK_BRUSH ));
EndPaint(hWnd, &ps);
return 0;
}
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
WNDCLASSEX wcex;
wcex.cbClsExtra = 0;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.cbWndExtra = 0;
wcex.hbrBackground = (HBRUSH) GetStockObject( WHITE_BRUSH );
wcex.hCursor = LoadCursor( NULL, IDC_ARROW );
wcex.hIcon = LoadIcon( NULL, IDI_APPLICATION );
wcex.hIconSm = NULL;
wcex.hInstance = hInstance;
wcex.lpfnWndProc = WndProc;
wcex.lpszClassName = ClassName;
wcex.lpszMenuName = NULL;
wcex.style = CS_OWNDC;
if (!RegisterClassEx(&wcex))
return 0;
DWORD dwExStyle = 0;
DWORD dwStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE;
m_hWnd = CreateWindowEx(dwExStyle, ClassName, WindowTitle, dwStyle, 0, 0, 300, 300, NULL, NULL, hInstance, NULL);
if (!m_hWnd)
return 0;
m_hDC = GetDC(m_hWnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
La bandiera CS_OWNDC è non per essere confuso con la bandiera CS_CLASSDC che:
contesto un dispositivo Alloca per essere condiviso da tutte le finestre della classe. Poiché classi di finestra sono processo specifico, è possibile che più thread di un'applicazione di creare una finestra della stessa classe. E 'anche possibile che i fili di tentano di utilizzare il contesto di periferica contemporaneamente. Quando questo accade, il sistema consente un solo filo per terminare correttamente la sua operazione di imbutitura.
Se tutto questo non basta ricostruire il CachedBitmap.
Quando si costruisce un oggetto CachedBitmap, è necessario passare l'indirizzo di un oggetto Graphics al costruttore. Se lo schermo associata a tale oggetto Graphics ha cambiato la sua profondità di bit dopo la bitmap nella cache è costruito, allora il metodo DrawCachedBitmap avrà esito negativo, e si dovrebbe ricostruire il bitmap memorizzata nella cache. In alternativa, è possibile collegare il messaggio di notifica di modifica del display e ricostruire la bitmap nella cache in quel momento.
Non sto dicendo che CS_OWNDC è la soluzione perfetta, ma è un passo verso una soluzione migliore.
Modifica
Il programma di esempio sembrava mantenere lo stesso DC durante la risoluzione dello schermo / profondità di bit test cambiamento con la bandiera CS_OWNDC, tuttavia, quando quella bandiera è stato rimosso, la DC erano diversi (Windows 7 64-bit Ultimate) ( dovrebbe funzionano allo stesso modo rispetto alle versioni del sistema operativo differn ... anche se non sarebbe male provare).
Edit2
In questo esempio non chiama GetUpdateRect per verificare se la finestra deve essere dipinto durante la WM_PAINT. Questo è un errore.
È possibile disegnare su qualunque finestra dc ti piace. Sono entrambi validi. Una finestra non ha un solo dc che può rappresentare in un momento. Così ogni volta che si chiama GetDC - e BeginPaint internamente fa così, si aprirà una nuova, dc unico, che rappresenta comunque la stessa area di visualizzazione. Basta ReleaseDC (o EndPaint) quando hai finito con loro. Nei giorni di Windows 3.1 contesti di periferica sono stati una risorsa di sistema limitato, o molto costoso, quindi le applicazioni sono stati incoraggiati a non tenere su di loro, ma di recuperarli dalla cache GetDC. al giorno d'oggi il suo perfettamente accettabile per creare una corrente continua al momento della creazione della finestra, e memorizzare nella cache per la vita della finestra.
L'unico "problema" è, durante la manipolazione WM_PAINT
, il dc restituito da BeginPaint sarà tagliata al rect non valido e quella salvata non sarà.
Non però a capire che cosa si sta tentando di ottenere con GDIPlus. Di solito, se un oggetto è ... selezionato in una corrente continua per un lungo periodo di tempo, che è un dc dc memoria, non una finestra cc.
Ogni volta che si chiama GetDC avrete una nuova HDC che rappresenta un contesto di periferica distinta con il suo proprio stato. Così, oggetti, colori di sfondo, le modalità di testo, ecc impostati su un DC non sarà effetto quello stato di un altro DC recuperato da una chiamata diversa da GetDC o BeginPaint.
Il sistema non può invalidare in modo casuale HDCS recuperati dal cliente, e in realtà fa un sacco di lavoro in background per garantire che HDCS recuperati prima di un interruttore di modalità di visualizzazione, continuano a funzionare. Anche cambiando la profondità di bit, che rende tecnicamente del dc incompatibili, non sarà, in alcun modo, impedire che un'applicazione di continuare ad utilizzare un HDC per blit.
Detto questo, è bene guardare almeno per WM_DISPLAYCHANGE, rilasciare qualsiasi DC memorizzati nella cache e bitmap dispositivo e ricreare.