Fire event from another thread

Rimvis
Rimvis used Ask the Experts™
on
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.

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®

Commented:
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.
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 ..
CompTIA Security+

Learn the essential functions of CompTIA Security+, which establishes the core knowledge required of any cybersecurity role and leads professionals into intermediate-level cybersecurity jobs.

Author

Commented:
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);

Author

Commented:
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 ...

Author

Commented:
>> 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 .. :(

Author

Commented:
So, any suggestions? Is there any workaround?
im looking into the alternatives , to get rid of the class pointer problem i guess the following can be used (not tried though)..

BEGIN_COM_MAP(CConnectionProvider)
    ....
    COM_INTERFACE_ENTRY_FUNC(CLSID_ConnectionProvider,0,GetCls)
END_COM_MAP()

static HRESULT WINAPI GetCls(void* pv, REFIID riid, LPVOID* ppv, DWORD dw)
{
    *ppv = pv;
    return S_OK;
}

but i seriously doubt that this will solve the problems (maybe you can give it a try), if it wont work then plz have a look at the following alternative

http://support.microsoft.com/default.aspx?scid=KB;en-us;q280512

Author

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

I'll try the other...

Author

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

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial