Solved

how to use MFC control in thread in mfc

Posted on 2006-11-03
10
745 Views
Last Modified: 2013-11-20
i want to use mfc control in thread suppose i want to use  Button in thread procedure
i start thread by following ways

      m_th1 = AfxBeginThread ( paint1,NULL ) ;      

UINT paint1 ( LPVOID param )
{
GetDlgItem(IDC_BUTTON1)->SetWindowText("yy");   // this give error

return 0

}
plz tell me how
0
Comment
Question by:yogesh28577
  • 4
  • 2
  • 2
  • +2
10 Comments
 
LVL 1

Author Comment

by:yogesh28577
ID: 17865713
i found solution on this page but any other method
http://www.codeguru.com/forum/showthread.php?t=312454
0
 
LVL 4

Accepted Solution

by:
bdunz19 earned 20 total points
ID: 17869930
Hi yogesh,

It seems to me that what you are saying: is how do I access a control from a thread? If so then you should pass the  CWnd/CDialog that contains the control to the thread.

Example:

void CMyDialog::StartMyThread()
{
  m_th1 = AfxBeginThread ( paint1,this ) ;
}

UINT paint1 ( LPVOID param )
{
  CMyDialog *pDlg = (CMyDialog*)param;

  pDlg->GetDlgItem(IDC_BUTTON1)->SetWindowText("yy");   // this give error

  return 0
}

Good luck,
Brandon
0
 
LVL 1

Author Comment

by:yogesh28577
ID: 17872481
how to exit from current thread
suppose i start  thread on two button
on button one i start thread
  m_th1 = AfxBeginThread ( paint1,this ) ;
on button two i start thread same ways
  m_th1 = AfxBeginThread ( paint1,this ) ;
suppose i click on button one thread started successfully
suppose user click on button2 then i want to stop thread of button1
at the time of second button thread start

0
 
LVL 4

Expert Comment

by:bdunz19
ID: 17874521
Well you need to be more specific about what you are trying to acomplish in the threads.

Generally if your thread is not performing something very time consuming you should just wait for the thread to finish. If you have something that is looping in the thread, and performing lots of operations then you could simply have a condition that is called so that you know it's time to break the operation and exit the thread. I use HANDLE objects and CreateEvent/SetEvent to perform operations like that.
0
 
LVL 19

Expert Comment

by:mrwad99
ID: 17877095
>> CMyDialog *pDlg = (CMyDialog*)param;

Hi Brandon :)

I am afraid that you cannot do that.  You cannot pass MFC objects created in one thread to another thread.  Sometimes the code will work, other times it will fail.  The safe objects to pass between MFC threads are HWNDs.

Basically this is because MFC performs a check to ensure that the HWND associated with the MFC control you pass to the thread function exists in the permanent or temporary handle map that the framework uses to convert HWNDs into CWnds.  Handle maps are local to each thread, so one thread cannot see another's map.  The HWND associated with pDlg will not necessarily exist in the handle map of the thread the code above exists in, so the bottom line is the code is not safe.

I think the best way is that which has already been discovered; post a message to the window containing the control which needs updating.  However, one can attempt code like the above, step through and it and find out if it fails or not.  Chances are, an ASSERT_VALID will fail somewhere.

I agree with you regarding the second question though; event objects are definitely the way to solve this.  Simply pass the events in the data passed to each thread and call ::SetEvent or ::ResetEvent as appropriate.

Thanks.
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 4

Expert Comment

by:bdunz19
ID: 17878253
Hi mrwad99,

I understand your concern. Yet then again, I do not. I have never had any sort of problem perfoming such a task. As long as before the Dialog window is closed it is verified that the thread pointing to the dialog is also closed then it seems to me that there should be no non-existant HWND (I generaly create an event before every thread is created that I then wait for the event to be set before exiting the window). It is interesting though, that you point that MFC might not allow such an operation. I don't know much about that. But yet I have never had any code that has failed because that was the case. In most circumstances I perform a SendMessage to the HWND of the dialog. Yet I also have cases when the control I am accessing has thread safty built into it, such as critical sections, so that I can access such methods directly as: GetDlgItem(IDC_BUTTON1)->SetWindowText("yy");.

Thanks for you concern and if you could go into further detail, I might just change the way I code :).

