LoadBitmaps doesn't load the bitmap.

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!
LVL 1
TommyN14Asked:
Who is Participating?
 
AndyAinscowFreelance programmer / ConsultantCommented:
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
 
AndyAinscowFreelance programmer / ConsultantCommented:
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
 
TommyN14Author Commented:
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
Cloud Class® Course: Microsoft Office 2010

This course will introduce you to the interfaces and features of Microsoft Office 2010 Word, Excel, PowerPoint, Outlook, and Access. You will learn about the features that are shared between all products in the Office suite, as well as the new features that are product specific.

 
AlexFMCommented:
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
 
AndyAinscowFreelance programmer / ConsultantCommented:
     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
 
TommyN14Author Commented:
I'm lost.  Could you guys please post me some links to examples or some sample codes?

Thanks!
0
 
TommyN14Author Commented:
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
 
AlexFMCommented:
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
 
TommyN14Author Commented:
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
 
AndyAinscowFreelance programmer / ConsultantCommented:
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
 
TommyN14Author Commented:
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
 
AndyAinscowFreelance programmer / ConsultantCommented:
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
 
TommyN14Author Commented:
Thanks, Andy!
0
 
AndyAinscowFreelance programmer / ConsultantCommented:
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
 
AndyAinscowFreelance programmer / ConsultantCommented:
Thanks for explaining.
0
 
TommyN14Author Commented:
Thanks Andy & Alex!!!
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.