// Copyright 1995-1997 Microsoft Corporation.  All Rights Reserved.

// 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 "header.h"
#include "AutoObj.H"

#ifndef _DEBUG
#undef THIS_FILE
static const char THIS_FILE[] = __FILE__;
#endif

//=--------------------------------------------------------------------------=
// 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.

CAutomationObject::CAutomationObject(IUnknown *pUnkOuter, int ObjType, void *pVTable)
	: CUnknownObject(pUnkOuter, pVTable), m_ObjectType (ObjType)
{
	m_fLoadedTypeInfo = FALSE;
}

//=--------------------------------------------------------------------------=
// CAutomationObject::~CAutomationObject
//=--------------------------------------------------------------------------=

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_COMMENT(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);
	}

	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

HRESULT CAutomationObject::InternalQueryInterface(REFIID riid, void **ppvObjOut)
{
#if 0
	ASSERT_COMMENT(ppvObjOut, "controlling Unknown should be checking this!");

	return E_NOTIMPL;
#endif

	// start looking for the guids we support, namely IDispatch, and the

	if (DO_GUIDS_MATCH(riid, IID_IDispatch)) {
		*ppvObjOut = (void *)(IDispatch *)m_pvInterface;
		((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

STDMETHODIMP CAutomationObject::GetTypeInfoCount(UINT *pctinfo)
{
	if (!pctinfo)
		return E_INVALIDARG;

	*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(_Module.GetModuleInstance(), 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;

	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.

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.

HRESULT CAutomationObject::Exception(HRESULT hrExcep, WORD idException, DWORD dwHelpContextID)
{
	ICreateErrorInfo *pCreateErrorInfo;
	IErrorInfo *pErrorInfo;
	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.

	CWStr cwzError(GetStringResource(idException));
	pCreateErrorInfo->SetDescription(cwzError);

	// load in the source

	cwzError = NAMEOFOBJECT(m_ObjectType);
	pCreateErrorInfo->SetSource(cwzError);

	// 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.

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;
}
