15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


Q: Multithreaded MDI application...

Duane Sallee -- Duane@teubner.com
Wednesday, November 20, 1996

     Environment: MFC/VC++ 4.0, WinNT 4.0
     
     I am currently writing a MDI client application that connects to 
     multiple servers (via named pipe or socket), receives and displays 
     messages concerning jobs that are being processed by the server.  For 
     every server that is connected to, the messages for that server are 
     displayed in an CListView-derived MDI window.
     
     Because of the fact that the information is coming to my app via named 
     pipe or socket, and the fact that there can be several connections 
     running concurrently, I need to make each MDI operate on it's own 
     thread, but I am having trouble with GUI threads.
     
     My question is this:  I have looked at the MTMDI sample on the VC CD, 
     and understand what is going on, but my situation is slightly 
     different.  In the sample, all the CChildFrame (MDI children) are 
     created as invisible windows which then create a new thread.  This 
     thread in turn creates a CWnd that is a child of the invisible 
     CChildFrame window.  The OnPaint command for the CWnd is handled for 
     all painting.  However, in my case, I want to use a CListView in the 
     MDI child itself (not creating a child to the MDI child window).  Is 
     it possible to set the m_pMainWnd member of my CWinThread-derived 
     class to the MDI child window itself.  This would allow me to use most 
     of the standard MDI functionality.
     
     Here's a summary representation of what I would like to do:
     
     in CChildFrame (MDI Child)
        -> CMDIChildWnd::Create(...)            //base class creation
        -> CMyThread pThread = new CMyThread    //allocate and
        -> pThread->CreateThread(m_hwnd)        //  create a new thread
     
     in CMyThread::InitInstance
        -> m_pMainWnd = m_pParentWnd            //set the mainwnd to
                                                // be the MDI child
     
     
     If there are other samples that pertain to MDI and GUI threads, please 
     let me know.  Any suggestions, help is appreciated.
     
     
     duane sallee
     teubner & associates, inc.




Mike Blaszczak -- mikeblas@nwlink.com
Sunday, December 01, 1996

At 10:37 11/20/96 CST, you wrote:
>     Environment: MFC/VC++ 4.0, WinNT 4.0

>     Because of the fact that the information is coming to my app via named 
>     pipe or socket, and the fact that there can be several connections 
>     running concurrently, I need to make each MDI operate on it's own 
>     thread, but I am having trouble with GUI threads.

The description of your problem doesn't make the requirement that you
say is a "need". In fact, the approach you're suggesting is inefficient
and questionable.
     
>     My question is this:  I have looked at the MTMDI sample on the VC CD, 
>     and understand what is going on, but my situation is slightly 
>     different.  In the sample, all the CChildFrame (MDI children) are 
>     created as invisible windows which then create a new thread.  This 
>     thread in turn creates a CWnd that is a child of the invisible 
>     CChildFrame window. 

Uh, okay.  But if a window is hidden, its children are hidden, too.

>     The OnPaint command for the CWnd is handled for 
>     all painting. 

Which CWnd are you talking about?  The child window?  It won't get any
OnPaint() calls, since it is hidden.

Your analysis of the MTMDI application is off.  A quick run of Spy++ against
the running sample will show you that it creates no hidden windows.

>     However, in my case, I want to use a CListView in the 
>     MDI child itself (not creating a child to the MDI child window). 

If the CListView is "in" the MDI Child frame, it is best for the CListView to
be a child.  That's for two reasons:

  1) A CListView is a CView derivative, and therefore needs to live in
     some CFrameWnd-derived window--like a CMDIChildFrame.

  2) If you don't keep a parent-child relationship, you'll need to write
     a lot of code to manage the parent-child relationship of the windows.
     If the list control window isn't a child of the frame, for example,
     you'll need to move the list control window every time the user moves
     the frame window, or resizes the frame window, or maximizes the frame
     window, or minimizes the frame window.  You'll spend a lot of time
     worrying about focus problems.  Since the popup control window can't
     have focus when the MDI child frame window has focus, the user will 
     be confused because they'll not perceive the control as a separate
     window and won't understand why the caption of the window they have
     active doesn't show that it is active.

I guess point 2) is moot: if you want the MDI child frame to be hidden, the
list view will be hidden, too, and the window will never be shown--so the
user can't possibly minimize it or maximize it or resize it.

>     Is 
>     it possible to set the m_pMainWnd member of my CWinThread-derived 
>     class to the MDI child window itself. 

No.  This will result in more ASSERT messages than you could click "Ignore"
upon in one lifetime.  Threads can only own windows they create.  If you create
a window in one thread and then make it the main window of another thread,
you're going to die in a firey crash.  What benefit do you think this hack
would possibly bring you?

>     This would allow me to use most 
>     of the standard MDI functionality.

How?  What MDI functionality wouldn't be available to you without
that change?
     
>     Here's a summary representation of what I would like to do:
     
>     in CChildFrame (MDI Child)
>        -> CMDIChildWnd::Create(...)            //base class creation
>        -> CMyThread pThread = new CMyThread    //allocate and
>        -> pThread->CreateThread(m_hwnd)        //  create a new thread
     
>     in CMyThread::InitInstance
>        -> m_pMainWnd = m_pParentWnd            //set the mainwnd to
>                                                // be the MDI child

Why?  It's very hard for me to understand why you would want this.
     
>     If there are other samples that pertain to MDI and GUI threads, please 
>     let me know.  Any suggestions, help is appreciated.

I'm afraid that MTMDI is a very poor sample.  It is so widely misunderstood that
I think it might be best if would remove it from the product.

You've spent most of the note talking about what you want your window hierarchy
to be like, and that description confuses me greatly--I can't see any benefit
from it.  Maybe the problem is that your understanding of the MTMDI sample's
hierarchy was flawed because you'd never tried cruising through the windows that
the sample creates with a tool like Spy++.

What you need to focus on when designing with threads is the threads and their
interaction with the rest of the world. A thread owns a window; a window can
not be created without an owning thread. You can't change the thread that owns
a window once it has been created.  I'm not sure what effect you're trying to
gain by fooling around with the m_pMainWnd of a thread the way you're proposing.

If your application needs to do communications stuff, I would strongly suggest
that you:

   1) use the primary thread of your application for all of your UI, including
      the MDI parent frame and all of the MDI children frames _and_ their
content
      views.

   2) spawn a thread with each connection you make.  the spawned thread
might not
      have a UI, unless it would be convenient to have those threads pop up
      windows that aren't direct participants in the MDI framework.

   3) when a communications thread notices something interesting happening, it
      can communicate that fact back to the user interface thread so the update
      can be relayed to the user.

This is probably the cleanest way to go, though I certainly can't put much
weight
behind my own suggestion because you've given esesentially _no_ description of
exactly what you want the _threads_ in your application to be doing.

(3), above, requires a great deal of attention.  If you're doing communication
character by character (or, maybe even datagram by datagram) you'll need to find
a reasonable way for the thread to batch up changes to the state of the
connection
before giving them back to the user interface thread.  The UI thread should be
asked to repaint for every fistful of characters, or maybe even only ever line
of characters.  You might want to give serious consideration to letting your
UI thread poll the communications threads rather than letting the communications
threads fire notifications back to your UI thread--that might make an
automatically
scaling application more reasonable than pciking some arbitrary high water
mark to 
react to.

.B ekiM
http://www.nwlink.com/~mikeblas/
I'm afraid I've become some sort of speed freak.
These words are my own. I do not speak on behalf of Microsoft.





| Вернуться в корень Архива |