Solved

Performing multi-threaded listbox updates

Posted on 1998-08-20
5
280 Views
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)
{

      THREADPARAMS* pParams = new THREADPARAMS;
      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,
            pParams);

      // Increment the counter for the next call
      m_ImageToRetrieveCounter++;

      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;
      THREADPARAMS *pParams = new THREADPARAMS;

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

      // Retrieve the name of the image
      CString vImageName = pList->GetAt(pList->FindIndex(
            pParams->vImageToRetrieve));

      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();
      pBM->Attach((HBITMAP)hThumbnail);

      // Add to the list box
      pListBox->AddThumbnail(pBM);

      // 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.)
0
Comment
Question by:DenMan
  • 3
  • 2
5 Comments
 
LVL 2

Accepted Solution

by:
prasanth earned 50 total points
ID: 1321101
Hi,

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

THREADPARAMS *pParams = new THREADPARAMS;
pParams = (THREADPARAMS *)pParam;

But all you really need is

THREADPARAMS *pParams = (THREADPARAMS *)pParam;

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...
0
 

Author Comment

by:DenMan
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?
0
 
LVL 2

Expert Comment

by:prasanth
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
{
public:
    CWebInfo m_info;

    CHospitalWeb();
    ~CHospitalWeb();
    etc.
    etc.
}

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;
   etc.
   etc.
}  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.
0
 

Author Comment

by:DenMan
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?
0
 
LVL 2

Expert Comment

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

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
Need a Shell script to start a service checking the port 6 48
Decoding 32 bit binary streams 6 37
ADO Memory leak with DELPHI 2007 37 170
string initialization in java 11 110
In this article, I'll describe -- and show pictures of -- some of the significant additions that have been made available to programmers in the MFC Feature Pack for Visual C++ 2008.  These same feature are in the MFC libraries that come with Visual …
Introduction: Dynamic window placements and drawing on a form, simple usage of windows registry as a storage place for information. Continuing from the first article about sudoku.  There we have designed the application and put a lot of user int…
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.
Along with being a a promotional video for my three-day Annielytics Dashboard Seminor, this Micro Tutorial is an intro to Google Analytics API data.

911 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

Need Help in Real-Time?

Connect with top rated Experts

24 Experts available now in Live!

Get 1:1 Help Now