Pergunta

Posso usar um DC fora de um ciclo de pintura? O DC da minha janela é garantido para ser válido para sempre?

Estou tentando descobrir quanto tempo o contexto do dispositivo do meu controle (DC) é válido.

Eu sei que posso ligar:

GetDC(hWnd);

Para obter o contexto do dispositivo da janela do meu controle, mas isso é permitido?

Quando o Windows me envia uma mensagem wm_paint, eu devo ligar Beginpaint/Paint reconhecer adequadamente que eu o pintei e limpar internamente a região inválida:

BeginPaint(hWnd, {out}paintStruct);
try
   //Do my painting
finally
   EndPaint(hWnd, paintStruct);
end;

Mas chamar Beginpaint também me devolve um DC dentro da estrutura da pintura. Este é o DC que eu deve estar pintando.

Não consigo encontrar nada na documentação que diz que o DC retornado por BeginPaint () é o mesmo DC que eu receberia do getDC ().

Especialmente agora, nos dias de composição da área de trabalho, é válido pintar em um DC que eu obtive fora do começo?

Parece haver duas maneiras pelas quais posso obter um DC para pintar durante um ciclo de pintura:

  1. dc = GetDC(hwnd);

  2. Beginpaint (e pintura);

Há uma 3ª maneira, mas parece ser um bug do Borland Delphi com o qual desenvolvo.

Durante Wm_paint Processamento, Delphi acredita que o WPARAM é um DC e passa a pintar nele. Enquanto o MSDN diz que o WPARAM de uma mensagem wm_paint não é utilizada.

O porquê

Meu verdadeiro objetivo é tentar manter um objeto gráfico GDI+ persistente Contra um HDC, para que eu possa usar alguns recursos de melhor desempenho do GDI+ que dependem de ter um DC persistente.

Durante o manuseio de mensagens wm_paint, quero desenhar uma imagem GDI+ para a tela. A versão NIEVE a seguir é muito lenta:

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);
}

O GDI contém um bitmap de desempenho mais rápido, um cachedbitmap. Mas usá -lo sem pensar não dá nenhum benefício de desempenho:

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);
}

O ganho de desempenho vem da criação do CachedBitmap uma vez, portanto, na inicialização do programa:

m_graphics = new Graphics(GetDC(m_hwnd));
m_cachedBitmap = new CachedBitmap(b_someBitmap, m_graphcis);

E agora no ciclo de pintura:

WM_PAINT:
{
   PAINTSTRUCT ps;
   BeginPaint(m_hwnd, ps);
   m_graphics.DrawCachedBitmap(m_cachedBitmap, 0, 0);
   EndPaint(h_hwnd, ps);
}        

Exceto agora, confio que o DC que obtive após a inicialização do programa será o mesmo CC para minha janela, desde que o aplicativo esteja em execução. Isso significa que ele sobrevive através de:

  • Switches de usuário rápido
  • composição habilitada/desativada
  • troca de temas
  • tema incapacitante

Não encontro nada no MSDN que garante que o mesmo DC seja usado para uma janela específica enquanto a janela existir.

Observação: Eu não estou usando buffer duplo, Porque eu quero ser um bom desenvolvedor e fazer a coisa certa. Às vezes isso significa que você buffer duplo é ruim.

Foi útil?

Solução

Existem exceções, mas em geral, você pode obter um DC diferente cada vez que ligar GetDC ou BeginPaint. Portanto, você não deve tentar salvar o estado no DC. (Se você deve fazer isso para desempenho, existem DCs especiais que você pode criar para uma classe de Windows ou uma instância de janela específica, mas não parece que é isso que você realmente precisa ou deseja.)

Na maioria das vezes, no entanto, esses DCs serão compatíveis. Eles representarão o mesmo modo gráfico, portanto, seu bitmap compatível deve funcionar, mesmo se você receber um DC diferente.

Existem mensagens do Windows que informam quando o modo gráfico muda, como WM_DISPLAYCHANGE e WM_PALETTECHANGED. Você pode ouvir isso e recriar seu bitmap em cache. Como esses são eventos raros, você não precisará se preocupar com o impacto do desempenho de recriar seu bitmap em cache naquele momento.

Você também pode obter notificações para coisas como mudanças de temas. Esses não alteram o modo gráfico-eles são um conceito de nível superior-, portanto, seu bitmap em cache ainda deve ser compatível com qualquer DC que você obtém. Mas se você quiser mudar o bitmap quando o tema mudar, você pode ouvir para WM_THEMECHANGED também.

Outras dicas

A única maneira que eu sei disso pode (ou não) fazer o que você está procurando é criar a janela com o CS_OWNDC estilo de classe.

O que isso faz é alocar um contexto de dispositivo exclusivo para cada janela da classe.

Editar

