AUTOBASE.CPP

/* 
* AUTOBASE.CPP
* Cosmo Chapter 14
*
* Base object for Cosmo's OLE Automation support. This class
* provides the implementation of IDispatch as well as the code
* that loads the appropriate type information. This leaves
* each specific object to implement it's specific interface and
* little else.
*
* Copyright (c)1993-1995 Microsoft Corporation, All Right Reserved.
*
* Kraig Brockschmidt, Microsoft
* Internet : kraigb@microsoft.com
* Compuserve: INTERNET>kraigb@microsoft.com
*/


#include "cosmo.h"


/*
* CAutoBase::CAutoBase
* CAutoBase::~CAutoBase
*
* Constructor Parameters:
* pv void * to the frame or document object that
* derived classes will use to access the functions
* they need to implemenet their interface.
* hInst HINSTANCE of the application (for loading
* resources)
* riid REFIID of the interface that this object
* should expose.
* rdiid REFIID of the dispinterface that this
* object should expose.
* pfnDestroy PFNDESTROYED to call when the object
* is destroyed.
*/

CAutoBase::CAutoBase(void *pv, HINSTANCE hInst, REFIID riid
, REFIID rdiid, PFNDESTROYED pfnDestroy)
{
m_cRef=0L;
m_pfnDestroy=pfnDestroy;

m_pObj=pv;
m_hInst=hInst;

m_iid=riid;
m_diid=rdiid;

m_pImpIDispatch=NULL;
m_pImpIExtConn=NULL;
return;
}


CAutoBase::~CAutoBase(void)
{
DeleteInterfaceImp(m_pImpIExtConn);
DeleteInterfaceImp(m_pImpIDispatch);
return;
}


/*
* CAutoBase::Init
*
* Purpose:
* Performs any intiailization of CAutoBase that's prone to failure
* that we also use internally before exposing the object outside.
*
* Parameters:
* fExtConn BOOL indicating if we care about external
* connections for shutdown.
*
* Return Value:
* BOOL TRUE if the function is successful,
* FALSE otherwise.
*/

BOOL CAutoBase::Init(BOOL fExtConn)
{
//The derived class has to supply the IUnknown implementation
m_pImpIDispatch=new CImpIDispatch(this
, (IUnknown *)VTableInterface());

if (NULL==m_pImpIDispatch)
return FALSE;

if (fExtConn)
{
m_pImpIExtConn=new CImpIExtConn
((IUnknown *)VTableInterface());

if (NULL==m_pImpIExtConn)
return FALSE;
}

return TRUE;
}



//IDispatch interface implementation

/*
* CImpIDispatch::CImpIDispatch
* CImpIDispatch::~CImpIDispatch
*
* Parameters (Constructor):
* pObj PCAutoBase to the containing object.
* pUnkOuter LPUNKNOWN to which we delegate.
*/

CImpIDispatch::CImpIDispatch(PCAutoBase pObj, LPUNKNOWN pUnkOuter)
{
m_cRef=0;
m_pObj=pObj;
m_pUnkOuter=pUnkOuter;
m_pITypeInfo=NULL;
return;
}

CImpIDispatch::~CImpIDispatch(void)
{
ReleaseInterface(m_pITypeInfo);
return;
}



/*
* CImpIDispatch::QueryInterface
* CImpIDispatch::AddRef
* CImpIDispatch::Release
*
* Purpose:
* IUnknown members for CImpIDispatch object.
*/

STDMETHODIMP CImpIDispatch::QueryInterface(REFIID riid, PPVOID ppv)
{
return m_pUnkOuter->QueryInterface(riid, ppv);
}


STDMETHODIMP_(ULONG) CImpIDispatch::AddRef(void)
{
++m_cRef;
return m_pUnkOuter->AddRef();
}

STDMETHODIMP_(ULONG) CImpIDispatch::Release(void)
{
--m_cRef;
return m_pUnkOuter->Release();
}



/*
* CImpIDispatch::GetTypeInfoCount
* CImpIDispatch::GetTypeInfo
*/

STDMETHODIMP CImpIDispatch::GetTypeInfoCount(UINT *pctInfo)
{
//We implement GetTypeInfo so return 1
*pctInfo=1;
return NOERROR;
}

STDMETHODIMP CImpIDispatch::GetTypeInfo(UINT itInfo, LCID lcid
, ITypeInfo **ppITypeInfo)
{
HRESULT hr;
ITypeLib *pITypeLib;
ITypeInfo **ppITI=NULL;

if (0!=itInfo)
return ResultFromScode(TYPE_E_ELEMENTNOTFOUND);

if (NULL==ppITypeInfo)
return ResultFromScode(E_POINTER);

*ppITypeInfo=NULL;

/*
* This switch statement is nicely extensible for other
* languages and other m_pITypeInfo variables (such as it
* was used in Beeper #2 (see ..\beeper2\beeper.cpp)
*/
switch (PRIMARYLANGID(lcid))
{
case LANG_NEUTRAL:
case LANG_ENGLISH:
ppITI=&m_pITypeInfo;
break;

default:
return ResultFromScode(DISP_E_UNKNOWNLCID);
}

//Load a type lib if we don't have the information already.
if (NULL==*ppITI)
{
hr=LoadRegTypeLib(LIBID_CosmoTypeLibrary, 1, 0
, PRIMARYLANGID(lcid), &pITypeLib);

/*
* If LoadRegTypeLib fails, try loading directly from
* our module with LoadTypeLib.
*/
if (FAILED(hr))
{
const int cch=512;
TCHAR szModule[cch];

GetModuleFileName(m_pObj->m_hInst, szModule, cch);

switch (PRIMARYLANGID(lcid))
{
case LANG_NEUTRAL:
case LANG_ENGLISH:
#ifdef WIN32ANSI
OLECHAR szTemp[512];
MultiByteToWideChar(CP_ACP, 0, szModule, -1, szTemp, 512);
hr=LoadTypeLib(szTemp, &pITypeLib);
#else
hr=LoadTypeLib(szModule, &pITypeLib);
#endif
break;

/*
* Easily extensible with additional languages.
* Use LoadTypeLibFromResource or specify
* separate .TLB files instead of the module name.
* Be sure to use your DIR registry key to find
* the right path.
*/
}
}

if (FAILED(hr))
return hr;

//Got the type lib, get type info for the interface we want
hr=pITypeLib->GetTypeInfoOfGuid(m_pObj->m_iid, ppITI);
pITypeLib->Release();

if (FAILED(hr))
return hr;
}

//Ref on ITypeInfo holds type library in memory
(*ppITI)->AddRef();
*ppITypeInfo=*ppITI;
return NOERROR;
}



