Win32: окно имеет тот же HDC в течение своего всего времени жизни?

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

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

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

GetDC(hWnd);

для получения контекста устройства окна моего управления но это позволяется?

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

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

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

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

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

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

  1. dc = GetDC (hWnd);

  2. BeginPaint (&paintStruct);

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

Во время обработки WM_PAINT Delphi полагает, что wParam является DC и продолжает подрисовывать его. Принимая во внимание, что в MSDN говорится, что wParam сообщения WM_PAINT не использован.

Почему

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

Во время обработки сообщений 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, который я получил после программы initializtion, будет тем же DC для моего окна, пока приложение работает. Это означает, что выживает через:

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

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

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

5
задан Ian Boyd 11 December 2019 в 12:41
поделиться

3 ответа

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

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

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

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

5
ответ дан 13 December 2019 в 05:35
поделиться

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

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

Edit

From the linked MSDN article:

A device context is a special set of значения, для которых применяются приложения рисование в клиентской части их окна. Для работы системы требуется устройство контекст для каждого окна на дисплее но допускает некоторую гибкость в том, как система хранит и обрабатывает это устройство контекст.

Если нет стиля "контекст устройства". явно заданный, система предполагает каждое окно использует контекст устройства извлечённый из пула контекстов поддерживаемый системой. При этом ящики, каждое окно должно быть извлечено и инициализировать контекст устройства перед рисовать и освобождать его после рисования.

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

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, который:

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

Если все остальное не удается, просто восстановите CachedBitmap.

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

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

Edit

Пример программы вроде бы сохранил тот же DC при тестировании разрешения экрана / изменения битовой глубины с флагом CS_OWNDC, однако, когда этот флаг был снят, DC были другими (Window 7 64-bit Ultimate)(should should work same over differn OS versions... хотя тестировать это не помешало бы).

Edit2

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

5
ответ дан 13 December 2019 в 05:35
поделиться

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

insertObject: atDepartingObjectIndex:

на ArrayController?

-121--4746548-

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

Единственная «проблема» заключается в том, что при обработке WM _ PAINT возвращенный BeginPaint DC будет обрезан до недопустимой rect, а сохраненный - нет.


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


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

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

При этом целесообразно следить за WM_DISPLAYCHANGE хотя БЫ, освобождать любые кэшированные контроллеры домена и растровые изображения устройств и воссоздавать их.

2
ответ дан 13 December 2019 в 05:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: