LuRen
asked on
[COM &multithread] why is there a exception happened?
I have created a multithread COM with ATL (VC++ 6.0/Sp5). I use the ATL wizard. Here is some detail of the COM:
========================== ======
"New ATL Object..." -> "Objects" -> "Simple Object" -> after input the name of the COM, i set the attributes of the com below:
Threading Model : Apartment (Default)
Interface : Dual (Default)
Aggregation : Yes (Default)
Select "Support ISupportErrorInfo"
Select "Support Connection Points"
don't select "Free Threaded Marshaler"
========================== ========== =======
OK, here is a simple COM created. Now i added methods and event of the COM. just for testing, I added two method:
StartUp(), Shutdown(), and added three event: OnFirstEvt() , OnSecondEvt(), OnThirdEvt().
I defined four message : MSG_FIRST, MSG_SECOND, MSG_THIRD, MSG_QUIT
In StartUp(), I created two thread : DaemonThread, TestThread.
DaemonThread had a message queue to receive the four message (MSG_FIRST, MSG_SECOND, MSG_THIRD, MSG_QUIT). If it
receive MSG_FIRST, it call fire_OnFirstEvt() and the client (maybe UI) can receive the event. MSG_SECOND and MSG_THIRD are in the same way. When it receive MSG_QUIT, the thread is finished. here is some codes of it :
========================== ========== ==========
...
while (1) {
WaitMessage();
while( GetMessage( &msg, NULL, MSG_FIRST, MSG_QUIT ) ) {
switch(msg.message) {
case MSG_FIRST:
lpCOM->Fire_OnFirstEvt();
break;
case MSG_SECOND:
lpCOM->Fire_OnSecondEvt();
break;
case MSG_THIRD:
lpCOM->Fire_OnThirdEvt();
break;
case MSG_QUIT:
return 0;
default:
break;
}
}
...
========================== ========== ==========
TestThread is very simple. It send message to DaemonThread. here is some codes of it :
========================== ========== ==========
...
status = PostThreadMessage( lpDaemonThreadID, MSG_FIRST, (WPARAM)NULL, (LPARAM)NULL );
if (status == FALSE) {
// ...
return -1;
} else
// ...
Sleep(3000);
status = PostThreadMessage( lpDaemonThreadID, MSG_SECOND, (WPARAM)NULL, (LPARAM)NULL );
if (status == FALSE) {
// ...
return -1;
} else
// ...
Sleep(3000);
status = PostThreadMessage( lpDaemonThreadID, MSG_THIRD, (WPARAM)NULL, (LPARAM)NULL );
if (status == FALSE) {
// ...
return -1;
} else
// ...
Sleep(3000);
...
========================== ========== ==========
It is all right Compiling and linking. Now i use this COM in VB. When i debug it in VB IDE, it is all right : all threads of COM work well, all events are fired well . Then i made a exe file. but when i ran the exe file, a exception was happened and exe were stopped. i don't know what's the matter?
is there anybody who has similiar experience?
any ideas or suggestions will be very appreciated.
==========================
"New ATL Object..." -> "Objects" -> "Simple Object" -> after input the name of the COM, i set the attributes of the com below:
Threading Model : Apartment (Default)
Interface : Dual (Default)
Aggregation : Yes (Default)
Select "Support ISupportErrorInfo"
Select "Support Connection Points"
don't select "Free Threaded Marshaler"
==========================
OK, here is a simple COM created. Now i added methods and event of the COM. just for testing, I added two method:
StartUp(), Shutdown(), and added three event: OnFirstEvt() , OnSecondEvt(), OnThirdEvt().
I defined four message : MSG_FIRST, MSG_SECOND, MSG_THIRD, MSG_QUIT
In StartUp(), I created two thread : DaemonThread, TestThread.
DaemonThread had a message queue to receive the four message (MSG_FIRST, MSG_SECOND, MSG_THIRD, MSG_QUIT). If it
receive MSG_FIRST, it call fire_OnFirstEvt() and the client (maybe UI) can receive the event. MSG_SECOND and MSG_THIRD are in the same way. When it receive MSG_QUIT, the thread is finished. here is some codes of it :
==========================
...
while (1) {
WaitMessage();
while( GetMessage( &msg, NULL, MSG_FIRST, MSG_QUIT ) ) {
switch(msg.message) {
case MSG_FIRST:
lpCOM->Fire_OnFirstEvt();
break;
case MSG_SECOND:
lpCOM->Fire_OnSecondEvt();
break;
case MSG_THIRD:
lpCOM->Fire_OnThirdEvt();
break;
case MSG_QUIT:
return 0;
default:
break;
}
}
...
==========================
TestThread is very simple. It send message to DaemonThread. here is some codes of it :
==========================
...
status = PostThreadMessage( lpDaemonThreadID, MSG_FIRST, (WPARAM)NULL, (LPARAM)NULL );
if (status == FALSE) {
// ...
return -1;
} else
// ...
Sleep(3000);
status = PostThreadMessage( lpDaemonThreadID, MSG_SECOND, (WPARAM)NULL, (LPARAM)NULL );
if (status == FALSE) {
// ...
return -1;
} else
// ...
Sleep(3000);
status = PostThreadMessage( lpDaemonThreadID, MSG_THIRD, (WPARAM)NULL, (LPARAM)NULL );
if (status == FALSE) {
// ...
return -1;
} else
// ...
Sleep(3000);
...
==========================
It is all right Compiling and linking. Now i use this COM in VB. When i debug it in VB IDE, it is all right : all threads of COM work well, all events are fired well . Then i made a exe file. but when i ran the exe file, a exception was happened and exe were stopped. i don't know what's the matter?
is there anybody who has similiar experience?
any ideas or suggestions will be very appreciated.
What was the exception code?
Did you call CoInitialize() on any threads you start?
Some questions?
1. How did you pass lpDaemonThreadID to TestThread?
2. Why did you use an 'lp' prefix in lpDaemonThreadID?
The first argument of PostThreadMessage() is a DWORD and not a pointer.
3. What happens to the TestThread after posting the three messages?
Does it terminate or is the showed sequence part of a (message) loop?
If it terminates, when does the crash happen? Between the messages or after?
I had a similar crash recently where a thread terminated and freed resources that have been still in use by the main thread. So, maybe you should post all the code of TestThread.
4. Do you know the system libraries that are linked to your VB program?
I have no experiences with VB, but with C++ you shouldn't mix single-threaded libraries with multi-threaded libraries, as it could crash if a single-threaded function is called from a multithreaded DLL because of wrong linking.
Regards, Alex
1. How did you pass lpDaemonThreadID to TestThread?
2. Why did you use an 'lp' prefix in lpDaemonThreadID?
The first argument of PostThreadMessage() is a DWORD and not a pointer.
3. What happens to the TestThread after posting the three messages?
Does it terminate or is the showed sequence part of a (message) loop?
If it terminates, when does the crash happen? Between the messages or after?
I had a similar crash recently where a thread terminated and freed resources that have been still in use by the main thread. So, maybe you should post all the code of TestThread.
4. Do you know the system libraries that are linked to your VB program?
I have no experiences with VB, but with C++ you shouldn't mix single-threaded libraries with multi-threaded libraries, as it could crash if a single-threaded function is called from a multithreaded DLL because of wrong linking.
Regards, Alex
When you debug in the VB IDE, everything is single threaded - the VB code that is (your C++ code can and often does create it's own threads). But once compiled the VB code can become multi-threaded as well.
For a VB GUI multi-threading is disastrous - that's why you cannot add a multi-threaded OCX control to a VB form.
You did mention that your [potentially] raising events back to the UI, and this is via multiple threads. Don't quite like the sound of this, and obviously VB doesn't either. It should, of couse, queue these calls as it's the main apartment of the process, but we are talking VB ...
Or MootPoint may have a point (no pun intended) and you're missing CoInitialize calls on every apartment created thread.
For a VB GUI multi-threading is disastrous - that's why you cannot add a multi-threaded OCX control to a VB form.
You did mention that your [potentially] raising events back to the UI, and this is via multiple threads. Don't quite like the sound of this, and obviously VB doesn't either. It should, of couse, queue these calls as it's the main apartment of the process, but we are talking VB ...
Or MootPoint may have a point (no pun intended) and you're missing CoInitialize calls on every apartment created thread.
ASKER
> What was the exception code?
when the daemonthread receives the first message, the exception happened. If testthread do not sleep after post first message, maybe the exception will be happened after daemonthread receives second message. (I have tested.)
> Did you call CoInitialize() on any threads you start?
The two threads are started in COM interface method StartUp(). The method is called by VB UI (Maybe a button). Do you mean that i call CoInitialize() in StartUp() before create the thread? I have used CoInitialize() in VC UI before. would you like to explain more detail?
I have tried call CoInitialize() before CreateThread() in StartUp(), and exception is still happened like before.
>1. How did you pass lpDaemonThreadID to TestThread?
>2. Why did you use an 'lp' prefix in lpDaemonThreadID?
>The first argument of PostThreadMessage() is a DWORD and not a pointer.
Yes, you are right. I made a mistake with the prefix. it should be dwDaemonThreadID. i pass the parameter throuth lpParam when creating thread.
>3. What happens to the TestThread after posting the three messages?
> Does it terminate or is the showed sequence part of a (message) loop?
> If it terminates, when does the crash happen? Between the messages or after?
The TestThread is just used to test the mechanism of the COM multithread. after it post the four message (include MSG_QUIT), it terminate.
the crash happens after DaemonThread receives the message MSG_FIRST.
>I had a similar crash recently where a thread terminated and freed resources that have been still >in use by the main thread. So, maybe you should post all the code of TestThread.
All these two threads are just used to test. there is no resource used in them.
>4. Do you know the system libraries that are linked to your VB program?
i don't know. the mechanism of VB is not similiar with VC. The developer needn't to care the libraries. everything is related with component.
>When you debug in the VB IDE, everything is single threaded - the VB code that is (your C++ >code can and often does create it's own threads). But once compiled the VB code can become >multi-threaded as well.
i am sure that the threads are all created right when i debug in VB IDE. And i have used "Process Viewer" , to check wheather there is more thread created when i debugging. the result is 2 thread added.
>For a VB GUI multi-threading is disastrous - that's why you cannot add a multi-threaded OCX >control to a VB form.
I think i did not use multi-threaded OCX control. it is a COM made with ATL. and it has no outline, just a COM Object. After referenced, the app can use it . like this :
' the following is some codes of VB:
private WithEvents objTest as OBJTEST ' OBJTEST is the COM Object i programmed.
>You did mention that your [potentially] raising events back to the UI, and this is via multiple >threads. Don't wuite like the sound of this, and obviously VB doesn't either. It should, of couse, >queue these calls as it's the main apartment of the process, but we are talking VB ...
I think this the way using COM is very useful. For if the app use some hardware, the hardware works and must tell GUI what happend, GUI can decide how to do according to the information. maybe somebody have more good idea, i'd like to know it.
>Or MootPoint may have a point (no pun intended) and you're missing CoInitialize calls on every >apartment created thread.
i am waiting for more idea or suggestions.
when the daemonthread receives the first message, the exception happened. If testthread do not sleep after post first message, maybe the exception will be happened after daemonthread receives second message. (I have tested.)
> Did you call CoInitialize() on any threads you start?
The two threads are started in COM interface method StartUp(). The method is called by VB UI (Maybe a button). Do you mean that i call CoInitialize() in StartUp() before create the thread? I have used CoInitialize() in VC UI before. would you like to explain more detail?
I have tried call CoInitialize() before CreateThread() in StartUp(), and exception is still happened like before.
>1. How did you pass lpDaemonThreadID to TestThread?
>2. Why did you use an 'lp' prefix in lpDaemonThreadID?
>The first argument of PostThreadMessage() is a DWORD and not a pointer.
Yes, you are right. I made a mistake with the prefix. it should be dwDaemonThreadID. i pass the parameter throuth lpParam when creating thread.
>3. What happens to the TestThread after posting the three messages?
> Does it terminate or is the showed sequence part of a (message) loop?
> If it terminates, when does the crash happen? Between the messages or after?
The TestThread is just used to test the mechanism of the COM multithread. after it post the four message (include MSG_QUIT), it terminate.
the crash happens after DaemonThread receives the message MSG_FIRST.
>I had a similar crash recently where a thread terminated and freed resources that have been still >in use by the main thread. So, maybe you should post all the code of TestThread.
All these two threads are just used to test. there is no resource used in them.
>4. Do you know the system libraries that are linked to your VB program?
i don't know. the mechanism of VB is not similiar with VC. The developer needn't to care the libraries. everything is related with component.
>When you debug in the VB IDE, everything is single threaded - the VB code that is (your C++ >code can and often does create it's own threads). But once compiled the VB code can become >multi-threaded as well.
i am sure that the threads are all created right when i debug in VB IDE. And i have used "Process Viewer" , to check wheather there is more thread created when i debugging. the result is 2 thread added.
>For a VB GUI multi-threading is disastrous - that's why you cannot add a multi-threaded OCX >control to a VB form.
I think i did not use multi-threaded OCX control. it is a COM made with ATL. and it has no outline, just a COM Object. After referenced, the app can use it . like this :
' the following is some codes of VB:
private WithEvents objTest as OBJTEST ' OBJTEST is the COM Object i programmed.
>You did mention that your [potentially] raising events back to the UI, and this is via multiple >threads. Don't wuite like the sound of this, and obviously VB doesn't either. It should, of couse, >queue these calls as it's the main apartment of the process, but we are talking VB ...
I think this the way using COM is very useful. For if the app use some hardware, the hardware works and must tell GUI what happend, GUI can decide how to do according to the information. maybe somebody have more good idea, i'd like to know it.
>Or MootPoint may have a point (no pun intended) and you're missing CoInitialize calls on every >apartment created thread.
i am waiting for more idea or suggestions.
> I have tried call CoInitialize() before CreateThread() in StartUp(), and exception is still happened like before.
Again, not sure that this IS your problem, but calling CoInitialize() prior to CreateThread() does NOT initialize COM for the thread you're starting, it HAS to be called WITHIN the thread itself, if that makes any sense.
Again, not sure that this IS your problem, but calling CoInitialize() prior to CreateThread() does NOT initialize COM for the thread you're starting, it HAS to be called WITHIN the thread itself, if that makes any sense.
ASKER
>Again, not sure that this IS your problem, but calling CoInitialize() prior to CreateThread() does >NOT initialize COM for the thread you're starting, it HAS to be called WITHIN the thread itself, if that >makes any sense.
I have tried calling CoInitialize() in thread (first sentence). the problem is still happened.
if you will, you can make a test like this app. if you need, i can send all the codes of the test to you. Thx.
continue to test and waiting for more idea or suggestions.
I have tried calling CoInitialize() in thread (first sentence). the problem is still happened.
if you will, you can make a test like this app. if you need, i can send all the codes of the test to you. Thx.
continue to test and waiting for more idea or suggestions.
>> I have tried calling CoInitialize() in thread (first sentence).
MSDN says:
CoInitialize
Initializes the COM library on the current apartment and identifies the concurrency model as single-thread apartment (STA).
You have to use CoInitializeEx instead.
Hope, that helps
MSDN says:
CoInitialize
Initializes the COM library on the current apartment and identifies the concurrency model as single-thread apartment (STA).
You have to use CoInitializeEx instead.
Hope, that helps
ASKER
>You have to use CoInitializeEx instead.
i have tried CoInitializeEx() and exception is still happened.
BTW, if somebody is interesting in this problem, i'd like send my test projects (VC & VB) to you. please tell me your mail-address first.
I think Multithread is often used in COM/COM+. if you're a COM guru, please tell me some suggrestion. TIA.
i have tried CoInitializeEx() and exception is still happened.
BTW, if somebody is interesting in this problem, i'd like send my test projects (VC & VB) to you. please tell me your mail-address first.
I think Multithread is often used in COM/COM+. if you're a COM guru, please tell me some suggrestion. TIA.
Just noticed lpCOM is accessed from DaemonThread. How is lpCOM passed into the thread, or is it globally accessible perhaps.
Within COM an object can generally only be accessed from within the apartment it was created in (i.e. the creating thread). This can be further constrained as you chose apartment-threading. Your object is thread bound.
Hence why I ask about lpCOM. If you want to touch it from another thread you gotta work it a little.
Within COM an object can generally only be accessed from within the apartment it was created in (i.e. the creating thread). This can be further constrained as you chose apartment-threading. Your object is thread bound.
Hence why I ask about lpCOM. If you want to touch it from another thread you gotta work it a little.
>> i have tried CoInitializeEx() and exception is still happened.
You have to use CoInitializeEx() both in TestThread and DaemonThread, say in all threads ...
>> if somebody is interesting in this problem, i'd like send my test projects (VC & VB) to you
I'm interested, however i'will be not at home til Friday. So, i wouldn't give a quick response. And as i have no VB environment i would need the executable and runtime dlls - if any. If you still want/need help, send all using a .zip or .rar attach to info@sbsweb.info.
>> if you're a COM guru,
I'm not ... but pretty experienced with C++. i would try to debug the C++ dll and might find out where it crashes...
Regards, Alex
You have to use CoInitializeEx() both in TestThread and DaemonThread, say in all threads ...
>> if somebody is interesting in this problem, i'd like send my test projects (VC & VB) to you
I'm interested, however i'will be not at home til Friday. So, i wouldn't give a quick response. And as i have no VB environment i would need the executable and runtime dlls - if any. If you still want/need help, send all using a .zip or .rar attach to info@sbsweb.info.
>> if you're a COM guru,
I'm not ... but pretty experienced with C++. i would try to debug the C++ dll and might find out where it crashes...
Regards, Alex
Similarly I've limited email access ATM. However I've provided an email address within my profile, and, owner permitting, I'll access your test project from there.
Unlike itsmeandnobodyelse I do have VB installed (*shudder*).
Unlike itsmeandnobodyelse I do have VB installed (*shudder*).
ASKER
itsmeandnobodyelse, _ys_
i have send the codes to your mail-box (a RAR file named test.rar). Please check it.
>Just noticed lpCOM is accessed from DaemonThread. How is lpCOM passed into the thread, or is it >globally accessible perhaps.
>Within COM an object can generally only be accessed from within the apartment it was created in >(i.e. the creating thread). This can be further constrained as you chose apartment-threading. Your >object is thread bound.
>Hence why I ask about lpCOM. If you want to touch it from another thread you gotta work it a little.
i pass the pointer of the COM obj to the thread, is it the reason of the exception? but i need use COM obj in thread. Please check the codes. there is not other else pointer passed to the thread.
i have send the codes to your mail-box (a RAR file named test.rar). Please check it.
>Just noticed lpCOM is accessed from DaemonThread. How is lpCOM passed into the thread, or is it >globally accessible perhaps.
>Within COM an object can generally only be accessed from within the apartment it was created in >(i.e. the creating thread). This can be further constrained as you chose apartment-threading. Your >object is thread bound.
>Hence why I ask about lpCOM. If you want to touch it from another thread you gotta work it a little.
i pass the pointer of the COM obj to the thread, is it the reason of the exception? but i need use COM obj in thread. Please check the codes. there is not other else pointer passed to the thread.
ASKER
itsmeandnobodyelse, _ys_
do you receive the test codes?
do you receive the test codes?
Haven't received anything. Trying a different email address this time.
If you wouldn't mind sending to me again, I'll doctor it.
[email address available within my profile]
If you wouldn't mind sending to me again, I'll doctor it.
[email address available within my profile]
Yes, i got it. I'll soon have time to look around.
Regards, Alex
Regards, Alex
ASKER
_ys_, I send you the file again. Please check your mail-box.
Ok, could build eztest.dll - though was already there?
Ok, could install vbtest - whatever it did (don't know much of VB)
However, where is an executable? Double-clicking on any file in vbtest-directory didn't work on my machine.
Made some code analyzing in C++.
>> CoInitializeEx( NULL,COINIT_APARTMENTTHREA DED );
MSDN says:
-------------------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- -----
Apartment-threading, the default model for earlier versions of Windows NT, while allowing for multiple threads of execution, serializes all incoming calls by requiring that calls to methods of objects created by this thread always run on the same thread – the apartment/thread that created them. In addition, calls can arrive only at message-queue boundaries (i.e., only during a PeekMessage, SendMessage, DispatchMessage, etc.). Because of this serialization, it is not typically necessary to write concurrency control into the code for the object, other than to avoid calls to PeekMessage and SendMessage during processing that must not be interrupted by other method invocations or calls to other objects in the same apartment/thread.
-------------------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- -----
However, in your thread there is a call to PostThreadMessage that is different from PeekMessage, SendMessage, DispatchMessage as required from the threading model. Therefore, you should do this:
CoInitializeEx( NULL,COINIT_MULTHITHREADED );
And i recommend this for both threads. if this still doesn't work you should send me an vb executable and a readme how to use it.
Regards, Alex
Ok, could install vbtest - whatever it did (don't know much of VB)
However, where is an executable? Double-clicking on any file in vbtest-directory didn't work on my machine.
Made some code analyzing in C++.
>> CoInitializeEx( NULL,COINIT_APARTMENTTHREA
MSDN says:
--------------------------
Apartment-threading, the default model for earlier versions of Windows NT, while allowing for multiple threads of execution, serializes all incoming calls by requiring that calls to methods of objects created by this thread always run on the same thread – the apartment/thread that created them. In addition, calls can arrive only at message-queue boundaries (i.e., only during a PeekMessage, SendMessage, DispatchMessage, etc.). Because of this serialization, it is not typically necessary to write concurrency control into the code for the object, other than to avoid calls to PeekMessage and SendMessage during processing that must not be interrupted by other method invocations or calls to other objects in the same apartment/thread.
--------------------------
However, in your thread there is a call to PostThreadMessage that is different from PeekMessage, SendMessage, DispatchMessage as required from the threading model. Therefore, you should do this:
CoInitializeEx( NULL,COINIT_MULTHITHREADED
And i recommend this for both threads. if this still doesn't work you should send me an vb executable and a readme how to use it.
Regards, Alex
ASKER
Thx, itsmeandnobodyelse, I will test as your said and tell you the result.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
itsmeandnobodyelse,
i have a try, and the problem is still exist.
_ys_,
thx, i think the solution is right. i will test it and tell you the result.
i have a try, and the problem is still exist.
_ys_,
thx, i think the solution is right. i will test it and tell you the result.
ASKER
_ys_,
i have a try and read some reference. there are some questions i am still confused.
1. how does the CEventProxy.h come from? is it made by the article author?
2. where does the project( ThreadEvents ) use the CComDynamicUnkArray_GIT(de fined in CEventProxy.h)?
3. I compiled the "ThreadEvents" project and success. But when i use the COM ( EventThreadObj of ThreadEvents.dll) in VB, there is some error happening. do you use it in VB? if you can, plese send me your VB test project. (BTW, do you receive my test codes?)
I will continue to have a try, and waiting for your response.
i have a try and read some reference. there are some questions i am still confused.
1. how does the CEventProxy.h come from? is it made by the article author?
2. where does the project( ThreadEvents ) use the CComDynamicUnkArray_GIT(de
3. I compiled the "ThreadEvents" project and success. But when i use the COM ( EventThreadObj of ThreadEvents.dll) in VB, there is some error happening. do you use it in VB? if you can, plese send me your VB test project. (BTW, do you receive my test codes?)
I will continue to have a try, and waiting for your response.
I did receive your test project, and that's what I used. I did not modify the VB code at all. As for the C++ code I modifed it as described in the article.
CEventProxy.h was authored by the article author - at least it sounds that way.
The C++ code I modified is provided below.
ATest.cpp
-----------
unsigned long __stdcall DaemonThread(void *lpParameter)
{
MSG msg;
CATest* lpObj = reinterpret_cast<CATest*>( lpParamete r);
CoInitialize(NULL);
CreateMsgQueue();
// ... rest as is
CoUnitialize();
}
exTestCP.h
-------------
#ifndef _EZTESTCP_H_
#define _EZTESTCP_H_
#include <map>
#include <comdef.h>
#include <atlcom.h>
using namespace std;
class CComDynamicUnkArray_GIT : public CComDynamicUnkArray
{
private:
IGlobalInterfaceTable* GIT;
public:
CComDynamicUnkArray_GIT() : CComDynamicUnkArray()
{
GIT = NULL;
CoCreateInstance( CLSID_StdGlobalInterfaceTa ble,
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IGlobalInterfaceT able),
reinterpret_cast< void** >(&GIT) );
}
~CComDynamicUnkArray_GIT()
{
clear();
if( GIT != NULL )
{
GIT->Release();
}
}
DWORD Add(IUnknown* pUnk);
BOOL Remove(DWORD dwCookie);
CComPtr<IUnknown> GetAt(int nIndex)
{
DWORD dwCookie = (DWORD)CComDynamicUnkArray ::GetAt( nIndex );
if( dwCookie == 0 )
return NULL;
if( CookieMap.find( dwCookie ) == CookieMap.end() )
{
return (IUnknown*)dwCookie;
}
if( GIT != NULL )
{
CComPtr<IUnknown> ppv;
HRESULT hr = GIT->GetInterfaceFromGloba l(
CookieMap[dwCookie], //Cookie identifying the desired global
//interface and its object
__uuidof(IUnknown), //IID of the registered global interface
reinterpret_cast< void** >(&ppv) //Indirect pointer to the desired interface
);
if( hr == S_OK )
{
return ppv;
}
}
return (IUnknown*)dwCookie;
}
void clear()
{
CComDynamicUnkArray::clear ();
if( GIT != NULL )
{
map< DWORD, DWORD >::iterator iter;
for( iter = CookieMap.begin(); iter != CookieMap.end(); ++iter )
{
GIT->RevokeInterfaceFromGl obal(
iter->second //Cookie that was returned from
//RegisterInterfaceInGloba l
);
}
}
CookieMap.clear();
}
protected:
map< DWORD, DWORD > CookieMap;
};
inline DWORD CComDynamicUnkArray_GIT::A dd(IUnknow n* pUnk)
{
DWORD Result = CComDynamicUnkArray::Add( pUnk );
HRESULT hr;
DWORD pdwCookie = 0;
if( GIT != NULL )
{
hr = GIT->RegisterInterfaceInGl obal(
pUnk, //Pointer to interface of type riid of object
//containing global interface
__uuidof(IUnknown), //IID of the interface to be registered
&pdwCookie //Supplies a pointer to the cookie that provides
//a caller in another apartment access to the
//interface pointer
);
}
if( hr == S_OK )
{
CookieMap[Result] = pdwCookie;
}
return Result;
}
inline BOOL CComDynamicUnkArray_GIT::R emove(DWOR D dwCookie)
{
BOOL Result = CComDynamicUnkArray::Remov e( dwCookie );
if( GIT != NULL )
{
if( CookieMap.find( dwCookie ) != CookieMap.end() )
{
GIT->RevokeInterfaceFromGl obal(
CookieMap[dwCookie] //Cookie that was returned from
//RegisterInterfaceInGloba l
);
CookieMap.erase(dwCookie);
}
}
return Result;
}
template <class T>
class CProxy_IATestEvents : public IConnectionPointImpl<T, &DIID__IATestEvents, CComDynamicUnkArray_GIT>
{
//Warning this class may be recreated by the wizard.
public:
VOID Fire_FirstEvt()
{
T* pT = static_cast<T*>(this);
int nConnectionIndex;
int nConnections = m_vec.GetSize();
for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
{
pT->Lock();
CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionInd ex);
pT->Unlock();
CComQIPtr< IDispatch > pDispatch( sp );
if (pDispatch != NULL)
{
DISPPARAMS disp = { NULL, NULL, 0, 0 };
pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, NULL, NULL, NULL);
}
}
}
VOID Fire_SecondEvt()
{
T* pT = static_cast<T*>(this);
int nConnectionIndex;
int nConnections = m_vec.GetSize();
for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
{
pT->Lock();
CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionInd ex);
pT->Unlock();
CComQIPtr< IDispatch > pDispatch( sp );
if (pDispatch != NULL)
{
DISPPARAMS disp = { NULL, NULL, 0, 0 };
pDispatch->Invoke(0x2, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, NULL, NULL, NULL);
}
}
}
VOID Fire_ThirdEvt()
{
T* pT = static_cast<T*>(this);
int nConnectionIndex;
int nConnections = m_vec.GetSize();
for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
{
pT->Lock();
CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionInd ex);
pT->Unlock();
CComQIPtr< IDispatch > pDispatch( sp );
if (pDispatch != NULL)
{
DISPPARAMS disp = { NULL, NULL, 0, 0 };
pDispatch->Invoke(0x3, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, NULL, NULL, NULL);
}
}
}
};
#endif
CEventProxy.h was authored by the article author - at least it sounds that way.
The C++ code I modified is provided below.
ATest.cpp
-----------
unsigned long __stdcall DaemonThread(void *lpParameter)
{
MSG msg;
CATest* lpObj = reinterpret_cast<CATest*>(
CoInitialize(NULL);
CreateMsgQueue();
// ... rest as is
CoUnitialize();
}
exTestCP.h
-------------
#ifndef _EZTESTCP_H_
#define _EZTESTCP_H_
#include <map>
#include <comdef.h>
#include <atlcom.h>
using namespace std;
class CComDynamicUnkArray_GIT : public CComDynamicUnkArray
{
private:
IGlobalInterfaceTable* GIT;
public:
CComDynamicUnkArray_GIT() : CComDynamicUnkArray()
{
GIT = NULL;
CoCreateInstance( CLSID_StdGlobalInterfaceTa
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IGlobalInterfaceT
reinterpret_cast< void** >(&GIT) );
}
~CComDynamicUnkArray_GIT()
{
clear();
if( GIT != NULL )
{
GIT->Release();
}
}
DWORD Add(IUnknown* pUnk);
BOOL Remove(DWORD dwCookie);
CComPtr<IUnknown> GetAt(int nIndex)
{
DWORD dwCookie = (DWORD)CComDynamicUnkArray
if( dwCookie == 0 )
return NULL;
if( CookieMap.find( dwCookie ) == CookieMap.end() )
{
return (IUnknown*)dwCookie;
}
if( GIT != NULL )
{
CComPtr<IUnknown> ppv;
HRESULT hr = GIT->GetInterfaceFromGloba
CookieMap[dwCookie], //Cookie identifying the desired global
//interface and its object
__uuidof(IUnknown), //IID of the registered global interface
reinterpret_cast< void** >(&ppv) //Indirect pointer to the desired interface
);
if( hr == S_OK )
{
return ppv;
}
}
return (IUnknown*)dwCookie;
}
void clear()
{
CComDynamicUnkArray::clear
if( GIT != NULL )
{
map< DWORD, DWORD >::iterator iter;
for( iter = CookieMap.begin(); iter != CookieMap.end(); ++iter )
{
GIT->RevokeInterfaceFromGl
iter->second //Cookie that was returned from
//RegisterInterfaceInGloba
);
}
}
CookieMap.clear();
}
protected:
map< DWORD, DWORD > CookieMap;
};
inline DWORD CComDynamicUnkArray_GIT::A
{
DWORD Result = CComDynamicUnkArray::Add( pUnk );
HRESULT hr;
DWORD pdwCookie = 0;
if( GIT != NULL )
{
hr = GIT->RegisterInterfaceInGl
pUnk, //Pointer to interface of type riid of object
//containing global interface
__uuidof(IUnknown), //IID of the interface to be registered
&pdwCookie //Supplies a pointer to the cookie that provides
//a caller in another apartment access to the
//interface pointer
);
}
if( hr == S_OK )
{
CookieMap[Result] = pdwCookie;
}
return Result;
}
inline BOOL CComDynamicUnkArray_GIT::R
{
BOOL Result = CComDynamicUnkArray::Remov
if( GIT != NULL )
{
if( CookieMap.find( dwCookie ) != CookieMap.end() )
{
GIT->RevokeInterfaceFromGl
CookieMap[dwCookie] //Cookie that was returned from
//RegisterInterfaceInGloba
);
CookieMap.erase(dwCookie);
}
}
return Result;
}
template <class T>
class CProxy_IATestEvents : public IConnectionPointImpl<T, &DIID__IATestEvents, CComDynamicUnkArray_GIT>
{
//Warning this class may be recreated by the wizard.
public:
VOID Fire_FirstEvt()
{
T* pT = static_cast<T*>(this);
int nConnectionIndex;
int nConnections = m_vec.GetSize();
for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
{
pT->Lock();
CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionInd
pT->Unlock();
CComQIPtr< IDispatch > pDispatch( sp );
if (pDispatch != NULL)
{
DISPPARAMS disp = { NULL, NULL, 0, 0 };
pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, NULL, NULL, NULL);
}
}
}
VOID Fire_SecondEvt()
{
T* pT = static_cast<T*>(this);
int nConnectionIndex;
int nConnections = m_vec.GetSize();
for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
{
pT->Lock();
CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionInd
pT->Unlock();
CComQIPtr< IDispatch > pDispatch( sp );
if (pDispatch != NULL)
{
DISPPARAMS disp = { NULL, NULL, 0, 0 };
pDispatch->Invoke(0x2, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, NULL, NULL, NULL);
}
}
}
VOID Fire_ThirdEvt()
{
T* pT = static_cast<T*>(this);
int nConnectionIndex;
int nConnections = m_vec.GetSize();
for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
{
pT->Lock();
CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionInd
pT->Unlock();
CComQIPtr< IDispatch > pDispatch( sp );
if (pDispatch != NULL)
{
DISPPARAMS disp = { NULL, NULL, 0, 0 };
pDispatch->Invoke(0x3, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, NULL, NULL, NULL);
}
}
}
};
#endif
ASKER
_ys_,
Thx. I modified my test codes and it works well.
BTW, if someboby needs the test code, please mail to me( LuRen_12342002@yahoo.com.c n). I'd like to share it.
Thx. I modified my test codes and it works well.
BTW, if someboby needs the test code, please mail to me( LuRen_12342002@yahoo.com.c