Fireev.exe Fires Events from a Second Thread
ID: Q157437
|
The information in this article applies to:
-
Microsoft Visual C++, 32-bit Editions, versions 4.0, 4.1, 4.2, 5.0, 6.0
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 INFORMATIONThe following files are available for download from the Microsoft
Download Center. Click the file names below to download the files:
Fireev.exe
For more information about how to download files from the Microsoft
Download Center, please visit the Download Center at the following Web
address
http://www.microsoft.com/downloads/search.asp
and then click How to use the Microsoft Download Center.
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:
- Create your control project.
- 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);
}
- Add any events you wish to fire from the second thread using
ClassWizard.
- 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_USER+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;
}
- 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 OCX
Keywords : kbfile kbole kbCtrl kbMFC kbVC kbVC400 kbVC410 kbVC420 kbVC500 kbVC600 OCX
Version : winnt:4.0,4.1,4.2,5.0,6.0
Platform : winnt
Issue type :
|