Hello everyone,
I'm studying an Object-Oriented Programming course and I'm supposed to write a small app in C++ which draws shapes based on given coordinates. I'm trying to do it in WINAPI (I use Dev C++) and I have a problem with the WM_PAINT message, I don't think I know exactly how it works.
I have a main window which is empty and serves as a canvas for drawing shapes, and a dialog box with boxes and buttons where you input data and initiate drawing. I completed the application and it works but it unfortunately uses up 25% of my CPU all the time. I noticed that if I remove one thing from the code, it then doesn't hog the resources but the shapes aren't drawn.
This is my message loop:
Code:
while ((bRet=GetMessage (&messages, NULL, 0, 0))!=0)
{
if (bRet==-1) return 0;
else if (!IsWindow(toolbox) || !IsDialogMessage(toolbox, &messages))
{
/* Translate virtual-key messages into character messages */
TranslateMessage(&messages);
/* Send message to WindowProcedure */
DispatchMessage(&messages);
}
}
This is the WinProc function:
Code:
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) /* handle the messages */
{
case WM_PAINT: // this case uses 25% CPU if it's there, but doesn't draw shapes if I delete it
break;
case WM_DESTROY:
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
I noticed that if i declare PAINTSTRUCT ps and do:
case WM_PAINT:
BeginPaint(hwnd,&ps);
EndPaint(hwnd,&ps);
break;
then it doesn't hog resources but no shapes can be drawn (canvas window remains clear);
this is my dialog box callback function(simplified):
Code:
INT_PTR CALLBACK toolboxProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
HWND wzium; //not important
PAINTSTRUCT ps;
char* color=NULL;
char colortemp[10];
HBRUSH brush;
HPEN hpen=CreatePen(PS_NULL,1,RGB(0,0,0));
RECT rect;
switch (message) /* handle the messages */
{
case WM_INITDIALOG:
SendDlgItemMessage(hwndDlg,IDC_COMBO1,CB_ADDSTRING,0,(LPARAM)"triangle");
SendDlgItemMessage(hwndDlg,IDC_COMBO1,CB_ADDSTRING,0,(LPARAM)"circle");
SendDlgItemMessage(hwndDlg,IDC_COMBO1,CB_ADDSTRING,0,(LPARAM)"rectangle"); // add strings to combobox
return TRUE;
case WM_COMMAND:
if(HIWORD(wParam)==BN_CLICKED)
{
switch(LOWORD(wParam))
{
case IDC_BUTTON1: // the "draw" button
// here is code (i cut it out) which checks for different cases of strings in editbox and sets the color of the brush accordingly
hdc=BeginPaint(GetParent(hwndDlg),&ps);
SelectObject(hdc,brush);
SelectObject(hdc,hpen);
if (SendDlgItemMessage(hwndDlg,IDC_COMBO1,CB_GETCURSEL,0,0)==0) //if triangle radio button is chosen
{
//put coordinates in a new triangle object
triangle tr(GetDlgItemInt(hwndDlg,IDC_EDIT4,NULL,FALSE),
GetDlgItemInt(hwndDlg,IDC_EDIT6,NULL,FALSE),
GetDlgItemInt(hwndDlg,IDC_EDIT2,NULL,FALSE),
GetDlgItemInt(hwndDlg,IDC_EDIT5,NULL,FALSE),
GetDlgItemInt(hwndDlg,IDC_EDIT3,NULL,FALSE),
GetDlgItemInt(hwndDlg,IDC_EDIT7,NULL,FALSE));
tr.draw(hdc); //draws triangle (polygon)
}
else if (SendDlgItemMessage(hwndDlg,IDC_COMBO1,CB_GETCURSEL,0,0)==1) //if circle is chosen
{
circle ci(GetDlgItemInt(hwndDlg,IDC_EDIT4,NULL,FALSE),
GetDlgItemInt(hwndDlg,IDC_EDIT6,NULL,FALSE),
GetDlgItemInt(hwndDlg,IDC_EDIT10,NULL,FALSE));
ci.draw(hdc);
}
else if (SendDlgItemMessage(hwndDlg,IDC_COMBO1,CB_GETCURSEL,0,0)==2) //if rectangle is chosen
{
rectangle re(GetDlgItemInt(hwndDlg,IDC_EDIT4,NULL,FALSE),
GetDlgItemInt(hwndDlg,IDC_EDIT6,NULL,FALSE),
GetDlgItemInt(hwndDlg,IDC_EDIT8,NULL,FALSE),
GetDlgItemInt(hwndDlg,IDC_EDIT9,NULL,FALSE));
re.draw(hdc);
}
DeleteObject(brush);
DeleteObject(hpen);
EndPaint(GetParent(hwndDlg),&ps);
InvalidateRect ( GetParent(hwndDlg), NULL, TRUE ); //i use this so that newly drawn shapes overwrite previous ones
return TRUE;
case IDC_BUTTON2: //exit button
//stuff here
return TRUE;
break;
case IDOK1: //clear screen button
//stuff here
return TRUE;
}
}
break;
}
return FALSE;
}
can someone explain to me how the WM_PAINT message works in this app and what can I do to make it draw shapes at 0% CPU use? thanks!
1. 0% CPU means that app does nothing, so noting happens and nothing changes...
2. WM_PAINT message is sent from Windows to your App window ... when the system or another application makes a request to paint a portion of an application's window. So if you ignore this request and do nothing - northing is shown!
1. 0% CPU means that app does nothing, so noting happens and nothing changes...
well i meant 0-1% because the app won't require more, obviously 25% is ridiculous for such an app which makes me think that the WM_PAINT message is being repeatedly sent by the OS (why?)
Originally Posted by VictorN
2. WM_PAINT message is sent from Windows to your App window ... when the system or another application makes a request to paint a portion of an application's window. So if you ignore this request and do nothing - northing is shown!
I see, but how should I handle this request exactly?
ok, I handled the WM_PAINT case in WinProc like this:
BeginPaint(hwnd,&ps);
EndPaint(hwnd,&ps);
and I also moved all the drawing functions from DialogProc to this WM_PAINT case because I read that BeginPaint/EndPaint functions should always be in WM_PAINT handling.
In Winproc it looks sort of like this (flag is a global variable initialized to 0):
Code:
case WM_PAINT:
if (flag==0)
{
BeginPaint(hwnd,&ps);
EndPaint(hwnd,&ps);
}
if (flag==1)
// here code for drawing specified shape, at the end set flag to 0
if (flag==2)
// here code for drawing a white rectangle same size as the window (clearing screen), set flag to 0
break;
In Dialogproc it looks like this:
Code:
case WM_COMMAND:
if(HIWORD(wParam)==BN_CLICKED)
{
switch(LOWORD(wParam))
{
case IDC_BUTTON1: // the "draw shape" button
flag=1;
InvalidateRect(GetParent(hwndDlg),NULL,TRUE); //to send the WM_PAINT msg
return TRUE;
case IDC_BUTTON2: //exit button
DestroyWindow(hwndDlg);
toolbox=NULL;
PostQuitMessage (0);
return TRUE;
case IDOK1: //clear screen button
flag=2;
InvalidateRect ( GetParent(hwndDlg), NULL,TRUE );
return TRUE;
}
}
break;
The app works now, but for some reason it always freezes after about a minute of using. I have no idea why it's happening. Why does it work okay for a minute and then stop? It freezes in a weird way because i'm not getting any "program not responding" message, cpu&mem usage is low, the dialog box just stops working, i can't click any button or edit any edit controls, the only button that works is the exit button which exits the app (not the top-right X button but the separate one I made - IDC_BUTTON2).
Could you explain this? I realize the way I'm implementing this is probably not the best one but I'm a beginner programmer and only recently moved from writing console apps in C on Linux to writing in c++ and winapi.
ok, I handled the WM_PAINT case in WinProc like this:
BeginPaint(hwnd,&ps);
EndPaint(hwnd,&ps);
and I also moved all the drawing functions from DialogProc to this WM_PAINT case because I read that BeginPaint/EndPaint functions should always be in WM_PAINT handling.
In Winproc it looks sort of like this (flag is a global variable initialized to 0):
Code:
case WM_PAINT:
if (flag==0)
{
BeginPaint(hwnd,&ps);
EndPaint(hwnd,&ps);
}
if (flag==1)
// here code for drawing specified shape, at the end set flag to 0
if (flag==2)
// here code for drawing a white rectangle same size as the window (clearing screen), set flag to 0
break;
It is wrong.
You drawing has to be between BeginPaint and EndPaint calls. And for drawing you have to use the hdc from the returned PAINTSTRUCT ps
Ok, I get it now. I corrected it, but it still hangs after a minute, the exact same way. Here's my wm_paint:
Code:
case WM_PAINT:
hdc=BeginPaint(hwnd,&ps);
if (flag==1) // if draw button was pressed
{
//here code to retrieve string describing desired color and a call to CreateSolidBrush, then two SelectObject calls for brush and pen
//Ellipse(), Rectangle(), or Polygon() according to selected option
//two DeleteObject calls, set flag to 0
}
if (flag==2) //if "clear" button is pressed
{
//GetWindowRect, CreateSolidBrush,SelectObject,Rectangle(white, of window size), DeleteObject, set flag to 0
}
EndPaint(hwnd,&ps);
break;
and this is my dialog box function after corrections (i think the bug is actually here somewhere):
Code:
INT_PTR CALLBACK toolboxProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
HFONT hfont0 = CreateFont(-11, 0, 0, 0, 0, FALSE, FALSE, FALSE, 1, 0, 0, 0, 0, ("Ms Shell Dlg"));
switch (message) /* handle the messages */
{
case WM_INITDIALOG:
//here some controls for dialog box are created (their handles are global vars), also SendMessages with WM_SETFONT for those controls
return TRUE;
case WM_COMMAND:
if(HIWORD(wParam)==BN_CLICKED)
{
switch(LOWORD(wParam))
{
case IDC_RADIO1:
//ShowWindow calls (visibility of some controls depend on radio button selection)
return TRUE;
case IDC_RADIO2:
//ShowWindow calls
return TRUE;
case IDC_RADIO3:
//ShowWindow calls
return TRUE;
case IDC_BUTTON1: // the "draw" button
flag=1;
InvalidateRect(GetParent(hwndDlg),NULL,TRUE);
return TRUE;
case IDC_BUTTON2: //exit button
DestroyWindow(hwndDlg);
toolbox=NULL;
PostQuitMessage (0);
return TRUE;
case IDOK1: //clear screen button
flag=2;
InvalidateRect ( GetParent(hwndDlg), NULL,TRUE );
return TRUE;
default:
return FALSE;
}
}
return FALSE;
default:
return FALSE;
}
}
Last edited by Elbios; December 14th, 2010 at 04:16 AM.
You create HFONT hfont0 every time the toolboxProc is called...
But you never delete it! Why???
Why not create it only once and save in some global variable?
You create HFONT hfont0 every time the toolboxProc is called...
But you never delete it! Why???
Why not create it only once and save in some global variable?
Thank You! This is why my app kept freezing. I saved the font as a global variable now and it works. By the way, what is the most appropriate way of declaring variables that I want to be accessible in both callback functions (WinProc and DialogProc) or both in WinMain and WinProc? Should I always use global variables in these cases or perhaps make a class in the header and put them there as static? How do professionals do this?
... what is the most appropriate way of declaring variables that I want to be accessible in both callback functions (WinProc and DialogProc) or both in WinMain and WinProc? Should I always use global variables in these cases or perhaps make a class in the header and put them there as static? How do professionals do this?
Sorry. I cannot answer...
I use MFC, so I don't have such a problem.
... unfortunately uses up 25% of my CPU all the time...
Here is an explanation of why your original program (without BeginPaint and EndPaint) used 25% CPU.
First off, you probably have a quad-core machine, so in actuality, since the application is singly-threaded, one core was pegged at 100%.
The reason is this: Windows sends your application a WM_PAINT message whenever it detects that your application has a window with an invalid region that needs re-painting. The proper response in your application is to draw in the invalidated region, and then to validate it. The way you do this is by calling BeginPaint to get a device context, using the device context to draw, and then calling EndPaint which releases the device context and (importantly) also re-validates the update region.
In your case without the BeginPaint/EndPaint calls, the invalidated region was not being re-validated. As a consequence, Windows continuously determined that there was an invalidated region that needed re-painting, and was continuously sending your application a continuous stream of WM_PAINT messages, occupying 100% CPU, and never resulting in validation of the update region.
* The Best Reasons to Target Windows 8
Learn some of the best reasons why you should seriously consider bringing your Android mobile development expertise to bear on the Windows 8 platform.