?
Solved

LoadBitmaps doesn't load the bitmap.

Posted on 2006-04-12
18
Medium Priority
?
800 Views
Last Modified: 2013-11-20
Hi,

I have a dialog with a CBitmapButton on it.  I set the CBitmapButton to Owner Draw mode.  I have a function to Load the bitmap like this:


void MyTestDlg::DisplayBitmap(int iMode)
{
   if (iMode == 1)
   {
      m_btn1.LoadBitmaps(IDC_BITMAP1);
   }
   else if (iMode == 2)
   {
      m_btn1.LoadBitmaps(IDC_BITMAP2);
   }
}


OnInitDialog function, i called DisplayBitmap(1), that works fine.  Now I have a thread that constantly call this function and update the button bitmap.  like this:

myThread.h

extern MyTestDlg *myDlg;
UINT myThread(LPVOID pParam)
{
   for (int i = 0; i < 100; i++)
   {
      if (i % 2 == 0)
      {
          myDlg->DisplayBitmap(1);
      }
      else
      {
          myDlg->DisplayBitmap(2);
      }
      ::Sleep(1000);
   }
}

the button doesn't change the bitmap at all.  What am I missing here?

Thank you!
0
Comment
Question by:TommyN14
  • 7
  • 7
  • 2
16 Comments
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16440515
The simplest way would be to have two buttons - one for each bitmap.  Then you can use code like m_btn1.ShowWindow(SW_HIDE);  m_btn2.ShowWindow(SW_SHOW); to show/hide the desired bitmap.  (A button click on either button could call the same function)
0
 
LVL 1

Author Comment

by:TommyN14
ID: 16440750
Well, my application doesn't just have 1 button.  I has 10 different buttons that can update statuses based on the value it received.  And each button has 4 status (4 different colors).  That will take up alot of resources.

I tried this:

void MyTestDlg::DisplayBitmap(int iMode)
{
   if (iMode == 1)
   {
      m_btn1.LoadBitmaps(IDC_BITMAP1_RED);
      m_btn1.SetState(TRUE);
      m_btn1.SetState(FALSE);

      m_btn2.LoadBitmaps(IDC_BITMAP2_RED);
      m_btn2.SetState(TRUE);
      m_btn2.SetState(FALSE);
      .....
   }
   else if (iMode == 2)
   {
      m_btn1.LoadBitmaps(IDC_BITMAP1_GRN);
      m_btn1.SetState(TRUE);
      m_btn1.SetState(FALSE);

      m_btn2.LoadBitmaps(IDC_BITMAP2_GRN);
      m_btn2.SetState(TRUE);
      m_btn2.SetState(FALSE);
   }
}

This will show the bitmaps.  But, everytime it call this function, it increasing the memory usage.  So there's a memory leak here.  I don't know how to free the memory.  What's the best way of programming such an app like this?  Please provide some sample if possible.

Thanks!
0
 
LVL 48

Expert Comment

by:AlexFM
ID: 16442890
Don't call any UI-related function directly from worker thread, this is not allowed in MFC. Instead of this, post user-defined message to the dialog and change bitmaps in message handler.
0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16443134
     m_btn1.LoadBitmaps(IDC_BITMAP1_RED);
      m_btn1.SetState(TRUE);
      m_btn1.SetState(FALSE);
m_btn1.Invalidate();     <<--------------------needed to make the button update



ps.  As Alex said passing CWnd pointers between threads is NOT safe.  Some functions are thread specific.  The correct way to do it is to pass the window handle (GetSafeHwnd() will get you a handle).  Then use the ::PostMessage(hWnd, MY_MESSAGE, wParam, lParam).  In the dialog respond to this message by running your function to modify the buttons.
0
 
LVL 1

Author Comment

by:TommyN14
ID: 16446276
I'm lost.  Could you guys please post me some links to examples or some sample codes?

Thanks!
0
 
LVL 45

Accepted Solution

by:
AndyAinscow earned 320 total points
ID: 16446383
When you create your thread you can pass a pointer to a  variable to it
UINT myThread(LPVOID pParam)
as pParam.  This can be a structure you define or it could just be the handle of the dialog window that has the buttons on it.  You can get that with GetSafeHwnd().

You define your own message eg.
#define MY_MESSAGE_DISPLAY_BITMAP (WM_APP+10)


Now instead of
myDlg->DisplayBitmap(1);
you have code like
::PostMessage(hWnd_myDlg, MY_MESSAGE_DISPLAY_BITMAP, 1, 0);  (or ::PostMessage(hWnd_myDlg, MY_MESSAGE, 2, 0); ....)

In the dialog you have a message map entry to run a function when the dialog receives your custom message

void MyTestDlg::DisplayBitmap(WPARAM wParm, LPARAM lParam)
{
   if (wParam == 1)
   {
      m_btn1.LoadBitmaps(IDC_BITMAP1_RED);
      m_btn1.SetState(TRUE);
      m_btn1.SetState(FALSE);

      m_btn2.LoadBitmaps(IDC_BITMAP2_RED);
      m_btn2.SetState(TRUE);
      m_btn2.SetState(FALSE);
      .....
   }
   else if (wParam == 2)
   {
      m_btn1.LoadBitmaps(IDC_BITMAP1_GRN);
      m_btn1.SetState(TRUE);
      m_btn1.SetState(FALSE);

      m_btn2.LoadBitmaps(IDC_BITMAP2_GRN);
      m_btn2.SetState(TRUE);
      m_btn2.SetState(FALSE);
   }
}
0
 
LVL 1

Author Comment

by:TommyN14
ID: 16447120
Andy,

Thanks for your quickly response.

I have a few questions:
1.  In my CMyTestDlg.cpp, somewhere in OnInitDialog function, I called this