Thanks again mrwad99
0
 
LVL 19

Expert Comment

by:mrwad99
ID: 17879838
Brandon

The best way for you to learn about this is for me to just copy a section of the book "Programming Windows in MFC" by Jeff Prosise.  This text is what my previous comment was based on.  It is quite long, but have a read and see what you think:

____

Calling MFC Member Functions Across Thread Boundaries

Now for the bad news about writing multithreaded MFC applications. As long as threads don't call member functions belonging to objects created by other threads, there are few restrictions on what they can do. However, if thread A passes a CWnd pointer to thread B and thread B calls a member function of that CWnd object, MFC is likely to assert in a debug build. A release build might work fine—but then again, it might not. There's also the possibility that a debug build won't assert but that it won't work properly, either. It all depends on what happens inside the framework when that particular CWnd member function is called. You can avoid a potential minefield of problems by compartmentalizing your threads and having each thread use only those objects that it creates rather than rely on objects created by other threads. But for cases in which that's simply not practical, here are a few rules to go by.

First, many MFC member functions can be safely called on objects in other threads. Most of the inline functions defined in the INL files in MFC's Include directory can be called across thread boundaries because they are little more than wrappers around API functions. But calling a noninline member function is asking for trouble. For example, the following code, which passes a CWnd pointer named pWnd from thread A to thread B and has B call CWnd::GetParent through the pointer, works without any problems:

CWinThread* pThread = AfxBeginThread (ThreadFunc, pWnd);
   
UINT ThreadFunc (LPVOID pParam)
{
    CWnd* pWnd = (CWnd*) pParam;
    CWnd* pParent = pWnd->GetParent ();
    return 0;
}


Simply changing GetParent to GetParentFrame, however, causes an assertion:

CWinThread* pThread = AfxBeginThread (ThreadFunc, pWnd);
   
UINT ThreadFunc (LPVOID pParam)
{
    CWnd* pWnd = (CWnd*) pParam;
    // Get ready for an assertion!
    CWnd* pParent = pWnd->GetParentFrame ();
    return 0;
}

Why does GetParent work when GetParentFrame doesn't? Because GetParent calls through almost directly to the ::GetParent function in the API. Here's how CWnd::GetParent is defined in Afxwin2.inl, with a little reformatting thrown in to enhance readability:

_AFXWIN_INLINE CWnd* CWnd::GetParent () const
{
    ASSERT (::IsWindow (m_hWnd));
    return CWnd::FromHandle (::GetParent (m_hWnd));
}

No problem there; m_hWnd is valid because it's part of the CWnd object that pWnd points to, and FromHandle converts the HWND returned by ::GetParent into a CWnd pointer.

But now consider what happens when you call GetParentFrame, whose source code is found in Wincore.cpp. The line that causes the assertion error is

ASSERT_VALID (this);

ASSERT_VALID calls CWnd::AssertValid, which performs a sanity check by making sure that the HWND associated with this appears in the permanent or temporary handle map the framework uses to convert HWNDs into CWnds. Going from a CWnd to an HWND is easy because the HWND is a data member of the CWnd, but going from an HWND to a CWnd can be done only through the handle maps. And here's the problem: Handle maps are local to each thread and aren't visible to other threads. If thread A created the CWnd whose address is passed to ASSERT_VALID, the corresponding HWND won't appear in thread B's permanent or temporary handle map and MFC will assert. Many of MFC's noninline member functions call ASSERT_VALID, but inline functions don't—at least not in current releases.

Frequently, MFC's assertions protect you from calling functions that wouldn't work anyway. In a release build, GetParentFrame returns NULL when called from a thread other than the one in which the parent frame was created. But in cases in which assertion errors are spurious—that is, in cases in which the function would work okay despite the per-thread handle tables—you can avoid assertions by passing real handles instead of object pointers. For example, it's safe to call CWnd::GetTopLevelParent in a secondary thread if you call FromHandle first to create an entry in the thread's temporary handle map, as shown below.

CWinThread* pThread = AfxBeginThread (ThreadFunc, pWnd->m_hWnd);
   
UINT ThreadFunc (LPVOID pParam)
{
    CWnd* pWnd = CWnd::FromHandle ((HWND) pParam);
    CWnd* pParent = pWnd->GetTopLevelParent ();
    return 0;
}

