Link to home
Start Free TrialLog in
Avatar of TommyN14
TommyN14

asked on

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!
Avatar of AndyAinscow
AndyAinscow
Flag of Switzerland image

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)
Avatar of TommyN14
TommyN14

ASKER

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

Thanks!
ASKER CERTIFIED SOLUTION
Avatar of AndyAinscow
AndyAinscow
Flag of Switzerland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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!!!
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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!
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)
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!
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.
Thanks, Andy!
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.)
Thanks for explaining.
Thanks Andy & Alex!!!