Link to home
Start Free TrialLog in
Avatar of anatta18
anatta18

asked on

Painting Problems In The SDK



I'm making a small program using the Windows SDK.  In it I have a listbox.  I'm using the WM_CTLCOLORLISTBOX message to change the colors of the list box.

My textbook says this message is sent before windows sends a
wm_paint message to the listbox.

It works, but not all of the time.  I tested the program out by moving other program windows on and off of my program window several times.  After a few reptitions the listbox loses the colors I set for it.  

I tried using InvalidateRect() & UpdateWindow() calls under my WM_PAINT messages to get the listbox to update itself, but this just makes it lose its colors sooner.

I've also noticed "ghost windows" when I move my program window around the screen very fast.  I have a 333mhz Pent II processor with 6gigs of hardrive.  Programs much larger then mine ( netscape ) do not tax my system this way.  Also, when my program fails to repaint I noticed that part of the window in msvc will not repaint.

Anyway, the bottom line is that I think I am doing something very amateurish ( I am an amateur, I just finnished reading petzold ) in my painting stratedy to cause these problems.

The code is below.  Any ideas would be appreciated.

Steve

( if the code is scrambled, let me know, I'll post it to a web page )



#include <windows.h>
#include <process.h>
#include <stdlib.h>
#include "Ezfont.h"
#include "MainProgram.h"

/*
***************************************************************************************
**                                                      GLOBAL VARIABLES
***************************************************************************************
*/

HWND hBannerWindow, hDifficultyLB;


typedef struct
{
      BOOL bKill;
}
PARAMS, *PPARAMS;


/*
****************************************************************************************
**                               FUNCTION PROTOTYPES
****************************************************************************************
*/


// Creates New Fonts
HFONT EzCreateFont( HDC, char*, int, int, int, BOOL );


// Process Windows Messages
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;


// A Seperate Thread Of Execution: Rotates Colors In The Title Banner
void ThreadBanner(PVOID);


// Rotate The Colors In The Title Banner
void RotateBannerColors(HDC, HWND, int [], int);


/*
****************************************************************************************
**                                       MAIN PROGRAM
****************************************************************************************
*/
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{

     // Get The Dimensions Of The User's Screen      
       int smCxScreen, smCyScreen;                                          
     smCxScreen = GetSystemMetrics(SM_CXSCREEN);
     smCyScreen = GetSystemMetrics(SM_CYSCREEN);


     static char szAppName[] = "TheVegetarianGame" ;
     HWND        hwnd ;
     MSG         msg ;
     WNDCLASSEX  wndclass ;

     wndclass.cbSize        = sizeof (wndclass) ;
     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
     wndclass.lpfnWndProc   = WndProc ;
     wndclass.cbClsExtra    = 0 ;
     wndclass.cbWndExtra    = 0 ;
     wndclass.hInstance     = hInstance ;
     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
     wndclass.hbrBackground = CreateSolidBrush( RGB(0, 128,0) );
     wndclass.lpszMenuName  = NULL ;
     wndclass.lpszClassName = szAppName ;
     wndclass.hIconSm       = LoadIcon (NULL, IDI_APPLICATION) ;

     RegisterClassEx (&wndclass) ;

     hwnd = CreateWindow (szAppName,         // window class name
                        "The Vegetarian Game",   // window caption
                    WS_OVERLAPPEDWINDOW,     // window style
                    CW_USEDEFAULT,           // initial x position
                    CW_USEDEFAULT,           // initial y position
                    smCxScreen / 2,          // initial x size
                    smCyScreen * .85,        // initial y size
                    NULL,                    // parent window handle
                    NULL,                    // window menu handle
                    hInstance,               // program instance handle
                        NULL) ;                         // creation parameters
 
 

     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;

    while (GetMessage (&msg, NULL, 0, 0))
    {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
    }

     return msg.wParam ;

}// End WinMain


/*
***************************************************************************************
**
**      Function      :      WndProc()
**
**      Purpose            :      Process Windows Messages For The Parent  & Other Child Windows
**
**      Created            :      Steve Russell  8/98
**
***************************************************************************************
*/

LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
      
      const int nRandColorSize = 9;

    static PARAMS params;



    HFONT hfont;
    HBRUSH hBannerBrush, hLbBrush;
      HWND  hCtlHwnd;
    HDC  hdc, hCtlHDC ;
    TEXTMETRIC tm;
      
            
    int nCtlID;
    int cxClient, cyClient;
      
   
    int nRandColor[nRandColorSize];
      
   
      hBannerBrush = CreateSolidBrush( RGB( 0,128,0) );
    hLbBrush       = CreateSolidBrush( RGB( 255, 128, 0 ) );
      
      

     switch (iMsg)
          {
          case WM_CREATE :

                hdc = GetDC(hwnd);
                        GetTextMetrics(hdc, &tm);
                        ReleaseDC(hwnd, hdc);

                        hBannerWindow = CreateWindow(
                                                              "static",
                                                              "The Vegetarian Game",
                                                               WS_CHILD | WS_VISIBLE | SS_CENTER,
                                                               0,0,0,0,
                                           hwnd,
                                                               (HMENU) IDC_STATIC_BANNERWINDOW,
                                                               ((LPCREATESTRUCT) lParam) -> hInstance,
                                                               NULL
                                                                 );



                        hDifficultyLB = CreateWindow(
                                                     "listbox",
                                                               NULL,
                                                               WS_CHILD | WS_VISIBLE | LBS_NOTIFY,
                                                               150,100,
                                           65,
                                                               60,
                                                               hwnd,
                                                               (HMENU) IDC_LB_DIFFICULTY,
                                                               ((LPCREATESTRUCT) lParam) -> hInstance,
                                                               NULL
                                                                 );

                      
                  hdc = GetDC(hBannerWindow);

                  hfont = EzCreateFont( hdc, "Times New Roman", 250, 250, 1, TRUE);
                  SendMessage(hBannerWindow, WM_SETFONT,(WPARAM) hfont,MAKELPARAM(TRUE, 1));
                  
                   ReleaseDC(hBannerWindow, hdc );

                  SendMessage(hDifficultyLB, LB_ADDSTRING,0,(LPARAM) "Easy" );
            SendMessage(hDifficultyLB, LB_ADDSTRING,1,(LPARAM) "Medium");
            SendMessage(hDifficultyLB, LB_ADDSTRING,2,(LPARAM) "Hard" );

             _beginthread(ThreadBanner,0, &params);


            return 0 ;

             

              case WM_SIZE :
                    cxClient = LOWORD(lParam);
                    cyClient = HIWORD(lParam);

                    MoveWindow(
                                          hBannerWindow,
                                          cxClient * .10,
                                          5,
                                          cxClient * .79,
                                          cyClient/13,
                                          TRUE
                                     );
 

                  
                    return 0;



          case WM_CTLCOLORSTATIC :
                      hCtlHwnd = (HWND) lParam;
                      hCtlHDC  = (HDC) wParam;

                        nCtlID = GetWindowLong( hCtlHwnd, GWL_ID );

                        if( nCtlID == IDC_STATIC_BANNERWINDOW )
                        {
                              RotateBannerColors(hCtlHDC, hCtlHwnd, nRandColor, nRandColorSize);
                        }

                        return (LRESULT) hBannerBrush;
                    

              case WM_CTLCOLORLISTBOX:
                    nCtlID = GetWindowLong( (HWND) lParam, GWL_ID );

                    if( nCtlID == IDC_LB_DIFFICULTY )
                    {
                 SetTextColor((HDC) wParam, RGB( 75, 0, 64 ) );
                         SetBkColor( (HDC) wParam, RGB( 255, 128, 0 ));
                    }
                    return (LRESULT) hLbBrush;
         
                    

              /*case WM_PAINT:
                    //InvalidateRect(hDifficultyLB, NULL, TRUE);
                    //UpdateWindow(hDifficultyLB);
                    return 0;*/

          case WM_DESTROY :
                     params.bKill = TRUE;

                     DeleteObject(hBannerBrush);
               DeleteObject(hLbBrush);


               PostQuitMessage (0) ;
               return 0 ;
          }

     return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
}
/*
***************************************************************************************
**
**      Function      :      ThreadBanner(PVOID pvoid)
**
**      Purpose            :      Constantly change the colors in the Vegtetarian Game Banner, which
**                  is a static text box / child window control
**
**      Created            :      Steve Russell  8/98
**
***************************************************************************************
*/
void ThreadBanner(PVOID pvoid)
{

      
      PPARAMS pparams;

      pparams = (PPARAMS) pvoid;

      while(!pparams->bKill)
      {
            InvalidateRect(hBannerWindow, NULL, TRUE);
            UpdateWindow(hBannerWindow);
            SleepEx( 500, FALSE );
      }

      _endthread();
}
/*
**************************************************************************************
**
**      Function      :      RotateBannerColors()
**
**      Purpose            :   Randomly change the colors in the words "The Vegetarian Game" and
**                  the background behind it ( the child window, hBannerWindow, a static
**                  text window
**
**      Created            :      Steve Russell  8/98
**
**************************************************************************************
*/
void RotateBannerColors(HDC hCtlHDC, HWND hCtlHwnd, int nRandColor[], int nRandColorSize )
{
      
      int c;
      
      for( c=0; c <= nRandColorSize; c++ )
            {
                  nRandColor[c]      =      rand()      & 255;
            }


      SetTextColor(  hCtlHDC, RGB(nRandColor[3],nRandColor[4],nRandColor[5])  );
      
      SetBkColor(hCtlHDC, RGB(nRandColor[6],nRandColor[7],nRandColor[8])  );
                                    

}// end function


