|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):
Unadviseand forward the call to the worker thread somehow. The implementation uses
CoMarshalInterThreadInterfaceInStreamand the worker thread uses
CoGetInterfaceAndReleaseStreamto 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.
Advisemethod 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
IConnectionPointImplcode, 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: