Re: return com object from inside
Hi!
I think that this:
IFooPtr foo(__uuidof(CFoo));
*pRetVal = foo.Detach();
CComObject<CFoo> *pFoo = (CComObject<CFoo> *)(*pRetVal);
works only if you actually have CComObject<CFoo>* in pRetVal. This is
not true in general, because
IFooPtr foo(__uuidof(CFoo))
can, depending on your COM setup, give you even an interface (proxy)
to an object on another machine, or simply an interface pointer to an
object in a different apartment. Also, I find that more often than not
I don't have CLSID for IFoo. No need, because it's internal to my COM
server. I think that using __uuidof(CFoo) is a bad form - this is IMO
reserved for stuff coming out of MIDL-, or compiler COM support-
generated code.
In this situation, I like to do the following:
BarImpl.cpp:
extern void CreateFoo(IFoo** ppResult, params);
STDMETHOD CBarImpl::get_Foo(IFoo ** pRetVal)
{
try
{
CreateFoo(pRetVal, params used to create and initialize CFooImpl);
return S_OK;
}
EXCEPTION_TO_COM_ERROR(CLSID, IID)
}
FooImpl.cpp:
void CreateFoo(IFoo** ppResult, params)
{
typedef CComObject<CFooImpl> CObjType;
CObjType* pObj = NULL;
CHECK_HR(CObjType::CreateInstance(&pObj));
std::auto_ptr<CObjType> PNewObject(pObj);
PNewObject->Initialize(params);
CHECK_HR(PNewObject->QueryInterface(ppResult));
PNewObject.release();
}
Why like that?
1. CreateFoo is to keep dependencies minimal. CBarImpl does not know
of CFooImpl. To top that, if CBarImpl and CFooImpl need to work
together over what is supported IFoo and IBar, I use "private
interfaces". Good people here told me it's a good approach and has
served me perfectly so far.
2. CComObject<>::CreateInstance is AFAIK, the method ATL uses to
create object when. I want my own implementation of IFoo, so I do what
ATL would do internally to create one.
3. I think that there's no C++ without exceptions, ATL code included.
So for me, all errors go through exceptions, hence:
3.1. COM boundary methods (all STDMETHODIMP calls in server code) must
be of the canonical form:
try
{
do stuff
}
EXCEPTION_TO_COM_ERROR(CLSID, IID)
EXCEPTION_TO_COM_ERROR is a macro that somehow converts exception to
HRESULT, using CComCoClass::Error in one way or another.
3.2. CHECK_HR throws appropriate exception - up to you to decide what
that is.
3.3. Initialize is internal to CFooImpl. It's basically a two-stage
construction. I know of no way to express C++ constructor semantics
with COM objects and ATL, short of custom
CComObjectDerivative<>::CreateInstance with additional parameters. But
I don't like the custom route - if CComObject<>::CreateInstance
changes in the next ATL release, my version becomes stale. So I prefer
minor inconvenience of two-stage initialization to the danger of
staleness. Exceptions for error handling in Initialize, just it would
have been necessary with a constructor.
3.4. auto_ptr protect me from memory leaks if Initialize fails.
HTH,
Goran.