/*
***************************************************************************************
**                                                END OF FILE      MainProgram.cpp
***************************************************************************************
*/
ASKER CERTIFIED SOLUTION
Avatar of Answers2000
Answers2000

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Answers2000
Answers2000

REASONS FOR THE PROBLEM

1. For every window message you application gets you create new brushes :

     hBannerBrush = CreateSolidBrush( RGB( 0,128,0) );
         hLbBrush = CreateSolidBrush( RGB( 255, 128, 0 ) );

2. These never get deleted and so just eat up GDI (the Windows Graphics subsystem) memory, and hence eventually GDI gets into trouble.  The reason for the funny effects is that GDI probably doesn't have enough memory left to complete any kind of painting operation.

3. Incidentally the reason that the UpdateWindow thing you tried, makes the problem worse, is that it causes you to leak memory more rapidly, as there are more Windows messages.


HOW TO FIX

1. Only create the brushes once, and re-use them.  Places to put this would be
either
(a) In WM_CREATE
or
(b) In Program Start up code (e.g. WinMain)


SUGGESTED CODE CHANGE

1. You want to retain the brush handles between messages (so you don't have to keep creating new ones)

Change
HBRUSH hBannerBrush, hLbBrush;

to
static HBRUSH hBannerBrush ;
static HBRUSH hLbBrush


2. Make sure the brushes are only created once

Move
       
     hBannerBrush = CreateSolidBrush( RGB( 0,128,0) );
         hLbBrush = CreateSolidBrush( RGB( 255, 128, 0 ) );

to underneath
case WM_CREATE:

so that the brushes are only created once when the Window is first created.


3. You clean up code for the brushes is already correct providing you make the changes I've suggested (it's incorrect for your initial version of the code, as most of the brushes than are created are not destroyed)





