CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 11 of 11
  1. #1
    Join Date
    Dec 2010
    Posts
    5

    Dialog Box WM_PAINT problem

    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!

    P.S I attached the program.
    Attached Files Attached Files

  2. #2
    VictorN's Avatar
    VictorN is offline Super Moderator Power Poster
    Join Date
    Jan 2003
    Location
    Hanover Germany
    Posts
    20,430

    Re: Dialog Box WM_PAINT problem

    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!
    Victor Nijegorodov

  3. #3
    Join Date
    Dec 2010
    Posts
    5

    Re: Dialog Box WM_PAINT problem

    Quote Originally Posted by VictorN View Post
    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?)

    Quote Originally Posted by VictorN View Post
    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?

  4. #4
    VictorN's Avatar
    VictorN is offline Super Moderator Power Poster
    Join Date
    Jan 2003
    Location
    Hanover Germany
    Posts
    20,430

    Re: Dialog Box WM_PAINT problem

    Quote Originally Posted by Elbios View Post
    I see, but how should I handle this request exactly?
    Just let it repaint!
    Victor Nijegorodov

  5. #5
    Join Date
    Dec 2010
    Posts
    5

    Re: Dialog Box WM_PAINT problem

    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.

  6. #6
    VictorN's Avatar
    VictorN is offline Super Moderator Power Poster
    Join Date
    Jan 2003
    Location
    Hanover Germany
    Posts
    20,430

    Re: Dialog Box WM_PAINT problem

    Quote Originally Posted by Elbios View Post
    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
    Victor Nijegorodov

  7. #7
    Join Date
    Dec 2010
    Posts
    5

    Re: Dialog Box WM_PAINT problem

    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.

  8. #8
    VictorN's Avatar
    VictorN is offline Super Moderator Power Poster
    Join Date
    Jan 2003
    Location
    Hanover Germany
    Posts
    20,430

    Re: Dialog Box WM_PAINT problem

    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?
    Victor Nijegorodov

  9. #9
    Join Date
    Dec 2010
    Posts
    5

    Re: Dialog Box WM_PAINT problem

    Quote Originally Posted by VictorN View Post
    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?

    Thank you again for your help.

  10. #10
    VictorN's Avatar
    VictorN is offline Super Moderator Power Poster
    Join Date
    Jan 2003
    Location
    Hanover Germany
    Posts
    20,430

    Re: Dialog Box WM_PAINT problem

    Quote Originally Posted by Elbios View Post
    ... 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.
    Victor Nijegorodov

  11. #11
    Join Date
    Nov 2002
    Location
    California
    Posts
    4,556

    Re: Dialog Box WM_PAINT problem

    Quote Originally Posted by Elbios View Post
    ... 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.

    Mike

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured