您的位置:首页 > 其它

ATL Com 如何用线程产生异步事件

2009-04-29 11:06 239 查看
http://topic.csdn.net/t/20010510/10/116466.html


windowless的ActiveX, 请特别注意最后一段。





SAMPLE:
Firing Events From a Second Thread


---------------------------------------------------------------------

The information in this article applies to:

-
Microsoft Visual C++, 32-bit Edition versions 4.0, 4.1, 4.2

---------------------------------------------------------------------


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
which will fire events to the container. Since
MFC ActiveX controls
use the Apartment threading model,
special consideration must be taken
into account when
firing events from a secondary thread.

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

An MFC based
ActiveX control supports events by implementing the

IConnectionPointContainer and IConnectionPoint interfaces, as well
as
supplying information about it's event interface in
it's 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 it's
event interface. Once the container
constructs it's sink interface, it
will pass a pointer
to that interface to the ActiveX control. The ActiveX

control will use it's 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 in an ActiveX control are:


1. MFC based ActiveX controls are in-process objects
(implemented in a
DLL).
2. 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. This is
because 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. This means
that the interface pointer
that will be used to fire
the event must be marshaled to the second thread

which 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 easily fire
events from
a second thread. It is possible to
override the default MFC code to marshal
the sink
interface pointers to the second thread, but this is not

recommended. The reason this is not recommended is that
since 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 it's
own
messages to the first thread and have that
thread fire the event. This code
can be easily set
up in an MFC ActiveX control. Take the following steps
to
add a second thread which 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 code for a method that starts a second thread
and returns
immediatly in an MFC ActiveX control
is shown below. 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 which 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 is shown below:


//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;
}


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 this:


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 it's 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 windowless 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 which is commented out in it's

StartLengthyProcess method and ThreadProc thread work function
which
demonstrates creating a window which is 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. Since
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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