Inter Thread Communication in Apartment Threading Model of ATL Component

Environment: VC6, Win NT4, Win 2000

Hi EveryBody,

This time I'm providing a solution to the very basic concept of Inter Thread Communication. In the Apartment threading model of COM, if we have to communicate between two apartments of threads, then we need marshalling of the first thread object to the second thread. If we have to access the primary thread data then we need to marshal the interface pointer from the primary thread to the second thread. This is the case when we are restricted to thread boundries just because we chose the Apartment threading model.

The solution is marshalling. We have to accept that we don't all want to put ourselves into marshalling. Sometime we work to avoid this kind of marshalling code.

The solution that I have implemented is to use window messge routing for inter thread communication. If we are implementiong activeX controls in ATL, then we can use a control's message routing. If we are developing a simple COM object without any window, then we have to create a hidden dummy window to use as a medium.

In my sample code, I have a simple COM object that supports the connection point and apartment threading model. I have one event that I have to fire from a secondry thread of same COM object.

As I told you earlier, I'm using a non-windowed simple COM object. To use window messge routing, you have to create a window using the CWnd class. I'm using my own class that I derived from CWnd with the name of CMyTempWindow. In the Create method I created a window without any style:

HWND CMyTempWindow::Create()
{
  LPCTSTR classname = 0;
  classname = AfxRegisterWndClass(0);

  // Create the window and return it's handle
  CWnd::CreateEx(NULL,classname,NULL,NULL,1,1,1,1,NULL,NULL);
  ASSERT(m_hWnd!=NULL);
  return m_hWnd;

}

In this window class I have implement one my user defined message handler to fire the event from that handler. To implemnt that I define one user define ID as:

#define WM_THREADFIREEVENT WM_USER+101

And it's handler

LRESULT CMyTempWindow::OnFireEventForThread(WPARAM wParam,
                                            LPARAM lParam)
{
  //This is a one-message-does-everything handler.  If 
  // wParam is not set, that means our message has been
  // sent to fire the even. If wParam is set that means
  // we're being asked to destroy ourselves
  if(!wParam)
     m_pControl->Fire_Checkevent();
  else if(wParam==1)
     DestroyWindow();

  return TRUE;
}

Now I have exposed one method of my COM object in which I'mm just creating the window and firing second thread with a window object to post the message on dummy window. That is:

STDMETHODIMP CObjectThread::BeginThread()
{
  AFX_MANAGE_STATE(AfxGetStaticModuleState())

  // TODO: Add your implementation code here
  CMyTempWindow *pWnd = new CMyTempWindow(this);
  HWND hwndTarget = pWnd->Create();
  AfxBeginThread(ThreadProc,(LPVOID)hwndTarget);

  return S_OK;
}

Now the final and most importent issue is the secondary thread procedure in which I'm posting the user defined message to the dummy window in order to fire the event of outgoing interface. The event would be mapped to the client site for notification. In order to avoid marshalling you means you can't fire the secondary thread event directly with an interface pointer because of the apartment model. By using window message routing as the medium to fire event from the primary thread of COM object, if you try to fire the event directly from the secondry thread you will see the difference. What will happen? The boundry of secondary thread will be in the primary thread and at your client site mapped event you will basically have an infinity loop and won't be able to get out of that event. Because an apartment boundry is not there to freely execute the secondary thraed.

To make second thread free and independent, after firing the primary thread event, this is the only way:

UINT ThreadProc(LPVOID pParam)
{
  // Simulate a lengthy process
  Sleep(2000);

  // Use this code if your control has a window
  // CObjectThread *pCtrl = (CObjectThread*)pParam;
  // PostMessage(pCtrl->m_hWnd,WM_THREADFIREEVENT,
  //                      (WPARAM)NULL,(LPARAM)NULL);

  //Use this code if your control doesn't have a window and is
  HWND hWnd =  (HWND)pParam;

// going to use the CMyTempWindow class to send messages 
  // to the control
  for(int i =0; i<10; i++) 
  {
    if(i==4)
    {
       PostMessage( hWnd,
                    WM_THREADFIREEVENT,
                    (WPARAM)NULL,
                    (LPARAM)NULL);
    }
    AfxMessageBox("Trial");
  }

  PostMessage( hWnd, 
               WM_THREADFIREEVENT,
               (WPARAM)1,
               (LPARAM)NULL);
  return 0;
}

In the case where you are using an activeX control and using a control window, just create a user define message handler in the control class as I have done in the dummy window class.

This is one of my tries for getting more knowledge on the apartment threading model and secondary thread issues with events. If I'm lacking somewhere, guidence would be appreciated.

As I stated earlier, I'm using a simple COM object from ATL so to map the event's on the client side you have to refer to an article in MSDN:

Q181845 HOWTO: Create a Sink Interface in MFC-Based COM Client

Thanks

Pardeep.

Downloads

Download source - 84.5 Kb


About the Author

Pardeep Kadian

Be young by heart, sharp by mind.
doesn't matter to have a whisky or wine.
Neither it belong to you nor mine.
Keep learning to always have a shine.