代码之家  ›  专栏  ›  技术社区  ›  Ian Boyd

Win32:窗口在其整个生命周期中是否具有相同的HDC?

  •  5
  • Ian Boyd  · 技术社区  · 15 年前

    我的窗口的DC是否保证永远有效?

    我试图找出控件的设备上下文(DC)的有效期。

    我知道我可以打电话:

    GetDC(hWnd);
    

    当Windows向我发送WM_PAINT消息时,我应该打电话 BeginPaint / EndPaint 要正确确认我已绘制了它,并在内部清除无效区域:

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

    但是调用BeginPaint也会在PAINTSTRUCT结构中返回一个DC。这就是我要找的DC 应该 正在画画。

    尤其是现在,在桌面构图的时代,我在BeginPaint之外获得的DC上画画是否有效?

    1. 直流电= GetDC (hWnd);

    2. 开始粉刷(&paintStruct);

    还有第三种方法,但这似乎是我开发的BorlandDelphi的一个bug。

    在期间 WM_PAINT 在处理过程中,Delphi认为wParam是DC,并继续在其上绘制。然而MSDN说WM_PAINT消息的wParam未使用。

    为什么

    我真正的目标 is to try to keep a persistent GDI+ Graphics object 针对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);
    }
    

    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就会被用于该窗口。

    注: because i want to be a good developer, and do the right thing . *

    3 回复  |  直到 5 年前
        1
  •  5
  •   Adrian McCarthy    8 年前

    当然也有例外,但一般来说,每次打电话都可能会收到不同的DC GetDC BeginPaint . 因此,您不应该尝试在DC中保存状态(如果您必须这样做以提高性能,那么您可以为一类窗口或一个特定的窗口实例创建一些特殊的DC,但听起来这并不是您真正需要或想要的。)

    WM_DISPLAYCHANGE WM_PALETTECHANGED

    您还可以获得主题更改之类的通知。这些不会改变图形模式——它们是一个更高级别的概念——因此缓存的位图应该仍然与您获得的任何DC兼容。但是如果你想在主题改变时改变位图,你可以监听 WM_THEMECHANGED

        2
  •  6
  •   Community CDub    12 年前

    CS_OWNDC

    这样做的目的是为类中的每个窗口分配唯一的设备上下文。

    编辑

    从链接的MSDN文章:

    设备上下文是一组特殊的 应用程序用于 在客户的工作区内绘图 窗户。系统需要一个设备 显示器上每个窗口的上下文 系统存储并处理该设备

    如果没有设置设备上下文样式 明确给定,系统假定 从上下文池中检索 由系统维护。这样 案例中,每个窗口都必须检索和 在之前初始化设备上下文

    窗口中,应用程序可以指定 窗口类的CS_OWNDC样式。 此类样式将引导系统 是指分配一个唯一的设备 类中每个窗口的上下文。 应用程序只需检索 随后的绘画。

    Windows 95/98/Me:虽然 小心,因为每个设备都有上下文 使用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;
    }
    

    如果其他一切都失败了 reconstruct CachedBitmap。

    朝着更好的解决方案迈出了一步。

    编辑

    在使用CS_OWNDC标志进行屏幕分辨率/位深度变化测试期间,示例程序似乎保留了相同的DC,但是,当该标志被移除时,DC不同(窗口7 64位Ultimate)( 应该

    编辑2

    本例不调用getUpdate来检查在WM_绘制期间是否需要绘制窗口。这是一个错误。

        3
  •  2
  •   Chris Becke    15 年前

    完成后只需释放DC(或EndPaint)。在Windows3.1时代,设备上下文是一种有限的或非常昂贵的系统资源,因此鼓励应用程序永远不要保留它们,而是从GetDC缓存中检索它们。现在,在创建窗口时创建dc并在窗口的生命周期中缓存它是完全可以接受的。

    唯一的“问题”是,在处理 WM_PAINT ,BeginPaint返回的dc将被剪裁到无效的rect,而保存的dc将不会被剪裁。


    然而,我不明白您试图用gdiplus实现什么。通常,如果一个对象是。。。长时间选入dc后,该dc是内存dc,而不是窗口dc。


    每次调用GetDC时,您都会得到一个新的HDC,它表示一个具有自己状态的不同设备上下文。因此,在一个DC上设置的对象、背景颜色、文本模式等不会影响通过对GetDC或BeginPaint的不同调用检索到的另一个DC的状态。

    系统无法随机使客户端检索到的HDC无效,并且实际上在后台做了大量工作,以确保在显示模式切换之前检索到的HDC继续工作。即使更改位深度,这在技术上使dc不兼容,也不会以任何方式阻止应用程序继续使用hdc到blit。

    这就是说,明智的做法是至少观察WM_DISPLAYCHANGE,释放任何缓存的DC和设备位图,然后重新创建它们。