Bitmap for a three state check box with OBM_CHECKBOXES - winapi

I want to use three state checkbox in the list control. Today, there is facility for normal checkbox. The bitmap for normal check box are loaded from OBM_CHECKBOXES.
The problem is, I am not able to find the bitmap image for intermediate state (CBS_MIXEDNORMAL). Could anyone help which image index I can use for intermediate state using OBM_CHECKBOXES.

That is a non-themed bitmap. If you want a themed bitmap, you have to use the DrawThemeBackground to draw it to create a bitmap.
There are two bitmaps may be used for intermediate state:
One is CBS_MIXEDNORMAL. DrawThemeBackground(theme, hdc, BP_CHECKBOX, CBS_MIXEDNORMAL, &rc, nullptr);
Another is CBS_CHECKEDDISABLED. DrawThemeBackground(theme, hdc, BP_CHECKBOX, CBS_CHECKEDDISABLED, &rc, nullptr);
Code sample as below:
HDC hdc = BeginPaint(hWnd, &ps);
HTHEME theme = OpenThemeData(nullptr, L"Button");
RECT rc = {};
rc.right = 12;
rc.bottom = 12;
DrawThemeBackground(theme, hdc, BP_CHECKBOX, CBS_MIXEDNORMAL, &rc, nullptr);
CloseThemeData(theme);
EndPaint(hWnd, &ps);

Related

Rich Edit Control paints whole application black after unminimize

SOLVED: I've posted my solution as an answer.
Here's my problem: (gif) (Sort of solved if I reload the bitmaps for painting the background image when unminimizing before any WM_PAINT message.)
It happens whenever I unminimize the application, time when the app first displays OK (for a brief split second unless you are stepping with the debugger), and suddenly turns black (or whatever color has been set as hbrBackground in the app window classes). I can prevent this behaviour by reloading the HBITMAPs used in WM_PAINT, which are global variables and initialised with their corresponding values at app startup.
The gif starts showing the app reopened after a minimize, with the debugger stepping through the parent window of the Rich Edit Control message loop, the moments just before and after the background of all windows turns black, and then stepping into the Rich Edit Control subclass message loop, into WM_PAINT.
This never happens if I'm switching between apps without the app in question never having been minimized before.
This never happens if the Rich Edit Control (RICHEDIT50W) hasn't displayed any text before, ie. the app works OK if no text is ever displayed.
This is the window tree:
Main Window
Some Child Windows
Child Window 1
Rich Edit Control
The stepping goes out of the Child Window 1 WndProc; into the WM_PAINT of the Rich Edit Control inside the WndSubclassProcWhatever callback.
Some of the things I've done before realizing that a call to LoadImage() just after unminimize could fix the background issue:
Intercept the message loop of the Rich Edit Control with a subclass, and handle (as well as in every other window) messages as: WM_COMMAND, WM_IME_NOTIFY, WM_NCPAINT, WM_WINDOWPOSCHANGED, WM_WINDOWPOSCHANGING, WM_ERASEBKGND... Mainly returning something different than the DefSubclassProc/DefWindowProcW.
Calling ValidateRect() as soon as the app is reopened...
It has happened before that instead of the whole app turning black, only the text "highlighting" or the Rich Edit Control parent turned black, with the whole app turning black after another minimize unminimize cycle.
I'm using Visual Studio Community 2019 with default settings in an updated Windows 10, and seeing this problem both in release and debug builds.
I'm now looking forward to prevent the bitmaps from "unloading", thus saving many seemingly unnecessary LoadImage() calls. SOLVED
I tried uploading a minimal version of the code, yet the behaviour turned out not to be exactly the same, so thanks for the answer given before!
This has nothing to do with Rich Edit Control , even if you delete all of the controls, this will happen.
All you have to do is add a default color to the window background when you register the window.
Here:
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_DBLCLKS;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL; // Procesás WM_GETICON
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = NULL;
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"mainWindowClass";
wcex.hIconSm = NULL; // Procesás WM_GETICON
return RegisterClassExW(&wcex);
}
Click again after minimize the window will cause it to redraw with the default background color, But you set the background color to NULL here. So try to change wcex.hbrBackground = NULL to wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1)
Updated:
It sounds like you have the same problem as I have encountered before.
Here is my previous code:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
hdcMem = CreateCompatibleDC(hdc);
HGDIOBJ previousbit = SelectObject(hdcMem, hBmp);
AlphaBlend(hdc, 0, 0, width_1, height_1, hdcMem, 0, 0, width_1, height_1, bf);
DeleteDC(hdcMem);
EndPaint(hWnd, &ps);
}
break;
case WM_MOUSEWHEEL:
{
if (GET_WHEEL_DELTA_WPARAM(wParam) > 0 && bTrans <= 234)
{
bTrans += 20;
bf.SourceConstantAlpha = bTrans;
InvalidateRect(hWnd, NULL, TRUE);
}
if (GET_WHEEL_DELTA_WPARAM(wParam) < 0 && bTrans >= 20)
{
bTrans -= 20;
bf.SourceConstantAlpha = bTrans;
InvalidateRect(hWnd, NULL, TRUE);
}
return 0;
}
I slide the mouse wheel, it will trigger the InvalidateRect(hWnd, NULL, TRUE);
But if I delete DeleteDC(hdcMem), it will return a main window without a picture.
The debug snapshot is :
Yes, you can find previousbit == NULL.
As #Remy Lebeau said that, you are leaking the HBITMAP that SelectObject() returns, and giving the HDC permission to potentially destroy your bitmapBackgroundMainWindow behind your back.
This is the main cause.
A call to DeleteObject() fixed the issue.
This is the code from one of the bitmap-background window WM_PAINT messages fixed with the corresponding DeleteObject() call:
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
HDC temporaryDC = CreateCompatibleDC(hdc);
BITMAP bitmapToBitBlt;
HGDIOBJ hgdiobjToBitBlt = SelectObject(temporaryDC, bitmapBackgroundMainWindow);
GetObjectW(bitmapBackgroundMainWindow, sizeof(BITMAP), &bitmapToBitBlt);
BitBlt(hdc, 0, 0, bitmapToBitBlt.bmWidth, bitmapToBitBlt.bmHeight, temporaryDC, 0, 0, SRCCOPY);
DeleteObject(temporaryDC); // This fixes the app.
EndPaint(hWnd, &ps);
return 0;
As Windows Docs state, after calling CreateCompatibleDC():
When you no longer need the memory DC, call the DeleteDC function. We recommend that you call DeleteDC to delete the DC. However, you can also call DeleteObject with the HDC to delete the DC.
How does this translate to my app unexpected behaviour, I don't know, feel free to clarify in the comments!

