Painting Problems In The SDK

Posted on 1998-09-02
Medium Priority
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.


( 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;

**                               FUNCTION PROTOTYPES

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

// Process Windows Messages

// 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

      const int nRandColorSize = 9;

    static PARAMS params;

    HFONT hfont;
    HBRUSH hBannerBrush, hLbBrush;
      HWND  hCtlHwnd;
    HDC  hdc, hCtlHDC ;
    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(
                                                              "The Vegetarian Game",
                                                               WS_CHILD | WS_VISIBLE | SS_CENTER,
                                                               (HMENU) IDC_STATIC_BANNERWINDOW,
                                                               ((LPCREATESTRUCT) lParam) -> hInstance,

                        hDifficultyLB = CreateWindow(
                                                               WS_CHILD | WS_VISIBLE | LBS_NOTIFY,
                                                               (HMENU) IDC_LB_DIFFICULTY,
                                                               ((LPCREATESTRUCT) lParam) -> hInstance,

                  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);

                                          cxClient * .10,
                                          cxClient * .79,

                    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);
                    return 0;*/

          case WM_DESTROY :
                     params.bKill = TRUE;


               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;

            InvalidateRect(hBannerWindow, NULL, TRUE);
            SleepEx( 500, FALSE );

**      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
Question by:anatta18
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 2

Accepted Solution

Answers2000 earned 400 total points
ID: 1414126
You're leaking brushes.  Code change in the comment below.

Expert Comment

ID: 1414127

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.


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


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

HBRUSH hBannerBrush, hLbBrush;

static HBRUSH hBannerBrush ;
static HBRUSH hLbBrush

2. Make sure the brushes are only created once

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

to underneath

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)


Author Comment

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?
NEW Veeam Agent for Microsoft Windows

Backup and recover physical and cloud-based servers and workstations, as well as endpoint devices that belong to remote users. Avoid downtime and data loss quickly and easily for Windows-based physical or public cloud-based workloads!


Expert Comment

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

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,

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.


Expert Comment

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

Author Comment

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.

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article describes how to programmatically preset the "Pages per Sheet" option that's available with most printer drivers.   This setting lets you do "n-Up" printing, where two, four, or more pages are printed on each sheet of paper. If your …
A theme is a collection of property settings that allow you to define the look of pages and controls, and then apply the look consistently across pages in an application. Themes can be made up of a set of elements: skins, style sheets, images, and o…
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…
Add bar graphs to Access queries using Unicode block characters. Graphs appear on every record in the color you want. Give life to numbers. Hopes this gives you ideas on visualizing your data in new ways ~ Create a calculated field in a query: …

771 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