//=--------------------------------------------------------------------------=
// AutoObj.Cpp
//=--------------------------------------------------------------------------=
// Copyright 1995-1997 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)
{
dbprintf("CAutomationObject::CAutomationObject %s:%d start\n",__FILE__,__LINE__);
    m_fLoadedTypeInfo = FALSE;
	m_fExpandoEnabled = fExpandoEnabled;
	m_pexpando = NULL;
dbprintf("CAutomationObject::CAutomationObject %s:%d done\n",__FILE__,__LINE__);
}


//=--------------------------------------------------------------------------=
// CAutomationObject::~CAutomationObject
//=--------------------------------------------------------------------------=
// "I have a rendezvous with Death, At some disputed barricade"
// - Alan Seeger (1888-1916)
//
// Notes:
//
CAutomationObject::~CAutomationObject ()
{
dbprintf("CAutomationObject::~CAutomationObject %s:%d start\n",__FILE__,__LINE__);
    // 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;
	}
dbprintf("CAutomationObject::~CAutomationObject %s:%d done\n",__FILE__,__LINE__);
    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
)
{
dbprintf("CAutomationObject::InternalQueryInterface %s:%d start\n",__FILE__,__LINE__);
    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();
dbprintf("CAutomationObject::InternalQueryInterface %s:%d done\n",__FILE__,__LINE__);
				return S_OK;
			}
		}

        *ppvObjOut = (void*) (IDispatch*) m_pvInterface;
        ((IUnknown *)(*ppvObjOut))->AddRef();
dbprintf("CAutomationObject::InternalQueryInterface %s:%d done2\n",__FILE__,__LINE__);
        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();
dbprintf("CAutomationObject::InternalQueryInterface %s:%d done3\n",__FILE__,__LINE__);
			return S_OK;
		}
    }

    // just get our parent class to process it from here on out.
    //
    HRESULT ret = CUnknownObject::InternalQueryInterface(riid, ppvObjOut);
dbprintf("CAutomationObject::InternalQueryInterface %s:%d done4\n",__FILE__,__LINE__);
    return ret;
}

//=--------------------------------------------------------------------------=
// 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
)
{
dbprintf("CAutomationObject::GetTypeInfoCount %s:%d start\n",__FILE__,__LINE__);
    // arg checking
    //
    if (!pctinfo)
dbprintf("CAutomationObject::GetTypeInfoCount %s:%d done\n",__FILE__,__LINE__);
        return E_INVALIDARG;

    // we support GetTypeInfo, so we need to return the count here.
    //
    *pctinfo = 1;
dbprintf("CAutomationObject::GetTypeInfoCount %s:%d done2\n",__FILE__,__LINE__);
    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
)
{
dbprintf("CAutomationObject::GetTypeInfo %s:%d start\n",__FILE__,__LINE__);
    DWORD       dwPathLen;
    char        szDllPath[MAX_PATH];
    HRESULT     hr;
    ITypeLib   *pTypeLib;
    ITypeInfo **ppTypeInfo =NULL;

    // arg checking
    //
    if (itinfo != 0){
dbprintf("CAutomationObject::GetTypeInfo %s:%d done\n",__FILE__,__LINE__);
        return DISP_E_BADINDEX;
    }

    if (!ppTypeInfoOut){
dbprintf("CAutomationObject::GetTypeInfo %s:%d done2\n",__FILE__,__LINE__);
        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);
dbprintf("CAutomationObject::GetTypeInfo %s:%d done3\n",__FILE__,__LINE__);
    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
)
{
dbprintf("CAutomationObject::GetIDsOfNames %s:%d start\n",__FILE__,__LINE__);
    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();

dbprintf("CAutomationObject::GetIDsOfNames %s:%d done\n",__FILE__,__LINE__);
    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
)
{
dbprintf("CAutomationObject::Invoke %s:%d start\n",__FILE__,__LINE__);
    HRESULT    hr;
    ITypeInfo *pTypeInfo;

    if (!DO_GUIDS_MATCH(riid, IID_NULL)){
dbprintf("CAutomationObject::Invoke %s:%d done\n",__FILE__,__LINE__);
        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();

dbprintf("CAutomationObject::Invoke %s:%d done2\n",__FILE__,__LINE__);
	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
)
{
dbprintf("CAutomationObject::Exception %s:%d start\n",__FILE__,__LINE__);
    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;
dbprintf("CAutomationObject::Exception %s:%d done.\n",__FILE__,__LINE__);
    }
    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();
dbprintf("CAutomationObject::Exception %s:%d done2.\n",__FILE__,__LINE__);
    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
)
{
dbprintf("CAutomationObject::InterfaceSupportsErrorInfo %s:%d start.\n",__FILE__,__LINE__);
    // see if it's the interface for the type of object that we are.
    //
    if (riid == (REFIID)INTERFACEOFOBJECT(m_ObjectType)){
dbprintf("CAutomationObject::InterfaceSupportsErrorInfo %s:%d done.\n",__FILE__,__LINE__);
        return S_OK;
    }
dbprintf("CAutomationObject::InterfaceSupportsErrorInfo %s:%d done2.\n",__FILE__,__LINE__);
    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
)
{
dbprintf("CAutomationObject::GetResourceHandle %s:%d start/done.\n",__FILE__,__LINE__);
    return ::GetResourceHandle();
}


