Solved

Sharing App CMDIFrameWnd with user-interface threads

Posted on 1997-02-22
1
572 Views
Last Modified: 2008-02-20
Visual C++ V.4.0  and MFC

Is it possible to share the Application's CMDIFrameWnd object with
another User interface thread?  If so, how.  Making member functions thread safe is easy, but how can
I make CMDIFrameWnd data members thread safe?  

i.e. I'm trying to share    theApp.m_pMainWnd <-- CMDIFrameWnd
     in an MDI application.



Scenario:      MDI(Multiple Document Interface) application.

Need to create/open a CMDIChildWnd object in a thread because it's time consuming.  The entire process of creating and opening CMDIChildWnd is slow, not just a component, so a worker thread is not really an option.

If specify, theApp.m_pMainWnd as a new CWinThread member(m_pMainWnd), creation of child window fails on assert(wincore.cpp line 872)     AssertValid function.

void CWnd::AssertValid() const
{
      // check for special wnd??? values
      ASSERT(HWND_TOP == NULL);       // same as desktop
      if (m_hWnd == HWND_BOTTOM)
            ASSERT(this == &CWnd::wndBottom);
      else if (m_hWnd == HWND_TOPMOST)
            ASSERT(this == &CWnd::wndTopMost);
      else if (m_hWnd == HWND_NOTOPMOST)
            ASSERT(this == &CWnd::wndNoTopMost);
      else
      {
            // should be a normal window
            ASSERT(::IsWindow(m_hWnd));

            // should also be in the permanent or temporary handle map
            CHandleMap* pMap = afxMapHWND();
            ASSERT(pMap != NULL);

            CObject* p;
            ASSERT(pMap->LookupPermanent(m_hWnd, p) ||
                  pMap->LookupTemporary(m_hWnd, p));
            ASSERT((CWnd*)p == this);   // must be us                            
line 872

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

***** tried this too, didn't work
//
// It is dangerous to pass C++ objects from one thread to
// another, unless the objects are designed to be used in
// such a manner.
}


yeah, yeah I know, this is
exactly what I'm trying to do
is it possible or do I need another  approch


So, far all the results of this make sense.  Trying to access a windows object created in another thread
is a no-no, so I tried the next approach.


Create a User interface using CRunTimeClass information.   Assign new thread a new CMDIFrameWnd
object to  CWinThread's  m_pMainWnd member.

BOOL UserInterfaceThread::InitInstance()
{
   // create main MDI Frame window

   CMDIFrameWnd *Frame = new CMDIFrameWnd;
   if (! Frame->LoadFrame(IDR_MAINFRAME)) return(FALSE);
   m_pMainWnd = Frame;

   
   // The main window has been initialized, so show and update it.
   Frame->ShowWindow(SW_SHOW);
   Frame->UpdateWindow();

 CMDIChildWnd *pChild = new CMDIChildWnd(....);     // works great

   pChild->ShowWindow(SW_SHOW);      // works great

 // at this point I can see Child window, use all of its menus, features etc.
// the child window is running normally in its own thread

 pChild->DestroyWindow();       // ka-boom chokes on
                                                                           
         ASSERT_KINDOF(CMDIFrameWnd, pMDIFrame);


in   CMDIChildWnd::GetMDIFrame()    member function

hence..

CMDIFrameWnd* CMDIChildWnd::GetMDIFrame()
{
      ASSERT_KINDOF(CMDIChildWnd, this);
      ASSERT(m_hWnd != NULL);
      HWND hWndMDIClient = ::GetParent(m_hWnd);
      ASSERT(hWndMDIClient != NULL);

      CMDIFrameWnd* pMDIFrame;
      pMDIFrame = (CMDIFrameWnd*)CWnd::FromHandle(::GetParent(hWndMDIClient));
      ASSERT(pMDIFrame != NULL);
**** ASSERT FIRES      ASSERT_KINDOF(CMDIFrameWnd, pMDIFrame);
      ASSERT(pMDIFrame->m_hWndMDIClient == hWndMDIClient);
      ASSERT_VALID(pMDIFrame);
      return pMDIFrame;
}
 
What am I doing wrong???

0
Comment
Question by:langer
1 Comment
 
LVL 11

Accepted Solution

by:
mikeblas earned 100 total points
Comment Utility

This question deals squarely with MFC.  Why isn't it in
the MFC Section?

Anyway, you asked: "I make CMDIFrameWnd data members thread safe?"

The easiest way would be to wrap them in accessor functions.  You
should put a CCriticalSection member in your class, and then
call Lock() on the critical section and then Unlock() on the way
out.  Let's assume you call this new member m_critSec.

So, instead of just using member variables like "pObject->m_nCount"
directly, you should add a GetCount() and SetCount() function:

void CYourClass::SetCount(int nCount)
{
   m_critSec.Lock();
   m_nCount = nCount;
   m_critSec.Unlock();
}

int CYourClass::GetCount()
{
   int nRetVal;
   m_critSec.Lock();
   nRetVal = m_nCount;
   m_critSec.Unlock();
   return nRetVal;
}

You also said:

 > I'm trying to share theApp.m_pMainWnd <-- CMDIFrameWnd
 > in an MDI application.

You can't pass a CWnd* pointer owned by one thread to another
thread.  theApp.m_pMainWnd's use as a window wrapper will only
be available in the main thread of your application, then.

So, in subordinate threads, you should get started by doing
something like:

   CWnd* pWnd = CWnd::FromHandle(theApp.m_pMainWnd->m_hWnd);

and using that pWnd in your subordinate thread.  There are lots
of rules which dictate the exact use and function of this kind
of thing; most notably, there will still be times that you can
(and must) use theApp.m_pMainWnd from your subordinate thread.

There are lots of books (and many places in the online documentation) where those rules are described.

.B ekiM




0

Featured Post

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

Join & Write a Comment

Written by John Humphreys C++ Threading and the POSIX Library This article will cover the basic information that you need to know in order to make use of the POSIX threading library available for C and C++ on UNIX and most Linux systems.   [s…
IntroductionThis article is the second in a three part article series on the Visual Studio 2008 Debugger.  It provides tips in setting and using breakpoints. If not familiar with this debugger, you can find a basic introduction in the EE article loc…
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

728 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

9 Experts available now in Live!

Get 1:1 Help Now