views:

128

answers:

1

I have a VB6 COM client that makes calls to an inprocess STA ATL/COM server. One of the Server methods, X, can take a while to finish so I need to be able to cancel it. What I tried was to run the method code in a new thread and include another method, Y, that does a timed WaitForSinleObject. So the client first calls X then goes into a loop calling VB6 DoEvents and then Y until Y indicates that X has finished. This works fine, however, the fly in the ointment is that the X thread also triggers events back to the client via the IConnectionPoint interface. The events get through ok but any GUI calls don't work because, as far as I can glean, the GUI can only work on one thread, ie the main thread.

Is there an obvious way round this using my existing code? Alternatively, please can you suggest other ways I could accomplish this.

Thanks in advance.

+1  A: 

You should always marshal your connection-point calls. When you don't do that, you can call VB code, but it fails in random ways (non-marshaled-objects), or just doesn't work (GUI).

To use marshaling, you have to implement several interfaces (see below).

The other possibility is to convert the asynchronous calls to VB into synchronous 'fetch' calls.

So your code goes from (in C Pseudo code ...) :

while( !wait( X ) )
{
   doevents();
}

to :

while( !wait( X ) )
{
    doevents();
    fetch_async_data();
}


1) Add a marshaller to your class by adding it to the COM_AGGRGATE table :

CComPtr<IUnknown> m_pUnkMarshaler;

BEGIN_COM_MAP(..)
   ...
   COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, m_pUnkMarshaler.p)
END_COM_MAP()

2) Create the marshaller in FinalConstruct()

FinalConstruct()
{
    HRESULT rval = CoCreateFreeThreadedMarshaler( GetControllingUnknown(), &m_pUnkMarshaler.p );
    ...
}

FinalRelease()
{ ...; m_pUnkMarshaler = 0; }

3) Derive your connection point from IConnectionPointImplMT and lock the calls internally when you can fire more then one at the same time.

4) Do not wait indefinitely in the methods of your object, because you can run in deadlocks.

5) Repeat this for every exposed object and connection point.

(This should work, but I haven't tried this in a long time ...)

Christopher
Thanks a lot, I've sorted it. I searched for IConnectionPointImplMT and found this link support.microsoft.com/kb/280512/EN-US/. The supplied code worked fine after changing 2 lines (should be: m_vec.GetUnknown on line 148 and m_vec.GetCookie on line 196). I'm unclear as to why I need to add a marshaller - no mention of this in the link?
Humbleton
The marshaller is needed, when you want to communicate back with your object in the vb6 thread. If it doesn't need that, ... .
Christopher