Do artigo do MSDN vinculado:

Um contexto de dispositivo é um conjunto especial de valores que os aplicativos usam para desenhar na área do cliente de suas janelas. O sistema requer um contexto de dispositivo para cada janela na tela, mas permite alguma flexibilidade na maneira como o sistema armazena e trata esse contexto de dispositivo.

Se nenhum estilo de contexto de dispositivo for explicitamente fornecido, o sistema pressupõe que cada janela use um contexto de dispositivo recuperado de um pool de contextos mantidos pelo sistema. Nesses casos, cada janela deve recuperar e inicializar o contexto do dispositivo antes de pintá -lo e libertá -lo após a pintura.

Para evitar a recuperação de um contexto de dispositivo cada vez que precisar pintar dentro de uma janela, um aplicativo pode especificar o estilo CS_OWNDC para a classe Window. Esse estilo de classe instrui o sistema a criar um contexto de dispositivo privado - ou seja, para alocar um contexto de dispositivo exclusivo para cada janela da classe. O aplicativo precisa apenas recuperar o contexto uma vez e depois usá -lo para toda a pintura subsequente.

Windows 95/98/me: Embora o estilo CS_OWNDC seja conveniente, use -o com cuidado, porque cada contexto de dispositivo usa uma parte significativa da pilha GDI de 64k.

Talvez este exemplo ilustre melhor o uso de cs_owndc:

#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;
}

A bandeira CS_OWNDC é não Para ser confundido com o sinalizador CS_CLASSDC que:

Aloca um contexto de dispositivo a ser compartilhado por todas as janelas da classe. Como as classes de janelas são específicas do processo, é possível que vários threads de um aplicativo criem uma janela da mesma classe. Também é possível que os threads tentem usar o contexto do dispositivo simultaneamente. Quando isso acontece, o sistema permite que apenas um thread conclua sua operação de desenho com sucesso.

Se tudo mais falhar apenas reconstruir o cachedbitmap.

Quando você constrói um objeto CachedBitmap, você deve passar o endereço de um objeto gráfico para o construtor. Se a tela associada a esse objeto gráfica tiver sua profundidade de bits alterada após a construção do bitmap em cache, o método drawcachedbitmap falhará e você deve reconstruir o bitmap em cache. Como alternativa, você pode conectar a mensagem de notificação de alteração da tela e reconstruir o bitmap em cache naquele momento.

Não estou dizendo que CS_OWNDC é a solução perfeita, mas é Um passo em direção a uma solução melhor.

Editar

O programa de amostra parecia manter o mesmo DC durante o teste de resolução da tela / mudança de profundidade com o sinalizador CS_OWNDC;deve Trabalhe da mesma forma versões diferentes ... embora não doeu testar).

Edit2

Este exemplo não chama GetUpDaterect para verificar se a janela precisa ser pintada durante o WM_Paint. Isso é um erro.

Você pode atrair para qualquer janela que a DC lhe agarre. Ambos são válidos. Uma janela não possui apenas um DC que pode representá -lo por vez. Portanto, cada vez que você chama GetDC - e o Beginpaint o faz internamente, você obterá um novo e único DC, que, no entanto, representa a mesma área de exibição. Acabei de lançar (ou endpaint) quando terminar com eles. Nos dias do Windows 3.1, os contextos dos dispositivos eram um recurso limitado ou muito caro do sistema, portanto, os aplicativos foram incentivados a nunca se apegar a eles, mas para recuperá -los do cache GETDC. Atualmente, é perfeitamente aceitável criar um DC na criação de janelas e cache -a para a vida útil da janela.

O único "problema" é, ao manusear WM_PAINT, o DC retornado por Beginpaint será preso ao Rect inválido, e o salvo não o fará.


No entanto, não entendo o que você está tentando alcançar com gdiplus. Geralmente, se um objeto for ... selecionado em um DC por um longo período de tempo, que DC é um CC de memória, não uma janela DC.


Cada vez que o GetDC é chamado, você receberá um novo HDC representando um contexto de dispositivo distinto com seu próprio estado. Portanto, objetos, cores de fundo, modos de texto etc. definidos em um DC não afetarão esse estado de outro DC recuperado por uma chamada diferente para GetDC ou BEGING.

O sistema não pode invalidar aleatoriamente os HDCs recuperados pelo cliente e, na verdade, faz muito trabalho em segundo plano para garantir que os HDCs recuperados antes de uma chave de modo de exibição continuem funcionando. Mesmo mudar a profundidade do bit, que tecnicamente torna o incompatível do DC, de forma alguma impedirá que um aplicativo continue a usar um HDC para escapar.

Dito isto, é aconselhável assistir pelo menos para WM_DISPLAYCHANGE, lançar quaisquer DCs em cache e bitmaps de dispositivo e recriá -los.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top