
| Last Modification: January 06, 2004 |
Why does my VB client keep crashing when compiled and not in the IDE when I use
an ActiveX Control with a worker thread?
You probably fire events from the worker thread in your control.
Since all ActiveX Controls live in single-threaded apartments, the
event sink your VB client supplies lives in that STA too. VB operates
in apartment model only, hence the pointer for the event sink is in
fact a direct pointer to the object in VB. Hence you are able to call
through this pointer. Unfortunately, by doing so you violate the
COM threading rules - every interface pointer is valid only within
the apartment it is obtained in. Since VB is not thread-safe - you
experience the crash.
Solutions. There are three possible solutions (described in ATL terms, but they are appropriate for straight C++ COM coding too):
IConnectionPointImpl::Advise and Unadvise and forward the
call to the worker thread somehow. The implementation
uses CoMarshalInterThreadInterfaceInStream and the worker
thread uses CoGetInterfaceAndReleaseStream to marshal
the interface pointer to the worker thread. Then the marshaled
pointer is stored in the map instead of the direct pointer from
the client. The worker thread must enter an apartment (MTA
or create new STA). The advantage of this approach is that the
code generated by the ATL connection points wizard doesn't
change. The drawback is that the events must be fired from
the worker thread's apartment only (so if you have multiple
worker threads you better enter the MTA in all of them).
The worker thread is suspended for the duration of the call.
Advise method
is modified to use the Global Interface Table to store the
interface and a cookie is stored in the map instead. Whenever
any thread wants to fire an event, the cookie is used to
temporarily obtain an interface pointer for the current apartment
then the event is fired and the interface pointer is released.
The drawback (with ATL in mind) is that in addition to the
IConnectionPointImpl code, you have to modify the code for
the proxy generated by the ATL wizard. The advantage is that
events can be fired from any apartment. All threads which
fire events must enter an apartment. The thread firing the
event is blocked for the duration of the call.References and samples: