KnowledgeBase Archive

An Archive of Early Microsoft KnowledgeBase Articles

View on GitHub

Q157437: Fireev.exe Fires Events from a Second Thread

Article: Q157437
Product(s): Microsoft C Compiler
Version(s): 4.0,4.1,4.2,5.0,6.0
Operating System(s): 
Keyword(s): kbfile kbole kbActiveX kbCOMt kbCtrl kbCtrlCreate kbInprocSvr kbMFC kbVC400 kbVC410 kbV
Last Modified: 03-JUL-2002

-------------------------------------------------------------------------------
The information in this article applies to:

- Microsoft Visual C++, versions 4.0, 4.1 
- Microsoft Visual C++, 32-bit Enterprise Edition, versions 4.2, 5.0, 6.0 
- Microsoft Visual C++, 32-bit Professional Edition, versions 4.2, 5.0, 6.0 
- Microsoft Visual C++, 32-bit Learning Edition, version 6.0 
- Microsoft Visual C++.NET (2002) 
-------------------------------------------------------------------------------

SUMMARY
=======

MFC-based ActiveX controls typically fire their events from the same thread that
implements the sink interface of the container that the events are being fired
to.

Sometimes, it is desirable to start a second thread in an ActiveX control that
will fire events to the container. Because MFC ActiveX controls use the
Apartment-threading model, special consideration must be taken when firing
events from a secondary thread.

MORE INFORMATION
================

The following files are available for download from the Microsoft Download
Center:

Visual C++ 6.0:

  DownloadDownload Fireev.exe now
  (http://download.microsoft.com/download/vc60pro/File/1/NT4/EN-US/Fireev.exe)

For additional information about how to download Microsoft Support files, click
the following article number to view the article in the Microsoft Knowledge
Base:

  Q119591 How to Obtain Microsoft Support Files from Online Services

Microsoft scanned this file for viruses. Microsoft used the most current
virus-detection software that was available on the date that the file was
posted. The file is stored on secure servers that prevent any unauthorized
changes to the file.

Visual C++ .NET:

  DownloadDownload Fireevvcnet.exe now
  (http://download.microsoft.com/download/VisualStudioNET/SAMPLE/1.24/WIN98MeXP/EN-US/Fireevvcnet.exe)

Release Date: 2-Jul-2002

For additional information about how to download Microsoft Support files, click
the following article number to view the article in the Microsoft Knowledge
Base:

  Q119591 How to Obtain Microsoft Support Files from Online Services

Microsoft scanned this file for viruses. Microsoft used the most current
virus-detection software that was available on the date that the file was
posted. The file is stored on secure servers that prevent any unauthorized
changes to the file.


NOTE: Use the -d option when running Fireev.exe to decompress the file and
Re-create the proper directory structure.

An MFC-based ActiveX control supports events by implementing the
IConnectionPointContainer and IConnectionPoint interfaces, as well as supplying
information about its event interface in its type information. When an MFC-based
ActiveX control is embedded in a container that supports events, that container
will dynamically construct a sink interface that has all of the methods
specified in the control's type information for its event interface. Once the
container constructs its sink interface, it will pass a pointer to that
interface to the ActiveX control. The ActiveX control will use its
implementation of IConnectionPoint to communicate through the now-hooked-up sink
interface that was constructed by the container. This sample will demonstrate
how to call methods of the container's sink interface from a second thread.

The two most important things to consider when starting a new thread to fire
events from an ActiveX control are:

- MFC-based ActiveX controls are in-process objects (implemented in a DLL).

- MFC-based ActiveX controls use the Apartment-threading model.

The Apartment-threading model specifies that all threads that want to use OLE
services must initialize OLE in their thread prior to using OLE services. Also,
if a thread wants to use a pointer to an interface that is either implemented by
a different thread of the same process or has been previously marshaled to a
different thread of the same process, that pointer must be marshaled to the
requesting thread. In the Apartment- threading model, hidden windows are created
to synchronize requests from other threads to the thread being called. This
means that all communication between threads will be done by using hidden
windows and Windows messages in the Apartment model.

There are two possible ways to fire events from a second thread in an ActiveX
control (or any other in-proc server that implements connection points) under
the Apartment-threading model. The first is to make the interface call from the
second thread by calling the event sink's method from the second thread. The
second is to have the second thread post a message to the first thread when it
is ready to fire the event, and have the first thread fire the event.

The first method mentioned above is not the optimal way to fire an event from a
second thread: For the second thread to fire the event, it must make a call on
an interface pointer that is held by the thread that initialized the control.
Therefore, the interface pointer that will be used to fire the event must be
marshaled to the second thread that will cause OLE to set up hidden windows to
communicate between the threads. Windows messages will be used to communicate
between the threads.

The MFC ActiveX control framework is not set up to fire events from a second
thread easily. It is possible to override the default MFC code to marshal the
sink interface pointers to the second thread, but this is not recommended
because Windows is going to create hidden windows and use PostMessage to send
messages between threads anyway. It makes more sense for the second thread to
post its own messages to the first thread and have that thread fire the event.
This code can be set up easily in an MFC ActiveX control. Use the following
steps to add a second thread that fires events to the container in an MFC
ActiveX control:

1. Create your control project.

2. Using ClassWizard, add a method that will start a second thread and return.
  The following code shows a method that starts a second thread and returns
  immediately in an MFC ActiveX control. A global function to serve as the
  second thread's work function is also declared:

     LONG ThreadProc(LPVOID pParam);

     void CFireeventCtrl::StartLengthyProcess()
     {
       DWORD dwID;
       HANDLE threadHandle = CreateThread(NULL,NULL,
                             (LPTHREAD_START_ROUTINE)ThreadProc,
                             (LPVOID)this, NULL, &dwID);
       TRACE("Started the thread %x\n",dwID);
     }

3. Add any events you wish to fire from the second thread using ClassWizard.

4. Define a custom message to be sent from the second thread. Also, add a
  message map entry to the control's message map that will call the
  message-handling function when the custom message is received. This message
  handler will fire the desired event. A sample of how to do this in an MFC
  ActiveX control follows:

        //define a custom message:
        #define WM_THREADFIREEVENT WM_APP+101

        //add an entry for the message to the message map of the control
        BEGIN_MESSAGE_MAP(CFireeventCtrl, COleControl)
        //{{AFX_MSG_MAP(CFireeventCtrl)
        //}}AFX_MSG_MAP
        ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
        ON_MESSAGE(WM_THREADFIREEVENT,OnFireEventForThread) //custom handler
        END_MESSAGE_MAP()

        //add a handler for the custom message that will fire our event
        LRESULT CFireeventCtrl::OnFireEventForThread(WPARAM wParam,
            LPARAM lParam)
        {
          FireLengthyProcessDone();
          return TRUE;
        }

5. In the thread procedure for the second thread, when it's time for the second
  thread to fire the event, post the custom message defined in step 3 back to
  the main thread. The event will be fired. The following code demonstrates:

        LONG ThreadProc(LPVOID pParam)
        {
          Sleep(2000); //simulate lengthy processing
          CFireeventCtrl *pCtrl = (CFireeventCtrl*)pParam;
          PostMessage(pCtrl->m_hWnd,
                      WM_THREADFIREEVENT,
                      (WPARAM)NULL,
                      (LPARAM)NULL);
          return TRUE;
        }

Notice in the sample code above that the window handle of the ActiveX control is
used as the target to which the message from the second thread will be posted.
In most cases, an MFC based ActiveX control will be in-place active when its
methods are called and will have a window handle. It is possible, however, for
an ActiveX control to not have a window handle, such as in the case of a
window-less control. One way to work around this is to create a hidden window
that could be used to communicate between threads. That window could then be
destroyed when the thread terminated. The FIREEV sample has code that is
commented out in its StartLengthyProcess method and ThreadProc thread work
function that demonstrates creating a window wrapped by the CMyWindow class that
serves this purpose. Also notice that PostMessage is used instead of
PostThreadMessage. MFC's message maps are set up to intercept thread messages in
CWinThread derived classes only. Because MFC ActiveX controls are derived from
CWnd, they will not have messages sent with PostThreadMessage routed to them.
Messages sent with PostThreadMessage will have a NULL hWnd.

Additional query words: ActiveX

======================================================================
Keywords          : kbfile kbole kbActiveX kbCOMt kbCtrl kbCtrlCreate kbInprocSvr kbMFC kbVC400 kbVC410 kbVC420 kbVC500 kbVC600 kbhowto kbGrpDSMFCATL 
Technology        : kbVCsearch kbVC400 kbAudDeveloper kbVC410 kbVC420 kbVC500 kbVC600 kbVC32bitSearch kbVCNET kbVC500Search
Version           : :4.0,4.1,4.2,5.0,6.0
Issue type        : kbhowto

=============================================================================

THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Copyright Microsoft Corporation 1986-2002.