That's why the MFC documentation warns that windows, GDI objects, and other objects should be passed between threads using handles instead of pointers. In general, you'll have fewer problems if you pass handles and use FromHandle to re-create objects in the destination threads. But don't take that to mean that just any function will work. It won't.

What about calling member functions belonging to objects created from "pure" MFC classes such as CDocument and CRect—classes that don't wrap HWNDs, HDCs, or other handle types and therefore don't rely on handle maps? Just what you wanted to hear: some work and some don't. There's no problem with this code:

CWinThread* pThread = AfxBeginThread (ThreadFunc, pRect);
   
UINT ThreadFunc (LPVOID pParam)
{
    CRect* pRect = (CRect*) pParam;
    int nArea = pRect->Width () * pRect->Height ();
    return 0;
}

But this code will assert on you:

CWinThread* pThread = AfxBeginThread (ThreadFunc, pDoc);
   
UINT ThreadFunc (LPVOID pParam)
{
    CDocument* pDoc = pParam;
    pDoc->UpdateAllViews (NULL);
    return 0;
}

Even some seemingly innocuous functions such as AfxGetMainWnd don't work when they're called from anywhere but the application's primary thread.

The bottom line is that before you go calling member functions on MFC objects created in other threads, you must understand the implications. And the only way to understand the implications is to study the MFC source code to see how a particular member function behaves. Also keep in mind that MFC isn't thread-safe, a subject we'll discuss further later in this chapter. So even if a member function appears to be safe, ask yourself what might happen if thread B accessed an object created by thread A and thread A preempted thread B in the middle of the access.

___
0
 
LVL 22

Expert Comment

by:mahesh1402
ID: 17880062
Note :

In the thread function ( worker thread ) do not try to manipulate the windows of the main thread directly.

The safest way is to post a custom message back to the main thread and have the handler update the controls.

To send a message to a thread use the API call ::PostThreadMessage().

-MAHESH
0
 
LVL 4

Expert Comment

by:bdunz19
ID: 17881837
Mrwad99, thank you for posting such a descriptive and helpful excerpt!

I completly see what you mean now, yet I feel while it is certanly good practice to pass th HWND instead of the Window Class, I believe it not to be neccisary. I was well aware of the problems when calling such functions as GetParentFrame. As you know though, pWnd->GetDlgItem(IDC_BUTTON1) does not have any such probems, and for those purposes I deem it acceptable to pass a pointer to the CWnd class. Now, I don't really like the use of GetDlgItem and I never use it, but for this poor guy (we've highjacked his thread haha) who posted this question, this use is acceptable.

Thanks again Mrwad99 for your input!

-Brandon
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 17887681
Threads should be used if you have lengthy evaluations which can be done asynchronously. It makes no sense to create a thread for a simple button handling what lasts some milliseconds only. A thread is terminated automatically if the thread function returns. To keep a thread running you need to have some kind of infinite loop, e. g.

UINT threadFunc (LPVOID pParam)
{
    CWnd* pParentDlg = (CWnd*)pParam;

    while (true)
    {
         // do what must be done
         ....
         Sleep(100);   // wait a little time to give other threads a chance
    }
    return 0;
}

If you need to terminate a thread by your main thread, you should use a stop flag which was checked in the loop:

UINT threadFunc (LPVOID pParam)
{
    CMyDlg* pParentDlg = (CMyDlg*)pParam;

    while (!pParentDlg->m_stopThread1)
    {
         // do what must be done
         ....
         Sleep(100);   // wait a little time to give other threads a chance
    }
    return 0;
}

Prior to starting the second thread you have to set the stop flag in your parent dialog what will cause thread1 to terminate after a short time.

Regards, Alex



 
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Here is how to use MFC's automatic Radio Button handling in your dialog boxes and forms.  Beginner programmers usually start with a OnClick handler for each radio button and that's just not the right way to go.  MFC has a very cool system for handli…
Introduction: Dialogs (1) modal - maintaining the database. Continuing from the ninth article about sudoku.   You might have heard of modal and modeless dialogs.  Here with this Sudoku application will we use one of each type: a modal dialog …
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.
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…

708 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

13 Experts available now in Live!

Get 1:1 Help Now