[Last Call] Learn how to a build a cloud-first strategyRegister Now


Performing multi-threaded listbox updates

Posted on 1998-08-20
Medium Priority
Last Modified: 2013-11-20
I have an owner-drawn listbox that displays bitmaps that are retrieved from the web after the box has been displayed.  I display a default image until the image has been downloaded, then replace the "blank" with the real image and start downloading the next image.  (This is quite similar in execution to a standard web browser.)

This function starts the retrieval thread, then is called by the thread when the thread is complete and starts a new thread to retrieve the next image:

long CMyDlg::RetrieveImage(WPARAM vResult, LPARAM l)

      pParams->vImageToRetrieve = m_ImageToRetrieveCounter;
      pParams->pImageList = &(m_pParent->m_pCurrentProc->m_OnlineImageList);
      pParams->pListBox = &m_cThumbListBox;
      pParams->hListBox = m_cThumbListBox.m_hWnd;
      pParams->pWeb = &(m_pParent->m_HospitalWeb);
      pParams->pWnd = this;

      // Are there more images to retrieve?  If not, just stop.
      if (m_ImageToRetrieveCounter >= m_TotalImages)
            return 0;

      // Activate the acquisition thread
      m_pThread = AfxBeginThread(ActivateAcquisitionThread,

      // Increment the counter for the next call

      return 1;

This next function is the thread function.  It retrieves the image and puts it into the list box.

UINT CMyDlg::ActivateAcquisitionThread(LPVOID pParam)
      CJpegDecoder vDecoder; // Will decode the retrieved jpeg
      CImageFunctions vIFunc;
      CBitmap *pBM;
      HBITMAP hThumbnail;
      CPoint vImageDim;
      BYTE *vImage;
      CThumbListBox *pListBox;
      CWnd *pWnd;

      pParams = (THREADPARAMS *)pParam;
      CStringList *pList = pParams->pImageList;

      // Retrieve the name of the image
      CString vImageName = pList->GetAt(pList->FindIndex(

      pListBox = (CThumbListBox *)CWnd::FromHandle((HWND)pParams->hListBox);

      // Retrieve the image, and get the saved file name
      vImageName = pParams->pWeb->GetImage(vImageName);
      if (vImageName == "")
            pParams->pWnd->PostMessage(UWM_THREAD_COMPLETE, -1);
            return -1;

      // Decode the image
      if ((vImage = vDecoder.Decode(vImageName, vImageDim)) == NULL)
            pWnd->PostMessage(UWM_THREAD_COMPLETE, -2);
            return -2;

        // Calls a func that assembles a BITMAP
      if (vDecoder.GetColorSpace() == JCS_GRAYSCALE)
            // Convert to RGB
            hThumbnail = vIFunc.MakeBitmap(vImage, vImageDim, 1, pParams->pWnd);
      else hThumbnail = vIFunc.MakeBitmap(vImage, vImageDim, 3, pParams->pWnd);

      // Make the bitmap and attach it
      pBM = new CBitmap();

      // Add to the list box

      // Post a "Completed" message
      pParams->pWnd->PostMessage(UWM_THREAD_COMPLETE, 1);

      return 1;

If the operations are performed in the main thread, they work fine, but I want to display the list box while the images are loading.  I keep getting errors, though.  With this code, it has trouble deleting the HWND.  The errors seem to vary somewhat, so I assume it has more to do with modifying memory elements that I shouldn't be modifying.  

Does anyone have suggestions on how I can approach this problem more safely?  If there is an alternative that is completely different from what I have shown, please share!

Thanks.  (PS if this question is harder than I think I'll up  the points.  Since every browser does this, though, it seems pretty simple.)
Question by:DenMan
  • 3
  • 2

Accepted Solution

prasanth earned 100 total points
ID: 1321101

Couple of things:
1) Most MFC objects are confined to a single thread. You cannot pass CWnds and other such classes between threads. You can, however, pass around the underlying Windows handle, HWND in the case of CWnds. Then in the "receiving" thread use a CWnd::FromHandle(). You are doing this already with pParams->hListBox, but it looks like you are also passing pParams->pListBox which is basically useless.

2) Instead of pParams->pWnd, pass the underlying HWND as pParams->hWnd or whatever. In ActivateAcquisitionThread you have a CWnd* pWnd but it doesn't seem to have been initialized. Use

CWnd* pWnd = CWnd::FromHandle(pParams->hWnd)

instead. Also, right now

pWnd->PostMessage(UWM_THREAD_COMPLETE, -2);

should be failing because pWnd has not been initialized to anything.

3) In ActivateAcquisitionThread, you have

pParams = (THREADPARAMS *)pParam;

But all you really need is


because the structure has already been allocated; no need to allocate another one and then lose track of it. Also, be sure to free pParams before ActivateAcquisitionThread exits otherwise you'll find yourself with a memory leak.

4) You may or may not need a lock on pParams->pImageList, depending on if it can be changed while any of the ActivateAcquisitionThread threads are running. If the list is simply created once and then all the worker threads are run, you don't need to lock it.

5) I don't know what kind of an object pParams->pWeb is, but it too might need a lock if there is a possibility that is will be changed while one of the ActivateAcquisitionThreads is running.

Hope this helps...

Author Comment

ID: 1321102
Those comments were quite helpful.

pWeb is an object derived from CWnd that includes a CSocket and a variety of variables (such as site name, URL, etc) that relate to interacting with a couple web pages.  I have noticed that passing the HWND allows me to call the functions within the pWeb object, but all the variables are unassigned.  Is there any way to pass the pWeb object to the new thread yet still have access to the previously-assigned member variables?

Expert Comment

ID: 1321103
One way to do this is to put all the info (socket handles, session variables, and what-not) into a separate CWebInfo (or something) class. Then in pWeb's class definition, include a CWebInfo as a member variable: (for example)

class CHospitalWeb : public CWnd
    CWebInfo m_info;


CWebInfo really doesn't even need to be a class. It can be a simply struct:

typedef struct
   CSocket m_socket;
   CString m_username;
   CString m_password;
}  CWebInfo;

And regardless of whether it is a class or a struct, you can pass CWebInfo*'s between threads because they are not derived from CWnd. But again, you may need to put a lock on it if there is a possibility CWebInfo or any of it's member fields might be changed while another thread is reading it.

Hope this helps.

Author Comment

ID: 1321104
Hmmm...I'm still having trouble.  I can pass the CWebInfo * just fine.  It seems that I need to pass the HWND for the CHospitalWeb AND the CWebInfo *.  Then, since the m_Info within pWeb is now unassigned, I need to set those values equal to the CWebInfo *.  The problem is that if I try to set any of pWeb's variables, I get a null exception.  Will locking the CHospitalWeb solve this problem, or is there something else I'm forgetting?

Expert Comment

ID: 1321105
you need to construct a new CWebInfo before you assign it's variables...

Featured Post

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

Question has a verified solution.

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

This is to be the first in a series of articles demonstrating the development of a complete windows based application using the MFC classes.  I’ll try to keep each article focused on one (or a couple) of the tasks that one may meet.   Introductio…
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 Micro Tutorial will teach you how to add a cinematic look to any film or video out there. There are very few simple steps that you will follow to do so. This will be demonstrated using Adobe Premiere Pro CS6.
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.

831 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