/*
* CImpIDispatch::GetIDsOfNames
* CImpIDispatch::Invoke
*/

STDMETHODIMP CImpIDispatch::GetIDsOfNames(REFIID riid
, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgDispID)
{
HRESULT hr;
ITypeInfo *pTI;

if (IID_NULL!=riid)
return ResultFromScode(DISP_E_UNKNOWNINTERFACE);

hr=GetTypeInfo(0, lcid, &pTI);

if (SUCCEEDED(hr))
{
hr=DispGetIDsOfNames(pTI, rgszNames, cNames, rgDispID);
pTI->Release();
}

return hr;
}

STDMETHODIMP CImpIDispatch::Invoke(DISPID dispID, REFIID riid
, LCID lcid, unsigned short wFlags, DISPPARAMS *pDispParams
, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
HRESULT hr;
ITypeInfo *pTI;

if (IID_NULL!=riid)
return ResultFromScode(DISP_E_UNKNOWNINTERFACE);

//Get the ITypeInfo for lcid
hr=GetTypeInfo(0, lcid, &pTI);

if (FAILED(hr))
return hr;

//Note: this sample doesn't do anything with exceptions.

//The VTableInterface function gets us the right pointer
hr=pTI->Invoke(m_pObj->VTableInterface(), dispID, wFlags
, pDispParams, pVarResult, pExcepInfo, puArgErr);

pTI->Release();
return hr;
}




//IExternalConnection implementation

/*
* CImpIExtConn::CImpIExtConn
* CImpIExtConn::~CImpIExtConn
*
* Parameters (Constructor):
* pUnkOuter LPUNKNOWN to which we delegate.
*/

CImpIExtConn::CImpIExtConn(LPUNKNOWN pUnkOuter)
{
m_cRef=0;
m_pUnkOuter=pUnkOuter;
m_cStrong=0;
return;
}

CImpIExtConn::~CImpIExtConn(void)
{
return;
}



/*
* CImpIExtConn::QueryInterface
* CImpIExtConn::AddRef
* CImpIExtConn::Release
*
* Purpose:
* Delegating IUnknown members for CImpIExtConn.
*/

STDMETHODIMP CImpIExtConn::QueryInterface(REFIID riid
, LPVOID *ppv)
{
return m_pUnkOuter->QueryInterface(riid, ppv);
}


STDMETHODIMP_(ULONG) CImpIExtConn::AddRef(void)
{
++m_cRef;
return m_pUnkOuter->AddRef();
}

STDMETHODIMP_(ULONG) CImpIExtConn::Release(void)
{
--m_cRef;
return m_pUnkOuter->Release();
}




/*
* CImpIExtConn::AddConnection
*
* Purpose:
* Informs the object that a connection has been made to it. We
* count only strong references.
*
* Parameters:
* dwConn DWORD identifying the type of connection, taken
* from the EXTCONN enumeration.
* dwReserved DWORD reserved. This is used inside OLE and
* should not be validated.
*
* Return Value:
* DWORD The number of connection counts on the
* object, used for debugging purposes only.
*/

STDMETHODIMP_(DWORD) CImpIExtConn::AddConnection
(DWORD dwConn, DWORD dwReserved)
{
if (EXTCONN_STRONG==dwConn)
{
m_cStrong++;
return m_cStrong;
}

return 0L;
}






/*
* CImpIExtConn::ReleaseConnection
*
* Purpose:
* Informs an object that a connection has been taken away from
* it in which case the object may need to shut down.
*
* Parameters:
* dwConn DWORD identifying the type of connection,
* taken from the EXTCONN enumeration.
* dwReserved DWORD reserved. This is used inside OLE and
* should not be validated.
* fLastReleaseCloses BOOL indicating if the last call to this
* function should close the object.
*
* Return Value:
* DWORD The number of remaining connection counts on
* the object, used for debugging purposes only.
*/

STDMETHODIMP_(DWORD) CImpIExtConn::ReleaseConnection
(DWORD dwConn, DWORD dwReserved, BOOL fLastReleaseCloses)
{
DWORD dw=0L;

if (EXTCONN_STRONG==dwConn)
{
dw=--m_cStrong;

/*
* Note that in the way we're using this interface we
* don't care about fLastReleaseCloses.
*/
if (0L==m_cStrong)
m_pUnkOuter->Release(); //Closes app or doc
}

return dw;
}