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:

  1. GetDC (hWnd);

  2. 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.

È stato utile?

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top