Inter Thread Communication in Apartment Threading Model of ATL Component

WEBINAR: On-demand webcast

How to Boost Database Development Productivity on Linux, Docker, and Kubernetes with Microsoft SQL Server 2017 REGISTER >

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.

Comments

  • Good article

    Posted by BSDahiya on 10/10/2008 05:37am

    Nice tutorial.

    Reply
  • Static.......

    Posted by Legacy on 05/01/2002 12:00am

    Originally posted by: Pardeep

    Hi Dan,
    i think u r also facing the same what i was ... and the solution what u r thinking is a right way , as per Q if u will use MSMQ then i think it would be unneccesary overhead on application , first try to fire the event using the way in dummy window.
    and if ur com object is having there on message map then u can post the message in ur com object routing also.
    But be sure ur API call should be in the same process.
    may help u .......
    pardeep

    Reply
  • static member functions and connection points

    Posted by Legacy on 04/30/2002 12:00am

    Originally posted by: Dan Perrin

    I have a similar problem, and seen as you have worked a bit on this topic, your comments would be appreciatd. 
    
    

    Windowless ATL COM Object, apartment threaded.

    I have a static callback member function that gets fired externally from the RAS API. In the callback function I reference a global pointer that allows me to to fire a connection point event : viz.

    MyMainClass * _pMyMainClass; //global

    MyMainClass::MyMainClass()//constructor
    {
    _pMyMainClass = this;
    }

    VOID WINAPI MyMainClass::MyCallback(DWORD dwSomeValue)
    {
    _pMyMainClass->Fire_MyEvent(dwSomeValue);

    }

    The COM object is used from a test VB app. The problem is I get an exception error from the VB runtime, and I assume the reason is because the static callback is in another thread. I have thought of a few solutions. One is creating a dummy window and doing a postmessage() from the static callback, the other is placing the event on a Q, and then posting a message (I don't know how as yet) to the COM objects message map. This would then generate an event would would allow the Fire_MyEvent() to be fired.

    Any comments on this?

    Thanks

    Dan

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • As all sorts of data becomes available for storage, analysis and retrieval - so called 'Big Data' - there are potentially huge benefits, but equally huge challenges...
  • The agile organization needs knowledge to act on, quickly and effectively. Though many organizations are clamouring for "Big Data", not nearly as many know what to do with it...
  • Cloud-based integration solutions can be confusing. Adding to the confusion are the multiple ways IT departments can deliver such integration...

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date