Solved

Painting Problems In The SDK

Posted on 1998-09-02
6
286 Views
Last Modified: 2013-12-03


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
***************************************************************************************
*/
0
Comment
Question by:anatta18
  • 4
  • 2
6 Comments
 
LVL 8

Accepted Solution

by:
Answers2000 earned 100 total points
ID: 1414126
You're leaking brushes.  Code change in the comment below.
0
 
LVL 8

Expert Comment

by:Answers2000
ID: 1414127
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)





0
 

Author Comment

by:anatta18
ID: 1414128
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?
0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 
LVL 8

Expert Comment

by:Answers2000
ID: 1414129
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.


0
 
LVL 8

Expert Comment

by:Answers2000
ID: 1414130
The para beginning "One final..." should have been at the end, oops
0
 

Author Comment

by:anatta18
ID: 1414131
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.
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Suggested Solutions

This tutorial is about how to put some of your C++ program's functionality into a standard DLL, and how to make working with the EXE and the DLL simple and seamless.   We'll be using Microsoft Visual Studio 2008 and we will cut out the noise; that i…
After several hours of googling I could not gather any information on this topic. There are several ways of controlling the USB port connected to any storage device. The best example of that is by changing the registry value of "HKEY_LOCAL_MACHINE\S…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now