//=--------------------------------------------------------------------------=
// AutoObj.Cpp
//=--------------------------------------------------------------------------=
// Copyright 1995 - 1998 Microsoft Corporation. All Rights Reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//=--------------------------------------------------------------------------=
//
// all of our objects will inherit from this class to share as much of the same
// code as possible. this super-class contains the unknown, dispatch and
// error info implementations for them.
//
#include "IPServer.H"
#include "LocalSrv.H"
#include "AutoObj.H"
#include "Globals.H"
#include "Util.H"
// for ASSERT and FAIL
//
SZTHISFILE
//=--------------------------------------------------------------------------=
// CAutomationObject::CAutomationObject
//=--------------------------------------------------------------------------=
// create the object and initialize the refcount
//
// Parameters:
// IUnknown * - [in] controlling Unknown
// int - [in] the object type that we are
// void * - [in] the VTable of of the object we really are.
//
// Notes:
//
CAutomationObject::CAutomationObject
(
IUnknown *pUnkOuter,
int ObjType,
void *pVTable,
BOOL fExpandoEnabled
)
: CUnknownObject(pUnkOuter, pVTable), m_ObjectType (ObjType)
{
m_fLoadedTypeInfo = FALSE;
m_fExpandoEnabled = fExpandoEnabled;
m_pexpando = NULL;
}
//=--------------------------------------------------------------------------=
// CAutomationObject::~CAutomationObject
//=--------------------------------------------------------------------------=
// "I have a rendezvous with Death, At some disputed barricade"
// - Alan Seeger (1888-1916)
//
// Notes:
//
CAutomationObject::~CAutomationObject ()
{
// if we loaded up a type info, release our count on the globally stashed
// type infos, and release if it becomes zero.
//
if (m_fLoadedTypeInfo) {
// we have to crit sect this since it's possible to have more than
// one thread partying with this object.
//
EnterCriticalSection(&g_CriticalSection);
ASSERT(CTYPEINFOOFOBJECT(m_ObjectType), "Bogus ref counting on the Type Infos");
CTYPEINFOOFOBJECT(m_ObjectType)--;
// if we're the last one, free that sucker!
//
if (!CTYPEINFOOFOBJECT(m_ObjectType)) {
PTYPEINFOOFOBJECT(m_ObjectType)->Release();
PTYPEINFOOFOBJECT(m_ObjectType) = NULL;
}
LeaveCriticalSection(&g_CriticalSection);
}
if (m_pexpando)
{
delete m_pexpando;
}
return;
}
//=--------------------------------------------------------------------------=
// CAutomationObject::InternalQueryInterface
//=--------------------------------------------------------------------------=
// the controlling unknown will call this for us in the case where they're
// looking for a specific interface.
//
// Parameters:
// REFIID - [in] interface they want
// void ** - [out] where they want to put the resulting object ptr.
//
// Output:
// HRESULT - S_OK, E_NOINTERFACE
//
// Notes:
//
HRESULT CAutomationObject::InternalQueryInterface
(
REFIID riid,
void **ppvObjOut
)
{
ASSERT(ppvObjOut, "controlling Unknown should be checking this!");
// start looking for the guids we support, namely IDispatch, and
// IDispatchEx
if (DO_GUIDS_MATCH(riid, IID_IDispatch)) {
// If expando functionality is enabled, attempt to allocate an
// expando object and return that for the IDispatch interface.
// If the allocation fails, we will fall back on using the regular
// IDispatch from m_pvInterface;
if (m_fExpandoEnabled)
{
if (!m_pexpando)
m_pexpando = new CExpandoObject(m_pUnkOuter, (IDispatch*) m_pvInterface);
if (m_pexpando)
{
*ppvObjOut = (void*)(IDispatch*) m_pexpando;
((IUnknown *)(*ppvObjOut))->AddRef();
return S_OK;
}
}
*ppvObjOut = (void*) (IDispatch*) m_pvInterface;
((IUnknown *)(*ppvObjOut))->AddRef();
return S_OK;
}
else if (DO_GUIDS_MATCH(riid, IID_IDispatchEx) && m_fExpandoEnabled) {
// Allocate the expando object if it hasn't been allocated already
if (!m_pexpando)
m_pexpando = new CExpandoObject(m_pUnkOuter, (IDispatch*) m_pvInterface);
// If the allocation succeeded, return the IDispatchEx interface from
// the expando. Otherwise fall through to CUnknownObject::InternalQueryInterface,
// (which will most likely fail)
if (m_pexpando)
{
*ppvObjOut = (void *)(IDispatchEx *) m_pexpando;
((IUnknown *)(*ppvObjOut))->AddRef();
return S_OK;
}
}
// just get our parent class to process it from here on out.
//
return CUnknownObject::InternalQueryInterface(riid, ppvObjOut);
}
//=--------------------------------------------------------------------------=
// CAutomationObject::GetTypeInfoCount
//=--------------------------------------------------------------------------=
// returns the number of type information interfaces that the object provides
//
// Parameters:
// UINT * - [out] the number of interfaces supported.
//
// Output:
// HRESULT - S_OK, E_NOTIMPL, E_INVALIDARG
//
// Notes:
//
STDMETHODIMP CAutomationObject::GetTypeInfoCount
(
UINT *pctinfo
)
{
// arg checking
//
if (!pctinfo)
return E_INVALIDARG;
// we support GetTypeInfo, so we need to return the count here.
//
*pctinfo = 1;
return S_OK;
}
//=--------------------------------------------------------------------------=
// CAutomationObject::GetTypeInfo
//=--------------------------------------------------------------------------=
// Retrieves a type information object, which can be used to get the type
// information for an interface.
//
// Parameters:
// UINT - [in] the type information they'll want returned
// LCID - [in] the LCID of the type info we want
// ITypeInfo ** - [out] the new type info object.
//
// Output:
// HRESULT - S_OK, E_INVALIDARG, etc.
//
// Notes:
//
STDMETHODIMP CAutomationObject::GetTypeInfo
(
UINT itinfo,
LCID lcid,
ITypeInfo **ppTypeInfoOut
)
{
DWORD dwPathLen;
char szDllPath[MAX_PATH];
HRESULT hr;
ITypeLib *pTypeLib;
ITypeInfo **ppTypeInfo =NULL;
// arg checking
//
if (itinfo != 0)
return DISP_E_BADINDEX;
if (!ppTypeInfoOut)
return E_POINTER;
*ppTypeInfoOut = NULL;
// ppTypeInfo will point to our global holder for this particular
// type info. if it's null, then we have to load it up. if it's not
// NULL, then it's already loaded, and we're happy.
// crit sect this entire nightmare so we're okay with multiple
// threads trying to use this object.
//
EnterCriticalSection(&g_CriticalSection);
ppTypeInfo = PPTYPEINFOOFOBJECT(m_ObjectType);
if (*ppTypeInfo == NULL) {
ITypeInfo *pTypeInfoTmp;
HREFTYPE hrefType;
// we don't have the type info around, so go load the sucker.
//
hr = LoadRegTypeLib(*g_pLibid, (USHORT)VERSIONOFOBJECT(m_ObjectType), 0,
LANG_NEUTRAL, &pTypeLib);
// if, for some reason, we failed to load the type library this
// way, we're going to try and load the type library directly out of
// our resources. this has the advantage of going and re-setting all
// the registry information again for us.
//
if (FAILED(hr)) {
dwPathLen = GetModuleFileName(g_hInstance, szDllPath, MAX_PATH);
if (!dwPathLen) {
hr = E_FAIL;
goto CleanUp;
}
MAKE_WIDEPTR_FROMANSI(pwsz, szDllPath);
hr = LoadTypeLib(pwsz, &pTypeLib);
CLEANUP_ON_FAILURE(hr);
}
// we've got the Type Library now, so get the type info for the interface
// we're interested in.
//
hr = pTypeLib->GetTypeInfoOfGuid((REFIID)INTERFACEOFOBJECT(m_ObjectType), &pTypeInfoTmp);
pTypeLib->Release();
CLEANUP_ON_FAILURE(hr);
// the following couple of lines of code are to dereference the dual
// interface stuff and take us right to the dispatch portion of the
// interfaces.
//
hr = pTypeInfoTmp->GetRefTypeOfImplType(0xffffffff, &hrefType);
if (FAILED(hr)) {
pTypeInfoTmp->Release();
goto CleanUp;
}
hr = pTypeInfoTmp->GetRefTypeInfo(hrefType, ppTypeInfo);
pTypeInfoTmp->Release();
CLEANUP_ON_FAILURE(hr);
// add an extra reference to this object. if it ever becomes zero, then
// we need to release it ourselves. crit sect this since more than
// one thread can party on this object.
//
CTYPEINFOOFOBJECT(m_ObjectType)++;
m_fLoadedTypeInfo = TRUE;
}
// we still have to go and addref the Type info object, however, so that
// the people using it can release it.
//
(*ppTypeInfo)->AddRef();
*ppTypeInfoOut = *ppTypeInfo;
hr = S_OK;
CleanUp:
LeaveCriticalSection(&g_CriticalSection);
return hr;
}
//=--------------------------------------------------------------------------=
// CAutomationObject::GetIDsOfNames
//=--------------------------------------------------------------------------=
// Maps a single member and an optional set of argument names to a
// corresponding set of integer DISPIDs
//
// Parameters:
// REFIID - [in] must be IID_NULL
// OLECHAR ** - [in] array of names to map.
// UINT - [in] count of names in the array.
// LCID - [in] LCID on which to operate
// DISPID * - [in] place to put the corresponding DISPIDs.
//
// Output:
// HRESULT - S_OK, E_OUTOFMEMORY, DISP_E_UNKNOWNNAME,
// DISP_E_UNKNOWNLCID
//
// Notes:
// - we're just going to use DispGetIDsOfNames to save us a lot of hassle,
// and to let this superclass handle it.
//
STDMETHODIMP CAutomationObject::GetIDsOfNames
(
REFIID riid,
OLECHAR **rgszNames,
UINT cNames,
LCID lcid,
DISPID *rgdispid
)
{
HRESULT hr;
ITypeInfo *pTypeInfo;
if (!DO_GUIDS_MATCH(riid, IID_NULL))
return E_INVALIDARG;
// get the type info for this dude!
//
hr = GetTypeInfo(0, lcid, &pTypeInfo);
RETURN_ON_FAILURE(hr);
// use the standard provided routines to do all the work for us.
//
hr = pTypeInfo->GetIDsOfNames(rgszNames, cNames, rgdispid);
pTypeInfo->Release();
return hr;
}
//=--------------------------------------------------------------------------=
// CAutomationObject::Invoke
//=--------------------------------------------------------------------------=
// provides access to the properties and methods on this object.
//
// Parameters:
// DISPID - [in] identifies the member we're working with.
// REFIID - [in] must be IID_NULL.
// LCID - [in] language we're working under
// USHORT - [in] flags, propput, get, method, etc ...
// DISPPARAMS * - [in] array of arguments.
// VARIANT * - [out] where to put result, or NULL if they don't care.
// EXCEPINFO * - [out] filled in in case of exception
// UINT * - [out] where the first argument with an error is.
//
// Output:
// HRESULT - tonnes of them.
//
// Notes:
//
STDMETHODIMP CAutomationObject::Invoke
(
DISPID dispid,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS *pdispparams,
VARIANT *pvarResult,
EXCEPINFO *pexcepinfo,
UINT *puArgErr
)
{
HRESULT hr;
ITypeInfo *pTypeInfo;
if (!DO_GUIDS_MATCH(riid, IID_NULL))
return E_INVALIDARG;
// get our typeinfo first!
//
hr = GetTypeInfo(0, lcid, &pTypeInfo);
RETURN_ON_FAILURE(hr);
// Clear exceptions
//
SetErrorInfo(0L, NULL);
// This is exactly what DispInvoke does--so skip the overhead.
//
hr = pTypeInfo->Invoke(m_pvInterface, dispid, wFlags,
pdispparams, pvarResult,
pexcepinfo, puArgErr);
pTypeInfo->Release();
return hr;
}
//=--------------------------------------------------------------------------=
// CAutomationObject::Exception
//=--------------------------------------------------------------------------=
// fills in the rich error info object so that both our vtable bound interfaces
// and calls through ITypeInfo::Invoke get the right error informaiton.
//
// Parameters:
// HRESULT - [in] the SCODE that should be associated with this err
// WORD - [in] the RESOURCE ID of the error message.
// DWORD - [in] helpcontextid for the error
//
// Output:
// HRESULT - the HRESULT that was passed in.
//
// Notes:
//
HRESULT CAutomationObject::Exception
(
HRESULT hrExcep,
WORD idException,
DWORD dwHelpContextID
)
{
ICreateErrorInfo *pCreateErrorInfo;
IErrorInfo *pErrorInfo;
WCHAR wszTmp[256];
char szTmp[256];
HRESULT hr;
// first get the createerrorinfo object.
//
hr = CreateErrorInfo(&pCreateErrorInfo);
if (FAILED(hr)) return hrExcep;
MAKE_WIDEPTR_FROMANSI(wszHelpFile, HELPFILEOFOBJECT(m_ObjectType));
// set up some default information on it.
//
pCreateErrorInfo->SetGUID((REFIID)INTERFACEOFOBJECT(m_ObjectType));
pCreateErrorInfo->SetHelpFile(wszHelpFile);
pCreateErrorInfo->SetHelpContext(dwHelpContextID);
// load in the actual error string value. max of 256.
//
LoadString(GetResourceHandle(), idException, szTmp, 256);
MultiByteToWideChar(CP_ACP, 0, szTmp, -1, wszTmp, 256);
pCreateErrorInfo->SetDescription(wszTmp);
// load in the source
//
MultiByteToWideChar(CP_ACP, 0, NAMEOFOBJECT(m_ObjectType), -1, wszTmp, 256);
pCreateErrorInfo->SetSource(wszTmp);
// now set the Error info up with the system
//
hr = pCreateErrorInfo->QueryInterface(IID_IErrorInfo, (void **)&pErrorInfo);
CLEANUP_ON_FAILURE(hr);
SetErrorInfo(0, pErrorInfo);
pErrorInfo->Release();
CleanUp:
pCreateErrorInfo->Release();
return hrExcep;
}
//=--------------------------------------------------------------------------=
// CAutomationObject::InterfaceSupportsErrorInfo
//=--------------------------------------------------------------------------=
// indicates whether or not the given interface supports rich error information
//
// Parameters:
// REFIID - [in] the interface we want the answer for.
//
// Output:
// HRESULT - S_OK = Yes, S_FALSE = No.
//
// Notes:
//
HRESULT CAutomationObject::InterfaceSupportsErrorInfo
(
REFIID riid
)
{
// see if it's the interface for the type of object that we are.
//
if (riid == (REFIID)INTERFACEOFOBJECT(m_ObjectType))
return S_OK;
return S_FALSE;
}
//=--------------------------------------------------------------------------=
// CAutomationObject::GetResourceHandle [helper]
//=--------------------------------------------------------------------------=
// virtual routine to get the resource handle. virtual, so that inheriting
// objects, such as COleControl can use theirs instead, which goes and gets
// the Host's version ...
//
// Output:
// HINSTANCE
//
// Notes:
//
HINSTANCE CAutomationObject::GetResourceHandle
(
void
)
{
return ::GetResourceHandle();
}