Link to home
Start Free TrialLog in
Avatar of Krip
Krip

asked on

CDialog::DoModal() in amultithreaded application

Hi,

I have A class derived from CDialog.
At some point in the program a random thread (A socket's threadpool) would create a new instance of that class (I tried heap and stack creation both) and calls DoModal() on it. At some point after it's created, the modal's pumpmessage ends up calling the modal's main window parent's PreTranslate (which is the application window) and when that PreTranslte calls CDialog::PreTransle it crashes in CWnd::AssertValid() just above MS's friendly note:

// Note: if either of the above asserts fire and you are
// writing a multithreaded application, it is likely that
// you have passed a C++ object from one thread to another
// and have used that object in a way that was not intended.
// (only simple inline wrapper functions should be used)
//
// In general, CWnd objects should be passed by HWND from
// one thread to another.  The receiving thread can wrap
// the HWND with a CWnd object by using CWnd::FromHandle.
//
// It is dangerous to pass C++ objects from one thread to
// another, unless the objects are designed to be used in
// such a manner.

I assure you I do nothing of the sort.

So what up???
Avatar of Agarici
Agarici
Flag of Romania image

i think the problem is exactly the one stated by MS comented lines:

even if you think you do not pass any object from one thread to another, this is actualy hapening because of what you said:

>> At some point after it's created, the modal's pumpmessage ends up calling the modal's main window parent's PreTranslate

the problem is the window parent's PreTranslate executes in the coling thread context so the aplication window obj is used in another thread than its own.


from what you described that seems to be the problem


A.
first of all please excuse my bad english.

I want to add something to my previous comment :

I dont  understand how you get calling PreTranslate(Message I suppose ) because it should be a private member function


if you need the app window to do something at a certain moment, you should  'annonce' the window that it has to take certain actions by sending it a message ( even a user defined message if it is necessary) - but that depends on what your program has to do.


A.
Avatar of DanRollins
>> I assure you I do nothing of the sort.

It is *very difficult* to avoid it, even if you are very careful.   CWnd* s are used everywhere in MFC and each one will cause that assert.  

That ASEERT is actually in CWnd::AssertValid, which can be called from numerous locations... so you need to backtrace the CallStack to see what part of your code actually made the all that ended up at that assertion.  Note that PreTranslateMessage function exists inside of several MFC classes.

Part of the problem is that any time you need to communicate with another window (common needs: update a progress bar, change the text of  a static control.. etc) a pointer to a CWnd-derived object is usually involved.

How are you starting the thread?   It might make a difference to use the alternate version of the AfxBeginThread class -- that is, start a U/I thread rather than a worker thread-- it depends upon what you will be doing in the thread.

-- Dan
Avatar of Krip
Krip

ASKER

Hi,

What happens exactly is this:

The thread that 'actually' calls DoModal() is originated in QueueUserAPC(). the only 'window' (or CWnd) thing I do inside that dialog is CenterWindow() fo which I explicitly use a FromHandle() call. The assert transpires in the application's main window's overriden PreTranslateMessage(), the end of which holds a 'return CDialog::PreTranslateMessage()'. The fact is that the CDialog's internals for some reason insist on finding the Dilaog's main window parent (the app) and calling it's PreTranslate.

But forget debugging my code for a second....

The question is simple actually I guess. If one would one to create a modal CDialog in a multithreaded application, how should that CDialog be declared and called??
>>how should that CDialog be declared and called??
The short answer is:  
Always call DoModal() in the main application thread.  Then there are no problems.

If you want to know how a secondary thread could call DoModal(), then the answer would be to make sure that that thread was started as a User Interface thread rather than as a worker thread.

There are several alternative approaches that might solve the immediate problem.   I think that the call to the CDialog-derived object's PreTranslate comes from the WalkPreTranslateTree fn of the CWinThread object.  If so, you could possibly prevent it by repareneting the dialog.

-- Dan
use SendMessage( HWNDMAIN, some msg, ... ) to notify your main window that a dlg has to be displayed, a hwnd may be returned to the calling thread.
Avatar of Krip

ASKER

I Solved this for the meantime and probably for ever, the obvious and crooked way, i.e. msg the main thread to call the relevant function. Once more 'I hate MFC' with all of it's lack of coherency and the snoty "if we didn't think it should be done like this then it shouldn't! so you can either not do it all or design an entire subsysytem to do the work for you"

Thanks for your help, but I'm withdrawing the thread
... exactly what was proposed above by me and !by! Agarici!
ASKER CERTIFIED SOLUTION
Avatar of DanRollins
DanRollins
Flag of United States of America 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
Avatar of Krip

ASKER

Dan,

A. I've used the proposed technique of having the main thread deal with events (through windows messages)  from ToolTip updates to incoming socket messages before. So no this is not an accaptable solution for me. I wanted once and for all to learn how to avoid the ugly code it produces i.e. "adding virtual functions in base-classes that don't belong there/runtime type checking and having to modify older code for compatability". This is not proper OO conduct ;)

I have no intention of rewriting MFC (it seems like an awful lot of work doesn't it??), So anyways
thanks again.

BTW, eventually I'll assign the points if no one dazzles me with a new approach... Ohh and MFC is NOT worth the trouble. There are so many beautiful and simple models for windowing out there (not for windows regrettably), so no need to kiss anything ;)