Win32: Does a window have the same HDC for its entire lifetime?
我可以在涂漆周期之外使用DC吗?
我的窗户的DC可以保证永远有效吗?
我试图找出控件的设备上下文(DC)有效期。
我知道我可以打电话:
1 | GetDC(hWnd); |
获取控件窗口的设备上下文,但是是否允许?
Windows向我发送WM_PAINT消息时,我应该调用BeginPaint / EndPaint来正确确认我已经绘制了它,并在内部清除无效区域:
1 2 3 4 5 6 | BeginPaint(hWnd, {out}paintStruct); try //Do my painting finally EndPaint(hWnd, paintStruct); end; |
但是调用BeginPaint也会在PAINTSTRUCT结构内部返回DC。这是我应该绘画的DC。
我在文档中找不到任何内容,表明BeginPaint()返回的DC与我从GetDC()获得的DC相同。
尤其是现在,在"桌面合成"时代,在我从BeginPaint之外获得的DC上绘画是否有效?
在绘画周期中,似乎有两种方法可以使DC进行绘画:
dc = GetDC(hWnd);
BeginPaint(
我知道的唯一可能会(或可能不会)完成您正在寻找的工作的方法是使用CS_OWNDC类样式创建窗口。
这样做是为类中的每个窗口分配唯一的设备上下文。
编辑
来自链接的MSDN文章:
A device context is a special set of
values that applications use for
drawing in the client area of their
windows. The system requires a device
context for each window on the display
but allows some flexibility in how the
system stores and treats that device
context.If no device-context style is
explicitly given, the system assumes
each window uses a device context
retrieved from a pool of contexts
maintained by the system. In such
cases, each window must retrieve and
initialize the device context before
painting and free it after painting.To avoid retrieving a device context
each time it needs to paint inside a
window, an application can specify the
CS_OWNDC style for the window class.
This class style directs the system to
create a private device context a€" that
is, to allocate a unique device
context for each window in the class.
The application need only retrieve the
context once and then use it for all
subsequent painting.Windows 95/98/Me: Although the
CS_OWNDC style is convenient, use it
carefully, because each device context
uses a significant portion of 64K GDI
heap.
也许此示例将更好地说明CS_OWNDC的使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | #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标志混淆:
Allocates one device context to be shared by all windows in the class. Because window classes are process specific, it is possible for multiple threads of an application to create a window of the same class. It is also possible for the threads to attempt to use the device context simultaneously. When this happens, the system allows only one thread to successfully finish its drawing operation.
blockquote>
如果其他所有方法均失败,则只需重建CachedBitmap。
When you construct a CachedBitmap object, you must pass the address of a Graphics object to the constructor. If the screen associated with that Graphics object has its bit depth changed after the cached bitmap is constructed, then the DrawCachedBitmap method will fail, and you should reconstruct the cached bitmap. Alternatively, you can hook the display change notification message and reconstruct the cached bitmap at that time.
blockquote>
我并不是说CS_OWNDC是完美的解决方案,但这是朝着更好的解决方案迈出的一步。
编辑
在使用CS_OWNDC标志进行屏幕分辨率/位深度更改测试期间,示例程序似乎保留了相同的DC,但是,当删除该标志时,DC有所不同(Window 7 64位Ultimate)(应该工作相同)在不同的OS版本上进行测试...尽管可以轻松进行测试)。
Edit2
此示例不调用GetUpdateRect来检查在WM_PAINT期间是否需要绘制窗口。那是一个错误。
有例外,但是通常,每次调用
GetDC 或BeginPaint 时,您可能会获得不同的DC。因此,您不应尝试将状态保存在DC中。 (如果必须执行此操作以提高性能,则可以为一类窗口或特定窗口实例创建特殊的DC,但这听起来并不是您真正需要或想要的。)但是,在大多数情况下,这些DC都是兼容的。它们将代表相同的图形模式,因此即使您获得了不同的DC,兼容的位图也应能正常工作。
有些Windows消息会告诉您图形模式何时更改,例如
WM_DISPLAYCHANGE 和WM_PALETTECHANGED 。您可以侦听这些内容,然后重新创建缓存的位图。由于这些事件很少见,因此您不必担心此时重新创建缓存的位图会对性能产生影响。您还可以收到有关主题更改之类的通知。那些不会改变图形模式-它们是一个更高层次的概念-因此您缓存的位图仍应与您获得的任何DC兼容。但是,如果您想在主题更改时更改位图,则也可以收听
WM_THEMECHANGED 。
您可以在dc取悦您的任何窗口上绘画。他们都是有效的。一个窗口一次并不能只代表一个直流。因此,每次调用GetDC时,内部都会使用BeginPaint进行调用,您将获得一个新的唯一dc,尽管如此,dc仍代表相同的显示区域。
完成对它们的处理后,只需使用ReleaseDC(或EndPaint)即可。在Windows 3.1时代,设备上下文是有限的或非常昂贵的系统资源,因此鼓励应用程序不要保留它们,而是从GetDC缓存中检索它们。如今,完全可以接受的是在创建窗口时创建一个dc,并在窗口的整个生命周期内对其进行缓存。唯一的"问题"是,在处理
WM_PAINT 时,BeginPaint返回的dc将被裁剪到无效的rect,而保存的将不会被裁剪。但是我不明白您要使用gdiplus实现什么。通常,如果长时间将一个对象...选定为dc,则该dc是内存dc,而不是窗口dc。
每次调用GetDC时,您将获得一个新的HDC,该HDC代表具有其自身状态的不同设备上下文。因此,在一个DC上设置的对象,背景颜色,文本模式等将不会影响通过对GetDC或BeginPaint的不同调用而检索到的另一个DC的状态。
系统无法随机使客户端检索到的HDC无效,并且实际上在后台做了很多工作,以确保在切换显示模式之前检索到的HDC继续起作用。即使更改位深度,从技术上来说,也会使dc不兼容,但无论如何也不会阻止应用程序继续使用hdc进行blit。
也就是说,明智的做法是至少注意WM_DISPLAYCHANGE,释放所有缓存的DC和设备位图,然后重新创建它们。