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?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

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)
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!
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.
CompTIA Network+

Prepare for the CompTIA Network+ exam by learning how to troubleshoot, configure, and manage both wired and wireless networks.

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

Thanks!
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);
   }
}

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
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!!!
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.
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!
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)
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!
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.
TommyN14Author Commented:
Thanks, Andy!
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.)
AndyAinscowFreelance programmer / ConsultantCommented:
Thanks for explaining.
TommyN14Author Commented:
Thanks Andy & Alex!!!
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
System Programming

From novice to tech pro — start learning today.