Rimvis
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*)pCon nSubscribe r;
//RequestStatusChanged is my event
pCS->Fire_RequestStatusCha nged(iRequ estID, iStatus);
...
After firing event, I get exception 0x8001010E (Invalid thread).
How can I avoid this?
Thanks in advance.
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*)pCon
//RequestStatusChanged is my event
pCS->Fire_RequestStatusCha
...
After firing event, I get exception 0x8001010E (Invalid thread).
How can I avoid this?
Thanks in advance.
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.
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/CoUnmar shalInterf ace 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_Std GlobalInte rfaceTable , 0,
CLSCTX_INPROC_SERVER,
IID_IGlobalInterfaceTable,
(void**)&g_pGIT);
passing an interface pointer to another apartment is by registering the pointer
g_pGIT->RegisterInterfaceI nGlobal(II D_ISome, pConnSubscriber, &dwCookie);
To unmarshals the object reference (can be called from any apartment in the same process)
g_pGIT->GetInterfaceFromGl obal(dwCoo kie, IID_ISome, (void**)&pConnSubscriber);
hope this helps ..
The GIT is an optimization of CoMarshalInterface/CoUnmar
IGlobalInterfaceTable *g_pGIT = 0;
CoCreateInstance(CLSID_Std
CLSCTX_INPROC_SERVER,
IID_IGlobalInterfaceTable,
(void**)&g_pGIT);
passing an interface pointer to another apartment is by registering the pointer
g_pGIT->RegisterInterfaceI
To unmarshals the object reference (can be called from any apartment in the same process)
g_pGIT->GetInterfaceFromGl
hope this helps ..
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->RegisterInterface InGlobal(p ConnSubscr iber, IID_ISome, &dwCookie);"
instead of
"g_pGIT->RegisterInterface InGlobal(I ID_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->RegisterInterfaceI nGlobal
(this, IID_IConnectionProvider, &dwEventCookie);
but compilers gives me error "error C2594: 'argument' : ambiguous conversions from 'class CConnectionProvider *const ' to 'struct IUnknown *'"
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->RegisterInterface
instead of
"g_pGIT->RegisterInterface
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->RegisterInterfaceI
(this, IID_IConnectionProvider, &dwEventCookie);
but compilers gives me error "error C2594: 'argument' : ambiguous conversions from 'class CConnectionProvider *const ' to 'struct IUnknown *'"
>> g_pGIT->RegisterInterfaceI nGlobal(pC onnSubscri ber, 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(IConnection Provider, (void**)& pCnPrv);
g_pGIT->RegisterInterfaceI nGlobal
(pCnPrv, IID_IConnectionProvider, &dwEventCookie);
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(IConnection
g_pGIT->RegisterInterfaceI
(pCnPrv, IID_IConnectionProvider, &dwEventCookie);
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<CComSingl eThreadMod el>,
public CComCoClass<CConnectionPro vider, &CLSID_ConnectionProvider> ,
public IConnectionPointContainerI mpl<CConne ctionProvi der>,
public IDispatchImpl<IConnectionP rovider, &IID_IConnectionProvider, &LIBID_RSCONMGRLib>,
public CProxy_IConnectionProvider Events< CConnectionProvider >
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<CComSingl
public CComCoClass<CConnectionPro
public IConnectionPointContainerI
public IDispatchImpl<IConnectionP
public CProxy_IConnectionProvider
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(CL SID_Connec tionProvid er, CConnectionProvider)
given the interface pointer (pUnk) heres how to get to the class pointer now
CConnectionProvider * pObj;
pUnk->QueryInterface(CLSID _Connectio nProvider, (void **)&pObj);
pObj->Fire_RequestStatusCh anged(.... );
im begining to feel that this might not work at all , its worth a try though, and do post your results ...
One way to do it is to use the INTERFACE_MAP, add the following entry to the map
COM_INTERFACE_ENTRY_IID(CL
given the interface pointer (pUnk) heres how to get to the class pointer now
CConnectionProvider * pObj;
pUnk->QueryInterface(CLSID
pObj->Fire_RequestStatusCh
im begining to feel that this might not work at all , its worth a try though, and do post your results ...
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(CConnectionP rovider)
COM_INTERFACE_ENTRY(IConne ctionProvi der)
COM_INTERFACE_ENTRY(IDispa tch)
COM_INTERFACE_ENTRY(IConne ctionPoint Container)
COM_INTERFACE_ENTRY_IMPL(I Connection PointConta iner)
COM_INTERFACE_ENTRY_IID(CL SID_Connec tionProvid er, CConnectionProvider)
END_COM_MAP()
event firing part:
CConnectionProvider * pObj;
g_pGIT->GetInterfaceFromGl obal
(dwEventCookie, IID_IConnectionProvider, (void**)&pConnSubscriber);
pConnSubscriber->QueryInte rface
(CLSID_ConnectionProvider, (void **)&pObj);
pObj->Fire_RequestStatusCh anged(iReq uestID, iStatus);
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(CConnectionP
COM_INTERFACE_ENTRY(IConne
COM_INTERFACE_ENTRY(IDispa
COM_INTERFACE_ENTRY(IConne
COM_INTERFACE_ENTRY_IMPL(I
COM_INTERFACE_ENTRY_IID(CL
END_COM_MAP()
event firing part:
CConnectionProvider * pObj;
g_pGIT->GetInterfaceFromGl
(dwEventCookie, IID_IConnectionProvider, (void**)&pConnSubscriber);
pConnSubscriber->QueryInte
(CLSID_ConnectionProvider,
pObj->Fire_RequestStatusCh
oh well just found out this method wont work for marshalled interface pointers .. :(
ASKER
So, any suggestions? Is there any workaround?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
I tried first solution, it dosn't work, same result, 0x00000000 :(
I'll try the other...
I'll try the other...
ASKER
IT WORKS :)
This KB article was clear, and, above all, worked :o)
Thank you
This KB article was clear, and, above all, worked :o)
Thank you
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.