Win32:Имеет ли окно один и тот же HDC на протяжении всего срока службы?

StackOverflow https://stackoverflow.com/questions/2074294

Вопрос

Могу ли я использовать DC вне цикла рисования?Гарантируется ли, что DC моего окна будет действителен вечно?

я пытаюсь выяснить, как долго действует контекст устройства моего элемента управления (DC).

я знаю, что могу позвонить:

GetDC(hWnd);

чтобы получить контекст устройства в окне моего элемента управления, но разрешено ли это?

Когда Windows отправляет мне сообщение WM_PAINT, я должен позвонить Начинайте рисовать/Конечная краска чтобы должным образом подтвердить, что я его нарисовал, и внутренне очистить недопустимую область:

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

Но вызов BeginPaint также возвращает мне DC внутри структуры PAINTSTRUCT.Это тот округ Колумбия, который я следует продолжайте рисовать дальше.

я не могу найти в документации ничего, что говорило бы о том, что DC, возвращаемый BeginPaint(), является тем же DC, который я получил бы от GetDC().

Особенно сейчас, во времена настольной композиции, допустимо ли рисовать на DC, который я получаю за пределами BeginPaint?

Кажется, есть 2 способа, которыми я могу заставить DC рисовать во время цикла рисования:

  1. постоянный ток = Получить DC(hWnd);

  2. BeginPaint(иpaintStruct);

Есть 3-й способ, но, похоже, это ошибка в Borland Delphi, с помощью которой я разрабатываю.

Во время WM_PAINT_ПРИМЕЧАНИЯ обрабатывая, Delphi считает, что wParam является DC, и продолжает рисовать на нем.Принимая во внимание, что в MSDN указано, что wParam сообщения WM_PAINT не используется.

Вопрос " Почему "

Моя настоящая цель заключается в том, чтобы попытаться сохранить постоянный графический объект GDI + против HDC, чтобы я мог использовать некоторые более производительные функции GDI +, которые зависят от наличия постоянного постоянного тока.

Во время обработки сообщения WM_PAINT я хочу нарисовать изображение GDI + на холсте.Следующая версия nieve работает очень медленно:

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 содержит более быстродействующее растровое изображение - CachedBitmap.Но использование его бездумно не дает никакого преимущества в производительности:

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

Прирост производительности достигается за счет создания CachedBitmap один раз, поэтому при инициализации программы:

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

А теперь перейдем к циклу покраски:

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

За исключением того, что теперь я верю, что DC, который я получил после инициализации программы, будет тем же DC для моего окна, пока приложение запущено.Это означает, что он выживает благодаря:

  • быстрые пользовательские переключения
  • композиция включена / отключена
  • переключение темы
  • отключение темы

я не нахожу в MSDN ничего, что гарантировало бы, что один и тот же DC будет использоваться для определенного окна до тех пор, пока оно существует.

Примечание: я не использую двойную буферизацию, потому что я хочу быть хорошим разработчиком и поступать правильно.Иногда это означает, что у вас плохая двойная буферизация.

Это было полезно?

Решение

Есть исключения, но в целом вы можете получать другой DC каждый раз, когда вызываете GetDC или BeginPaint.Таким образом, вам не следует пытаться сохранить состояние в DC.(Если вам необходимо сделать это для повышения производительности, существуют специальные контроллеры домена, которые вы можете создать для класса windows или конкретного экземпляра window, но не похоже, что это то, что вам действительно нужно.)

Однако в большинстве случаев эти контроллеры домена будут совместимы.Они будут представлять один и тот же графический режим, поэтому ваше совместимое растровое изображение должно работать, даже если вы получаете другой DC.

Есть сообщения Windows, которые сообщают вам, когда изменяется графический режим, например WM_DISPLAYCHANGE и WM_PALETTECHANGED.Вы можете прослушать их и воссоздать свое кэшированное растровое изображение.Поскольку это редкие события, вам не придется беспокоиться о влиянии на производительность повторного создания вашего кэшированного растрового изображения в этот момент.

Вы также можете получать уведомления о таких вещах, как смена темы.Они не меняют графический режим - это концепция более высокого уровня, - поэтому ваше кэшированное растровое изображение все равно должно быть совместимо с любым полученным вами DC.Но если вы хотите изменить растровое изображение при смене темы, вы можете прослушать WM_THEMECHANGED также.

Другие советы

Единственный известный мне способ, который может (или не может) сделать то, что вы ищете, - это создать окно с CS_OWNDC ( CS_OWNDC ) классный стиль.

Что это делает, так это выделяет уникальный контекст устройства для каждого окна в классе.

Редактировать

Из связанной статьи MSDN:

