How to start a second thread in an MFC-based ActiveX control to fire events in Visual C++ (157437)
The information in this article applies to:
- Microsoft Visual C++ 4.0
- Microsoft Visual C++ 4.1
- Microsoft Visual C++, 32-bit Enterprise Edition 4.2
- Microsoft Visual C++, 32-bit Enterprise Edition 5.0
- Microsoft Visual C++, 32-bit Enterprise Edition 6.0
- Microsoft Visual C++, 32-bit Professional Edition 4.2
- Microsoft Visual C++, 32-bit Professional Edition 5.0
- Microsoft Visual C++, 32-bit Professional Edition 6.0
- Microsoft Visual C++, 32-bit Learning Edition 6.0
- Microsoft Visual C++ .NET (2002)
- Microsoft Visual C++ .NET (2003)
- Microsoft Visual C++ 2005 Express Edition
This article was previously published under Q157437 Note Microsoft Visual C++ .NET 2002 and Microsoft Visual C++ .NET 2003 support both the managed code model that is provided by the Microsoft .NET Framework and the unmanaged native Microsoft Windows code model. The information in this article applies only to unmanaged Visual C++ code. Microsoft Visual C++ 2005 supports both the managed
code model that is provided by the Microsoft .NET Framework and the unmanaged
native Microsoft Windows code model. 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: Visual C++ 6.0 For
additional information about how to download Microsoft Support files, click the
following article number to view the article in the Microsoft Knowledge Base: 119591 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 security-enhanced servers that help to
prevent any unauthorized changes to the file.
Visual C++ .NET 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: 119591 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 security-enhanced servers that help to
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:
- 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_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;
}
- 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.
Modification Type: | Major | Last Reviewed: | 1/9/2006 |
---|
Keywords: | kbActivexEvents kbinfo kbdownload kbCtrlCreate kbfile kbhowto kbInprocSvr KB157437 kbAudDeveloper |
---|
|