Avatar of anatta18

ASKER

Once again,  Anwer2000 did more then just answer my direct question.  He gave some background knowledge that I found very valuable.

Eventhough it is obvious, I am new windows programmer so I didn't pick up that local variables in WndProc() are recreated EVERY TIME my program processes a message.

I am concerned that I might be wasting resources.  Would it be smart to declare all of my local variables in WndProc() as static ( the ones that don't need to change values )?

How about declaring hdc handles locally in WndProc()?  Will this overload windows or will realsing all of those DC's as I use them clean things up?
Thanks anatta

I looked at your code quite carefully and you don't seem to be leaking anything else that I can spot.

You don't leak HDCs because you free the one from GetDC with ReleaseDC, and you free the one from beginpaint in endpaint.

In terms of structure I'd suggest
i. Declare variables close to where you need them.  For a WndProc you can do

case WM_SOMEMESSAGE:
{
int x ; /* Some variable */
}
break ;

Only make things Global to the entire WndProc when they need to be (like your brushes)

I find it easier to keep track of things.

ii. Use NULL as a flag to tell you that something has been destroyed,

e.g.
HDC hDC = NULL ; // initial value is NULL meaning it's not created

hDC = GetDC(...etc...) ; // hDC should now have a value


One final point, if you consider using MFC for win apps (I was reluctant to switch over myself after using the SDK for 6 or so years, but then switched a couple of years back, and would never go back), it will do a lot of the clean up etc. for you (CDC class in MFC), so it's harder to make mistakes.  For 9/10 Windows apps, MFC is easier, for 1/10 I still use the API, and the API knowledge helps me spot MFC problems that I wouldn't be aware of otherwise (so this knowledge isn't wasted).

ReleaseDC( hDC ...etc ) ;
hDC = NULL ;

Using assert (in assert.h) you can add debug code to your app to check that you don't access windows resources before you create them, or after you destroy them.


The para beginning "One final..." should have been at the end, oops
Thanks for the tips.

About MFC I agree.   I went nuts trying to learn to make windows programs by studying visual c++ books.  A boss I had suggested studying Charles Petzold first, as everything else is based on it or sits on top of it.   I do have a larger idea of "what the *&()_ is going on :).

However, I studied C++ for three semesters in school and I am a firm believer in OO.

I have a copy of Proisse's "Programming Windows95 with MFC" that I plan to rip into once I get this program finished.   I lost my last job because I didn't know enough windows.  During my time off I read and studied the whole Petzold book.  My program is meant to help me consolidate my SDK skills and to have something to show employers for my time off.  Hopefully, I will have a good chunk of my MFC book done by the time I start working.