Link to home
Start Free TrialLog in
Avatar of Rimvis
RimvisFlag for Lithuania

asked on

Fire event from another thread

Hi all.

How can I fire some event from another thread?

I have tried this:

In my COM object's constructor:

...
//pConnSubscriber is global void* variable, available to all threads
pConnSubscriber = this;
...



When I want to fire event in another thread:

...
//CConnectionProvider is my object
CConnectionProvider* pCS = NULL;
pCS = (CConnectionProvider*)pConnSubscriber;
//RequestStatusChanged is my event
pCS->Fire_RequestStatusChanged(iRequestID, iStatus);
...

After firing event, I get exception 0x8001010E (Invalid thread).

How can I avoid this?


Thanks in advance.

Avatar of markesh
markesh

Hello.

You can never fire an event to a VB client from another thread directly. This is prevented. The same is probably true for VC clients that have their marshalling setup to prevent this. The quick and dirty way to solve this problem is to create a window, send a message to that window, and let the window message handler fire the event. There is no need to post the message, just send it.
It depends on what thread model your program supports.
If you registered as APARTMENT model then "markesh" is right (also you can use interface marshalling but it's a painful way).
But if you use FREE model you can fire event into another thread easy. All you need to do is the initialization of COM library with CoInitializeEx(0, COINIT_MULTITHREADED) in BOTH threads (caller and it's pear).
According the code you provided I don't see VB using, but if you did, so "markesh" again is right.
Avatar of ambience
hmmm .. the way you are sharing a global you might be interested in having a look at IGlobalInterfaceTable interface. I suppose that is best suited for the situation under discussion.

The GIT is an optimization of CoMarshalInterface/CoUnmarshalInterface that allows interface pointers to be accessed by all apartments in a process. COM internally implements one GIT per process. The GIT contains marshaled interface pointers that can be efficiently unmarshaled multiple times within the same process.

IGlobalInterfaceTable *g_pGIT = 0;
CoCreateInstance(CLSID_StdGlobalInterfaceTable, 0,
                  CLSCTX_INPROC_SERVER,
                  IID_IGlobalInterfaceTable,
                  (void**)&g_pGIT);

passing an interface pointer to another apartment is by registering the pointer

g_pGIT->RegisterInterfaceInGlobal(IID_ISome, pConnSubscriber, &dwCookie);

To unmarshals the object reference (can be called from any apartment in the same process)

g_pGIT->GetInterfaceFromGlobal(dwCookie, IID_ISome, (void**)&pConnSubscriber);

hope this helps ..
Avatar of Rimvis

ASKER

Ambience:

It looks like your solution is best so far.
However, I have a few problems implementing it.

First of all, it should be
"g_pGIT->RegisterInterfaceInGlobal(pConnSubscriber, IID_ISome, &dwCookie);"
instead of
"g_pGIT->RegisterInterfaceInGlobal(IID_ISome, pConnSubscriber, &dwCookie);"
right? And pConnSubscriber should be IUnknown*, right?

But how shoul I pass pointer to new instance of my interface? Now I'm trying to do so in my class constructor. I've tried
g_pGIT->RegisterInterfaceInGlobal
(this, IID_IConnectionProvider, &dwEventCookie);

but compilers gives me error "error C2594: 'argument' : ambiguous conversions from 'class CConnectionProvider *const ' to 'struct IUnknown *'"
>> g_pGIT->RegisterInterfaceInGlobal(pConnSubscriber, IID_ISome, &dwCookie);

oh yes.

>> And pConnSubscriber should be IUnknown*, right?

HRESULT RegisterInterfaceInGlobal(
  IUnknown *pUnk,  //Pointer to interface of type riid of object
                   //containing global interface
  REFIID  riid,    //IID of the interface to be registered
  DWORD *pdwCookie //Supplies a pointer to the cookie that provides
                   //a caller in another apartment access to the
                   //interface pointer
);

>> Now I'm trying to do so in my class constructor

Its not a good idea at all to do it in the constructor (your object dont even exist as yet), the ideal place is FinalConstruct.

>> CConnectionProvider *const ' to 'struct IUnknown *'"

for the ambiguous conversion problem you can try the following

IConnectionProvider* pCnPrv = 0;
QueryInterface(IConnectionProvider, (void**)& pCnPrv);
g_pGIT->RegisterInterfaceInGlobal
(pCnPrv, IID_IConnectionProvider, &dwEventCookie);

Avatar of Rimvis

ASKER

OK, I managed to register interface in GIT

But how do I call event firing method?

This method is generated by VS wizard and is member of event proxy class.

Declaration of my class:

class ATL_NO_VTABLE CConnectionProvider :
     public CComObjectRootEx<CComSingleThreadModel>,
     public CComCoClass<CConnectionProvider, &CLSID_ConnectionProvider>,
     public IConnectionPointContainerImpl<CConnectionProvider>,
     public IDispatchImpl<IConnectionProvider, &IID_IConnectionProvider, &LIBID_RSCONMGRLib>,
     public CProxy_IConnectionProviderEvents< CConnectionProvider >

hehe , i knew this would be a problem, you need to get to the this pointer given the interface pointer.

One way to do it is to use the INTERFACE_MAP, add the following entry to the map

COM_INTERFACE_ENTRY_IID(CLSID_ConnectionProvider, CConnectionProvider)

given the interface pointer (pUnk) heres how to get to the class pointer now

CConnectionProvider * pObj;
pUnk->QueryInterface(CLSID_ConnectionProvider, (void **)&pObj);
pObj->Fire_RequestStatusChanged(....);


im begining to feel that this might not work at all , its worth a try though, and do post your results ...
Avatar of Rimvis

ASKER

>> One way to do it is to use the INTERFACE_MAP
Do you mean COM_MAP?

I tried your solution, but after QueryInterface pObj becomes 0x00000000

My COM_MAP now looks like this:

BEGIN_COM_MAP(CConnectionProvider)
     COM_INTERFACE_ENTRY(IConnectionProvider)
     COM_INTERFACE_ENTRY(IDispatch)
     COM_INTERFACE_ENTRY(IConnectionPointContainer)
     COM_INTERFACE_ENTRY_IMPL(IConnectionPointContainer)
     COM_INTERFACE_ENTRY_IID(CLSID_ConnectionProvider, CConnectionProvider)
END_COM_MAP()


event firing part:

CConnectionProvider * pObj;    
g_pGIT->GetInterfaceFromGlobal
     (dwEventCookie, IID_IConnectionProvider, (void**)&pConnSubscriber);
pConnSubscriber->QueryInterface
     (CLSID_ConnectionProvider, (void **)&pObj);
pObj->Fire_RequestStatusChanged(iRequestID, iStatus);

oh well just found out this method wont work for marshalled interface pointers .. :(
Avatar of Rimvis

ASKER

So, any suggestions? Is there any workaround?
ASKER CERTIFIED SOLUTION
Avatar of ambience
ambience
Flag of Pakistan image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Rimvis

ASKER

I tried first solution, it dosn't work, same result, 0x00000000 :(

I'll try the other...
Avatar of Rimvis

ASKER

IT WORKS :)
This KB article was clear, and, above all, worked :o)
Thank you