Solved

Painting Problems In The SDK

Posted on 1998-09-02
6
298 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
[X]
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
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
Salesforce Has Never Been Easier

Improve and reinforce salesforce training & adoption using WalkMe's digital adoption platform. Start saving on costly employee training by creating fast intuitive Walk-Thrus for Salesforce. Claim your Free Account Now

 
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

Secure Your Active Directory - April 20, 2017

Active Directory plays a critical role in your company’s IT infrastructure and keeping it secure in today’s hacker-infested world is a must.
Microsoft published 300+ pages of guidance, but who has the time, money, and resources to implement? Register now to find an easier way.

Question has a verified solution.

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

This article shows how to make a Windows 7 gadget that accepts files dropped from the Windows Explorer.  It also illustrates how to give your gadget a non-rectangular shape and how to add some nifty visual effects to text displayed in a your gadget.…
zlib is a free compression library (a DLL) on which the popular gzip utility is built.  In this article, we'll see how to use the zlib functions to compress and decompress data in memory; that is, without needing to use a temporary file.  We'll be c…
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…
With Secure Portal Encryption, the recipient is sent a link to their email address directing them to the email laundry delivery page. From there, the recipient will be required to enter a user name and password to enter the page. Once the recipient …

730 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