Re: HOWTO set up minimal sink object in console COM client?

From: Mateusz Qoskot?= (mateusz_at_loskot.net)
Date: 03/10/05


Date: Wed, 09 Mar 2005 19:44:40 -0800


Hi,

Thank you Alexander for your help.
Below I'm trying to explaining what I've done already.

Please, let me to add a little explanation related to my objectives.
Here is my MyObject declaration:

class ATL_NO_VTABLE CMyObject :
public CWindowImpl<CMyObject>,
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CMyObject, &CLSID_MyObject>,
public ISupportErrorInfo,
public IConnectionPointContainerImpl<CMyObject>,
public CProxy_IMyObjectEvents<CMyObject>,
public IDispatchImpl<IMyObject, &IID_IMyObject, &LIBID_MYOBJECTLib,
/*wMajor =*/ 1, /*wMinor =*/ 0>
{
// ....
STDMETHOD(StartLongTask)(void); // creates worker thread
};

I create worker thread in StartLongTask call (I use _beginthreadex)
so I derive my CMyObject from CWindowImpl because I want to be able to
PostMessage from Thread Procedure to the COM Object in order to fire
events to client.
This is a simple architecture based on the Microsoft solution presented
in this article:

http://support.microsoft.com/kb/q196026/

So, my console based COM object client should be able to response to
events fired
by the COM object above, the same way as VB client does.

In article <ezqqRd0IFHA.1996@TK2MSFTNGP12.phx.gbl> Alexander Nickolov
wrote:
> You start a regular console project, then add minimal ATL
> support - atlbase.h and atlcom.h, with the _Module in between
> in ATL3 (ATL7 doesn't need it anymore).

Yes, I have it declared in stdafx.h of my client project:

#include <atlbase.h>
extern CComModule _Module; // declared in main.cpp of the client
#include <atlcom.h>

Then in main.cpp file of my client (there is also _tmain app entry
point)
I have _Module and object map declared:

CComModule _Module;
BEGIN_OBJECT_MAP(ObjectMap) // intencionally left empty
END_OBJECT_MAP()

> Then depending
> on the type of event interface you are suing you either need
> IDispEvent[Simpel]Impl for a dispinterface, or a simle ATL
> object for a COM interface. For the latter you need to derive
> from CComObjectRootEx<CComXxxThreadModel> and
> the event interface you are implementing, then add an interface
> map with that interface, and finally implement the interface
> itself.

So, does it mean sink object is always COM object?
I think so but I'd like to be sure :-)

OK, here is my versiom - dispinterface based (MyObject is main COM
object).
My MYOBJECT.idl contains source interface declared as follows:

dispinterface _IMyObjectEvents
{
// metthods
[id(1)] HRESULT OnTaskStarted([in] long Counter);
}

Then my sink class is declared this way:

class CEventHandler :
public IDispEventImpl<0, CEventHandler, &DIID__IMyObjectEvents,
&LIBID_MYOBJECTLib, 1, 0>
{
void __stdcall OnTaskStarted(long counter)
{ g_bFinish = true; // event handled - set global flag }
BEGIN_SINK_MAP(CEventHandler)
SINK_ENTRY_EX(0, DIID__IMyObjectEvents, 1, OnTaskStarted)
END_SINK_MAP()
};

> You use AtlAdvise/AtlUnadvise to attach/detach your
> sink object (for the dispinterface sink you use DispEventAdvise
> instead).

Yes, I use DispEventAdvise as my sink is derived
from IDispEventImpl and dispinterface.

> You use CComObject<your_sink>::CreateInstance
> to create an instance of your sink object.
> Beware your object
> has refernce count of zeor when created so you should imediately
> AddRef and Release after finished advising (to take care of
> proper disposal in case of failure).
> Use GetUnknown() to get
> your sink object's IUnknown pointer (to pass to AtlAdvise).
>

Hm, in one of examples on m.p.vc.atl group I found object creation
using "new" operator withouth any refs counting (add/relese) like this:

CEventHandler * pSink = new CEventHandler();
pSink->DispEventAdvise(spUnk);

But it seems very suspicious to me.
So, now I know I have to create sink object as common COM object
in COM way. Right?

OK, thanks for giving me such great light to my COM knowledge :-)

Below I put my _tmain() function of my console-basd COM client.
I have one more problem with message map
Could you be so kind and review my code below and explain
things marked in comments?

// COM _Module declared above
// COM object map declared above
//
int _tmain(int argc, _TCHAR* argv[])
{
// globals
g_hInstance = NULL;
g_bFinish = FALSE;

std::cout << "INITIALIZE" << std::endl;

// ##### Here I use COINIT_MULTITHREADED, is that correct for my COM
// object above?
// I think I could use apartment too, right?
HRESULT hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
_ASSERTE(SUCCEEDED(hr));

{ // start of COM object life block

std::cout << " INIT" << std::endl;
g_hInstance = ::GetModuleHandle(NULL);
_Module.Init(ObjectMap, g_hInstance, &GUID_NULL);

IMyObjectPtr spMyObj;
spMyObj.CreateInstance(CLSID_MyObject);
IUnknownPtr spUnk = spMyObj;

//CEventHandler * pSink = new CEventHandler(); //
CComObject<CEventHandler> * pSink;
hr = CComObject<CEventHandler>::CreateInstance(&pSink)
_ASSERTE(SUCCEEDED(hr));

pSink->AddRef();
pSink->DispEventAdvise(spUnk);

std::cout << " TASK" << std::endl;
spMyObj->StartLongTask(); // creates worker thread

// ##### Here is my problem, when I uncomment
// all those *Message calls, this loop hangs in
// GetMessage. I'm really confused if I should use real message
// loop here, should I? This is console app, so it has no window :-)
// but COM object has its own message loop implemented by CWindowImpl
// so I think here should be only non-message-loop waiting for
// flag set by event handler (waiting for event).
// Is it good solution?

MSG msg;
while (!g_bFinish) // && GetMessage(&msg, NULL, 0, 0))
{
//TranslateMessage(&msg);
if (g_bFinish) // event handler sets this flag
std::cout << " FINISH" << std::endl;
//DispatchMessage(&msg);
}

pSink->DispEventUnadvise(spUnk);
pSink->Release();
std::cout << " END" << std::endl;

_Module.Term();
std::cout << " TERM" << std::endl;

} // end of COM object life block

::CoUninitialize();
std::cout << "UNINITIALIZE" << std::endl;

return 0;
} // end of _tmain()

Uff, that was my another long post on this group>
Thank you very much for your help.

Greets

--
Mateusz Łoskot
e-mail: mateusz at loskot dot net
www:    http://loskot.net/about/contact/