Am i allowed to use a DC outside of a paint cycle?
Is my window's DC guaranteed to be valid forever?
i'm trying to figure out how long my control's Device Context (DC) is valid.
i know that i can call:
GetDC(hWnd);
to get the device context of my control's window, but is that allowed?
When Windows sends me a WM_PAINT message, i am supposed to call BeginPaint/EndPaint to properly acknowledge that i've painted it, and to internally clear the invalid region:
BeginPaint(hWnd, {out}paintStruct);
try
//Do my painting
finally
EndPaint(hWnd, paintStruct);
end;
But calling BeginPaint also returns me a DC inside the PAINTSTRUCT structure. This is the DC that i should be painting on.
i cannot find anything in the documentation that says that the DC returned by BeginPaint() is the same DC that i would get from GetDC().
Especially now, in the days of Desktop Composition, is it valid to paint on a DC that i obtain outside of BeginPaint?
There seem to be 2 ways i can get a DC to paint on during a paint cycle:
-
dc = GetDC(hWnd);
-
BeginPaint(&paintStruct);
There is a 3rd way, but it seems to be a bug with the Borland Delphi that i develop with.
During WM_PAINT processing, Delphi believes that the wParam is a DC, and proceeds to paint on it. Whereas the MSDN says that the wParam of a WM_PAINT message is unused.
The Why
My real goal is to try to keep a persistent GDI+ Graphics object against an HDC, so that i can use some better performing features of GDI+ that depend on having a persistent DC.
During the WM_PAINT message handling i want to draw a GDI+ image to the canvas. The following nieve version is very slow:
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 contains a faster performing bitmap, a CachedBitmap. But using it without thinking gives no performance benefit:
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);
}
The performance gain comes from creating the CachedBitmap once, so on program initialization:
m_graphics = new Graphics(GetDC(m_hwnd));
m_cachedBitmap = new CachedBitmap(b_someBitmap, m_graphcis);
And now on the paint cycle:
WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(m_hwnd, ps);
m_graphics.DrawCachedBitmap(m_cachedBitmap, 0, 0);
EndPaint(h_hwnd, ps);
}
Except now i'm trusting that the DC i obtained after program initializtion will be the same DC for my window as long as the application is running. This means that it survives through:
- fast user switches
- composition enabled/disabled
- theme switching
- theme disabling
i find nothing in MSDN that guarantees that the same DC will be used for a particular window for as long as the window exists.
Note: i am not using double-buffering, because i want to be a good developer, and do the right thing. *
Sometimes that means you double-buffering is bad.
Best Answer
The only way I know of that may (or may not) do what you are looking for is to create the window with the CS_OWNDC class style.
What that does is allocates a unique device context for each window in the class.
Edit
From the linked MSDN article:
Perhaps this example will illustrate the use of CS_OWNDC better:
The CS_OWNDC flag is not to be confused with the CS_CLASSDC flag which:
If all else fails just reconstruct the CachedBitmap.
I'm not saying that CS_OWNDC is the perfect solution, but it is one step towards a better solution.
Edit
The sample program seemed to retain the same DC during screen resolution / bit depth change testing with the CS_OWNDC flag, however, when that flag was removed, the DC's were different (Window 7 64-bit Ultimate)(should work the same over differn OS versions... although it wouldn't hurt to test).
Edit2
This example doesn't call GetUpdateRect to check if the window needs to be painted during the WM_PAINT. That is an error.