Windows - GDI - Scaling a screen DC to a printer DC without modifying the draw functions

I'm writing a Windows application showing a document to the user. The content is painted using the GDI functions, and all appears as expected on the screen.
Now I want to print this document. I get a printer device context, and I do the exact same drawing as I do on the screen. Of course the printed content appears tiny on the top of the printed page. The reason of this behavior is clear for me, and is fully explained here:
https://www.codeproject.com/Articles/764057/GDI-Drawing-and-Printing
So I need to add a scaled viewport on my printer DC, and there are several functions to achieve that in the GDI. However I'm a little puzzled about HOW to configure these functions. I tried various examples found on the internet, but none of them worked for me.
My screen resolution is 1920x1080 pixels, and I'm trying to print on an A4 portrait page. I tested various configurations, and I found that the best approximation to fit on my printed page is the following:
::SetMapMode(hDC, MM_ISOTROPIC);
::SetWindowExtEx(hDC, 1, 1, NULL);
::SetViewportExtEx(hDC, 5, 5, NULL);
::SetViewportOrgEx(hDC, -10200, 0, NULL);
As the screen and print configurations may, of course, change on other PC, I need to know how the above values may be calculated, but I cannot find a formula that works in my case. Especially I don't know why I need to scale my canvas origin using the SetViewportOrgEx() function, nobody mentioned that on the documents I read.
So what is the correct manner to calculate my print DC viewport, considering that:
The exactly same painting functions will be used for both the screen and printer drawing, and I will NEVER write different functions to print on the screen and the printer
The screen and printer devices may be entirely configured by the user, but the printed result should always fit the document on both the screen and the printer
And as an additional question, it would be better to use a metafile to do this kind of job?
In order to map the screen coordinates to paper coordinates, we need the width and length of the paper. This information is available in GetDeviceCaps(hdc, PHYSICALWIDTH) and GetDeviceCaps(hdc, PHYSICALHEIGHT), where hdc is printer's device context. We already have the screen coordinates somewhere.
The printer cannot print on the edges of the paper. We can get that information from PHYSICALOFFSETX and PHYSICALOFFSETY.
The example below uses uses a common function paint which does all the painting. print doesn't do any painting, it calls paint instead.
This assumes that rc.left and rc.right is (0,0) in screen coordinates.
void paint(HDC hdc, RECT rc)
{
HBRUSH brush = GetSysColorBrush(COLOR_WINDOWTEXT);
InflateRect(&rc, -10, -10);
FrameRect(hdc, &rc, brush);
DrawText(hdc, L"hello world", -1, &rc, 0);
}
void print(HWND hWnd, RECT rc)
{
PRINTDLG pd = { sizeof(pd) };
pd.hwndOwner = hWnd;
pd.Flags = PD_RETURNDC;
if(!PrintDlg(&pd))
return;
HDC hdc = pd.hDC;
DOCINFO doc = { sizeof(doc) };
StartDoc(hdc, &doc);
StartPage(hdc);
SetMapMode(hdc, MM_ISOTROPIC);
SetWindowExtEx(hdc, rc.right, rc.bottom, NULL);
SetViewportExtEx(hdc,
GetDeviceCaps(hdc, PHYSICALWIDTH), GetDeviceCaps(hdc, PHYSICALHEIGHT), NULL);
SetViewportOrgEx(hdc,
-GetDeviceCaps(hdc, PHYSICALOFFSETX), -GetDeviceCaps(hdc, PHYSICALOFFSETY), NULL);
paint(hdc, rc);
EndPage(hdc);
EndDoc(hdc);
DeleteObject(hdc);
}
Testing:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc;
GetClientRect(hwnd, &rc);
paint(hdc, rc);
EndPaint(hwnd, &ps);
break;
}
case WM_LBUTTONDOWN:
{
RECT rc;
GetClientRect(hwnd, &rc);
print(hwnd, rc);
break;
}