::AfxBeginThread(myThread, GetSafeHwnd(), THREAD_PRIORITY_LOWEST);

Is this how I should be starting the thread?  Is that how you call the GetSafeHwnd()?

2.  Do I define this #define MY_MESSAGE_DISPLAY_BITMAP (WM_APP+10) at the begin of the thread file or in my CMyTestDlg.cpp file?

3.  Where is this variable come from? hWnd_myDlg

4.  How do you create a Message Map entry?

I tried this and it crashed:

in CMyTestDlg.cpp file
#include "myThread.h"

BOOL CMyTestDlg::OnInitDialog()
{
....
   ::AfxBeginThread(myThread, GetSafeHwnd(), THREAD_PRIORITY_LOWEST);
}

void CMyTestDlg::DisplayBitmaps(WPARAM wParam, LPARAM lParam)
{
   if (wParam == 1)
   {
      m_btn1.LoadBitmaps(IDC_BITMAP_RED);
      m_btn1.SetState(TRUE);
      m_btn1.SetState(FALSE);

      m_btn2.LoadBitmaps(IDC_BITMAP_RED);
      m_btn2.SetState(TRUE);
      m_btn2.SetState(FALSE);
 //     .....
   }
   else if (wParam == 2)
   {
      m_btn1.LoadBitmaps(IDC_BITMAP_GRN);
      m_btn1.SetState(TRUE);
      m_btn1.SetState(FALSE);

      m_btn2.LoadBitmaps(IDC_BITMAP2_GRN);
      m_btn2.SetState(TRUE);
      m_btn2.SetState(FALSE);
   }
}

And in myThread.h file

#define MY_MESSAGE_DISPLAY_BITMAP (WM_APP+10)

//extern CMyTestDlg *myDlg;
UINT myThread(LPVOID pParam)
{
   for (int i = 0; i < 100; i++)
   {
      if (i % 2 == 0)
      {
          //myDlg->DisplayIcons(1);
         ::PostMessage((HWND)pParam, MY_MESSAGE_DISPLAY_BITMAP, 1, 0);
      }
      else
      {
          //myDlg->DisplayBitmap(2);
         ::PostMessageA((HWND)pParam, MY_MESSAGE_DISPLAY_BITMAP, 2, 0);
      }
      ::Sleep(1000);
   }
   return 0;
}

Please help!  Thank you!!!
0
 
LVL 48

Assisted Solution

by:AlexFM
AlexFM earned 180 total points
ID: 16447209
1. Yes.
2. In any place where this constant will be available where it is used: for example, in some h-file.
3. myDlg.m_hWnd - I guess this what he means. But you already have pParam for this.
4.
BEGIN_MESSAGE_MAP(...)
...

ON_MESSAGE(MY_MESSAGE_DISPLAY_BITMAP, DisplayBitmaps)   // add this line manually before END_MESSAGE_MAP
END_MESSAGE_MAP(...)

LRESULT CMyTestDlg::DisplayBitmaps(WPARAM wParam, LPARAM lParam)
{
   // your existing code

   return 0;
}

Notice that message handler must return LRESULT.

Check out MFC sample MTRECALC.
0
 
LVL 1

Author Comment

by:TommyN14
ID: 16447374
Thanks Alex!

Now I'm getting this popup error when executing this app.

Debug Assertion Failed!
Program: D:\MyTest\Debug\MyTest.exe
File: winbtn.cpp
Line: 107
For information on how...

Do you have any ideas?

Thanks!
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16448140
Have you loaded the bitmaps into the buttons before attempting to draw them ?
(Call this DisplayBitmaps in the OnInitDialog and before the thread is started to load the bitmaps)
0
 
LVL 1

Author Comment

by:TommyN14
ID: 16471555
Sorry, i was out for a few days.  I was able to get it to work.  Thanks to Andy & Alex.

I'm experiencing problem with LoadBitmaps function.  It seems like everytime i call this function to load a bitmap, memory usage will increase.  Is there a way we can stop memory from increasing?  

Thanks!
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16471931
If you single step into the LoadBitmaps function you will find that it explicitly calls the DeleteObject on each of the four bitmaps (4 states available for a CBitmapButton) that are member variables - so you don't get any memory/resource leaks.

In other words what you are seeing is normal behaviour of windows.


<Is there a way we can stop memory from increasing?  >
You could adopt my original suggestion of numbers of buttons ;-) and just show/hide the buttons.
You could create your own class based on CBitmapButton, load the different bitmaps at one go and swap the bitmaps internally to display the different ones when required.

Both of those would have a larger usage of memory initially but would stop the behaviour you are experiencing.
0
 
LVL 1

Author Comment

by:TommyN14
ID: 16472172
Thanks, Andy!
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16475421
Alex, are you GranMod by any chance or is there someone watching over our shoulders out there?  (There is no question in your profile re the awarding here and you usually post a comment in the thread when not happy.)
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 16475686
Thanks for explaining.
0
 
LVL 1

Author Comment

by:TommyN14
ID: 16479277
Thanks Andy & Alex!!!
0

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

Question has a verified solution.

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

Introduction: Displaying information on the statusbar.   Continuing from the third article about sudoku.   Open the project in visual studio. Status bar – let’s display the timestamp there.  We need to get the timestamp from the document s…
Introduction: Hints for the grid button.  Nested classes, templated collections.  Squash that darned bug! Continuing from the sixth article about sudoku.   Open the project in visual studio. First we will finish with the SUD_SETVALUE messa…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
We’ve all felt that sense of false security before—locking down external access to a database or component and feeling like we’ve done all we need to do to secure company data. But that feeling is fleeting. Attacks these days can happen in many w…
Suggested Courses
Course of the Month15 days, 12 hours left to enroll

850 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