Контекст устройства - это специальный набор значений, которые приложения используют для рисования в клиентской области своих windows.Системе требуется устройство контекст для каждого окна на дисплее но это обеспечивает некоторую гибкость в том, как система сохраняет это устройство и обрабатывает его контекст.

Если стиль контекста устройства не задан явно, система предполагает, что каждое окно использует контекст устройства извлекается из пула контекстов поддерживается системой.В таких случаях каждое окно должно извлекать и инициализировать контекст устройства перед рисованием и освобождать его после рисования.

Чтобы избежать извлечения контекста устройства каждый раз, когда требуется выполнить рисование внутри окна, приложение может указать Стиль CS_OWNDC для класса window.Этот стиль класса направляет систему на создание контекста частного устройства, то есть выделение уникального устройства контекста для каждого окна в классе.Приложению нужно только получить контекст один раз, а затем использовать его для всего последующего рисования.

Windows 95/98/Me:Хотя стиль CS_OWNDC удобен, используйте его осторожно, потому что контекст каждого устройства использует значительную часть 64K GDI куча.

Возможно, этот пример лучше проиллюстрирует использование 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;
}

Флаг CS_OWNDC равен не путать с флагом CS_CLASSDC , который:

Выделяет один контекст устройства для совместного использования всеми окнами в классе.Поскольку классы окон зависят от процесса, несколько потоков приложения могут создавать окна одного и того же класса.Также потоки могут пытаться использовать контекст устройства одновременно.Когда это происходит, система разрешает только одному потоку успешно завершить операцию рисования.

Если все остальное терпит неудачу, просто реконструировать кэшированная карта BITMAP.

Когда вы создаете объект CachedBitmap, вы должны передать адрес графического объекта конструктору.Если разрядность экрана, связанного с этим графическим объектом, изменилась после создания кэшированного растрового изображения, то метод DrawCachedBitmap завершится ошибкой, и вам следует восстановить кэшированное растровое изображение.В качестве альтернативы вы можете подключить уведомление об изменении отображения и восстановить кэшированное растровое изображение в это время.

Я не говорю, что CS_OWNDC - идеальное решение, но это является один шаг к лучшему решению.

Редактировать

Пример программы, казалось, сохранил тот же DC во время тестирования изменения разрешения экрана / разрядности с флагом CS_OWNDC, однако, когда этот флаг был удален, DC были другими (Windows 7 64-bit Ultimate) (следует работайте одинаково с разными версиями ОС...хотя проверить бы не помешало).

Редактировать 2

В этом примере не вызывается GetUpdateRect для проверки, нужно ли закрашивать окно во время WM_PAINT.Это ошибка.

Вы можете рисовать в любом окне dc, которое вам понравится.Они оба действительны.Окно не имеет только одного постоянного тока, который может представлять его одновременно.Таким образом, каждый раз, когда вы вызываете GetDC - и BeginPaint внутренне делает это, вы будете получать новый, уникальный dc, который, тем не менее, представляет ту же область отображения.Просто ReleaseDC (или EndPaint), когда вы закончите с ними.Во времена Windows 3.1 контексты устройств были ограниченным или очень дорогим системным ресурсом, поэтому приложениям рекомендовалось никогда не сохранять их, а извлекать из кэша GetDC.в настоящее время вполне приемлемо создавать dc при создании окна и кэшировать его на протяжении всего срока службы окна.

Единственная "проблема" заключается в том, что при обращении WM_PAINT, dc, возвращенный BeginPaint, будет обрезан до недопустимого rect, а сохраненный - нет.


Однако я не понимаю, чего вы пытаетесь достичь с помощью gdiplus.Обычно, если объект является ...выбранный в постоянный ток на длительный период времени, этот постоянный ток является постоянным током памяти, а не постоянным током окна.


Каждый раз, когда вызывается GetDC, вы будете получать новый HDC, представляющий отдельный контекст устройства с его собственным состоянием.Итак, объекты, цвета фона, текстовые режимы и т.д.установка на один DC НЕ повлияет на состояние другого DC, полученного другим вызовом GetDC или BeginPaint.

Система не может случайным образом аннулировать HDCS, полученные клиентом, и фактически выполняет большую работу в фоновом режиме, чтобы гарантировать, что HDCS, полученные до переключения режима отображения, продолжают функционировать.Даже изменение разрядности, которое технически делает dc несовместимыми, никоим образом не помешает приложению продолжать использовать hdc для blit.

Тем не менее, разумно следить по крайней мере за WM_DISPLAYCHANGE, освобождать все кэшированные контроллеры домена и растровые изображения устройства и воссоздавать их.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top