We help IT Professionals succeed at work.

Sharing App CMDIFrameWnd with user-interface threads

langer
langer asked
on
Medium Priority
662 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???

Comment
Watch Question

Commented:

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




Not the solution you were looking for? Getting a personalized solution is easy.

Ask the Experts
Access more of Experts Exchange with a free account
Thanks for using Experts Exchange.

Create a free account to continue.

Limited access with a free account allows you to:

  • View three pieces of content (articles, solutions, posts, and videos)
  • Ask the experts questions (counted toward content limit)
  • Customize your dashboard and profile

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.