How do I correctly convert an ICON to a BITMAP using MFC?

I'm loading an ICON of another application via
HICON ico = ExtractIcon(NULL, L"path\\to\\OtherApp.exe", 0);
How can I create a CBitmap object from this icon?
Specifically (not really answered in the dup question for me):
Which device context?
At the end, I want a CBitmap object that outlives the function that converts the icon:
What do I need to clean up immediately and what do I need to keep around? (DC, ...?)
Here's the code I have so far:
void ConvertIconToBitmap(CBitmap& bmpObj, HICON hIcon, int cx, int cy) {
CClientDC clientDC(NULL);
CDC dc;
dc.CreateCompatibleDC(NULL);
CBitmap bmpTmp;
VERIFY( bmpTmp.CreateCompatibleBitmap(&clientDC, cx, cy) );
CBitmap* pOldBmp = (CBitmap*)dc.SelectObject(&bmpTmp);
VERIFY( ::DrawIconEx( dc.GetSafeHdc(), 0, 0, hIcon, cx, cy, 0, NULL, DI_NORMAL) );
dc.SelectObject( pOldBmp );
// For some reason I need to copy the bitmap here: (maybe it's the DIB flag)
HBITMAP hDibBmp = (HBITMAP)::CopyImage((HANDLE)(HBITMAP)bmpTmp, IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE | LR_CREATEDIBSECTION);
VERIFY( hDibBmp );
VERIFY( bmpObj.Attach(hDibBmp) );
// VERIFY( bmpObj.Attach(bmpTmp.Detach()) );
}
Now, this code works, but I don't understand it:
Why do I need a CClientDC? (If I use only CDC the bitmap is not shown or Black&White, depending on where I put it.)
(Why) is the dc.SelectObject( pOldBmp ) line needed?
Why do I have to do CopyImage? (If I don't, the bitmap is sometimes drawn with inverted colors.)
Does this code leak anything or is everything properly cleaned up?
Here's another version that also seems to work:
void ConvertIconToBitmap2(CBitmap& bmpObj, HICON hIcon, int cx, int cy) {
CClientDC clientDC(NULL);
CDC memDC;
memDC.CreateCompatibleDC(&clientDC);
ASSERT(hIcon);
ICONINFO info;
VERIFY(GetIconInfo(hIcon, &info));
BITMAP bmp;
GetObject(info.hbmColor, sizeof(bmp), &bmp);
HBITMAP hBitmap = (HBITMAP)CopyImage(info.hbmColor, IMAGE_BITMAP, 0, 0, 0);
ASSERT(hBitmap);
ASSERT(memDC.GetSafeHdc());
HBITMAP hOldBmp = (HBITMAP)memDC.SelectObject(hBitmap);
clientDC.BitBlt(0, 0, bmp.bmWidth, bmp.bmHeight, &memDC, 0, 0, SRCCOPY);
memDC.SelectObject(hOldBmp);
VERIFY( bmpObj.Attach(hBitmap) );
DeleteObject(info.hbmColor);
DeleteObject(info.hbmMask);
}
•Why do I need a CClientDC? (If I use only CDC the bitmap is not shown or Black&White, depending on where I put it.)
You would need a DC that is based on your window or the screen, just declaring a CDC is not enough, you will also need to call dc.Attach() or one of the CDC::Create* functions.
•(Why) is the dc.SelectObject( pOldBmp ) line needed?
So that the bitmap is disconnected from the DC
•Why do I have to do CopyImage? (If I don't, the bitmap is sometimes drawn with inverted colors.)
It looks like you are creating a device independent bimap using the CopyImage() call using the LR_CREATEDIBSECTION parameter
•Does this code leak anything or is everything properly cleaned up?
Looks ok to me!

Correctly release resources after flipping a bitmap

I have an application that writes a very large data set as a bitmap. [~500MB] I am writing the data patch wise from top to down. Due to the nature of BMP's file structure it must be flipped once written to the disk. (I wrote it this way because I assumed that flipping a bitmap would be a common application and I would find libraries to do the task)
I am using a code snippet I found on the internet to flip the bitmap. This one:
// GetInvertedBitmap - Creates a new bitmap with the inverted image
// Returns - Handle to a new bitmap with inverted image
// hBitmap - Bitmap to invert
// bLateral - Flag to indicate whether to invert laterally or vertically
HBITMAP CRisatImport::GetInvertedBitmap( HBITMAP hBitmap, BOOL bLateral )
{
// Create a memory DC compatible with the display
CDC sourceDC, destDC;
sourceDC.CreateCompatibleDC( NULL );
destDC.CreateCompatibleDC( NULL );
// Get logical coordinates
BITMAP bm;
::GetObject( hBitmap, sizeof( bm ), &bm );
// Create a bitmap to hold the result
HBITMAP hbmResult = ::CreateCompatibleBitmap(CClientDC(NULL),
bm.bmWidth, bm.bmHeight);
// Select bitmaps into the DCs
HBITMAP hbmOldSource = (HBITMAP)::SelectObject( sourceDC.m_hDC, hBitmap );
HBITMAP hbmOldDest = (HBITMAP)::SelectObject( destDC.m_hDC, hbmResult );
if( bLateral )
destDC.StretchBlt( 0, 0, bm.bmWidth, bm.bmHeight, &sourceDC,
bm.bmWidth-1, 0, -bm.bmWidth, bm.bmHeight, SRCCOPY );
else
destDC.StretchBlt( 0, 0, bm.bmWidth, bm.bmHeight, &sourceDC,
0, bm.bmHeight-1, bm.bmWidth, -bm.bmHeight, SRCCOPY );
// Reselect the old bitmaps
::SelectObject( sourceDC.m_hDC, hbmOldSource );
::SelectObject( destDC.m_hDC, hbmOldDest );
return hbmResult;
}
The problem is that I have a limited understanding of the above code. I tried to write a function to use the above snippet the best I could from sample code from MSDN. I do not think I am releasing all resources correctly. And I cant seem to figure out the error - mainly because my lack of knowledge about GDI. I would really appreciate it if some one pointed out what I am doing wrong.
The program crashes if I try to call this function twice - that is why I suspect that I am releasing resources incorrectly.
This is the function I wrote:
void CRisatImport::flipBitMapSaveAsJPG( CString outputFileName, CString outputJPGName, bool saveAsJpg)
{
// Flip the bitmap to correct odd file structure
HBITMAP hBitmap;
hBitmap = (HBITMAP)::LoadImage(NULL, outputFileName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
hBitmap = GetInvertedBitmap( hBitmap, FALSE );
CImage image;
image.Attach(hBitmap);
image.Save(outputFileName,Gdiplus::ImageFormatBMP);
if(saveAsJpg){
image.Save(outputJPGName,Gdiplus::ImageFormatJPEG);
}
image.Destroy();
DeleteObject( hBitmap );
}
I am using MFC and VS2010 for this application - I have 4GB of RAM and no other applications running.
This is the error I get:
When you Attach your bitmap handle to CImage you're handing over responsibility for the handle's lifetime to CImage so you don't want to Destory it after. But you're also using the same variable name for your new bitmap. Try this instead:
void CRisatImport::flipBitMapSaveAsJPG( CString outputFileName, CString outputJPGName, bool saveAsJpg)
{
// Flip the bitmap to correct odd file structure
HBITMAP hBitmapLoad = (HBITMAP)::LoadImage(NULL, outputFileName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
HBITMAP hBitmapInverted = GetInvertedBitmap( hBitmapLoad , FALSE );
DeleteObject( hBitmapLoad );
CImage image;
image.Attach(hBitmapInverted );
image.Save(outputFileName,Gdiplus::ImageFormatBMP);
if(saveAsJpg){
image.Save(outputJPGName,Gdiplus::ImageFormatJPEG);
}
image.Destroy();
}

Win32: Does a window have the same HDC for its entire lifetime?

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.
There are exceptions, but in general, you may get a different DC each time you call GetDC or BeginPaint. Thus you shouldn't try to save state in the DC. (If you must do this for performance, there are special DCs you can create for a class of windows or a particular window instance, but it doesn't sound like that's what you really need or want.)
Most of the time, however, those DCs will be compatible. They will represent the same graphics mode, so your compatible bitmap should work, even if you get a different DC.
There are Windows messages that tell you when the graphics mode changes, like WM_DISPLAYCHANGE and WM_PALETTECHANGED. You can listen for these, and recreate your cached bitmap. Since those are rare events, you won't have to worry about the performance impact of recreating your cached bitmap at that point.
You can also get notifications for things like theme changes. Those don't change the graphics mode--they're a higher level concept--so your cached bitmap should still be compatible with any DC you get. But if you want to change bitmap when the theme changes, you can listen for WM_THEMECHANGED as well.
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:
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 — 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.
Perhaps this example will illustrate the use of CS_OWNDC better:
#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;
}
The CS_OWNDC flag is not to be confused with the CS_CLASSDC flag which:
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.
If all else fails just reconstruct the 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.
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.
You can draw onto whichever window dc pleases you. They're both valid. A window does not have just one dc that can represent it at a time. So each time you call GetDC - and BeginPaint internally does so, you will get a new, unique dc, that nonetheless represents the same display area.
Just ReleaseDC (or EndPaint) when you're done with them. In the days of Windows 3.1 device contexts were a limited, or very expensive system resource, so applications were encouraged to never hold onto them, but to retrieve them from the GetDC cache. nowadays its perfectly acceptable to create a dc at window creation, and cache it for the life of the window.
The only "problem" is, when handling WM_PAINT, the dc returned by BeginPaint will be clipped to the invalid rect, and the saved one will not.
I don't however understand what you are attempting to achieve with gdiplus. Usually, if an object is ... selected into a dc for a long period of time, that dc is a memory dc, not a window dc.
Each time GetDC is called you WILL get a new HDC representing a distinct device context with its own state. So, objects, background colors, text modes etc. set on one DC will NOT effect that state of another DC retrieved by a different call to GetDC or BeginPaint.
The system cannot randomly invalidate HDCs retrieved by the client, and actually does a lot of work in the background to ensure that HDCs retrieved before a display mode switch, continue to function. Even changing the bit depth, that technically makes the dc's incompatible, will not, in any way, prevent an application from continuing to use an hdc to blit.
That said, it is wise to watch at LEAST for WM_DISPLAYCHANGE, release any cached DCs and device bitmaps, and recreate them.

Resources