Solved

Sharing App CMDIFrameWnd with user-interface threads

Posted on 1997-02-22
1
582 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
ID: 1161981

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

DevOps Toolchain Recommendations

Read this Gartner Research Note and discover how your IT organization can automate and optimize DevOps processes using a toolchain architecture.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Unlike C#, C++ doesn't have native support for sealing classes (so they cannot be sub-classed). At the cost of a virtual base class pointer it is possible to implement a pseudo sealing mechanism The trick is to virtually inherit from a base class…
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.

777 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