//---------------------------------------------------------------------------
// OleAuto.cpp
//---------------------------------------------------------------------------
// Simple class for doing dual OLE Automation interfaces
//---------------------------------------------------------------------------
// (C) Copyright 1992-1996 by 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.
//---------------------------------------------------------------------------

#include "Main.h"

#define NUM_RESERVED_EXTENDER_DISPIDS 500
#define EXTENDER_DISPID_BASE ((ULONG)(0x80010000))
#define IS_EXTENDER_DISPID(x) ( ( (ULONG)(x) & 0xFFFF0000 ) == EXTENDER_DISPID_BASE )

//---------------------------------------------------------------------------
// DEBUG info
//---------------------------------------------------------------------------
SZTHISFILE


//---------------------------------------------------------------------------
// A little utility which simplifies firing dispatch events.
//---------------------------------------------------------------------------
HRESULT InvokeEvent
(
  IDispatch  *pdisp,    // IDispatch of Sink
  DISPID      dispid,   // DISPID of event
  VARIANTARG *pvararg,  // Args to event
  UINT        carg      // # args
)
{
  DISPPARAMS dp;
  EXCEPINFO  ei;
  UINT       uArgErr = 0;

  dp.rgvarg            = pvararg;
  dp.rgdispidNamedArgs = NULL;
  dp.cArgs             = carg;
  dp.cNamedArgs        = 0;
  return pdisp->Invoke(dispid, IID_NULL, 0, DISPATCH_METHOD, &dp, NULL, &ei, &uArgErr);
}


//---------------------------------------------------------------------------
// This routine will load a TypeLib and (optionally) find the TypeInfo inside
// which matches the given clsid.  The TypeLib and TypeInfo pointers are
// in/out so you can simply:
//      hr = LoadTypeInfo(..., &g_pMyTypeLib, &m_pMyObjectsTypeInfo);
// and it will fill in g_pMyTypeLib and m_pMyObjectsTypeInfo, if necessary.
//---------------------------------------------------------------------------
HRESULT LoadTypeInfo
(
  HINSTANCE   hinst,             // hinst of where to load TypeLib from, if not found
  UINT        itinfo,            // index of TypeInfo requested, only 0 supported
  USHORT      dwMaj,             // Maj version # of TypeLib
  USHORT      dwMin,             // Min version # of TypeLib
  LCID        lcid,              // Locale of TypeLib to load
  REFGUID     libid,             // LIBID of TypeLib  to find
  REFCLSID    clsid,             // CLSID of TypeInfo to find
  REFIID      iid,               // IID   of TypeInfo to find
  BOOL        fDispOnly,         // TRUE=ensure *ptinfoIntInOut is a TKIND_DISPATCH, not vtbl
  ITypeLib  **pptlibInOut,       // Ptr to cache of pTypeLib, typically &g_ptlib
  ITypeInfo **pptinfoClassInOut, // Ptr to cache of pTypeInfo, typically &s_ptinfoCls
  ITypeInfo **pptinfoIntInOut    // Ptr to cache of pTypeInfo, typically &s_ptinfoInt
)
{
  HRESULT    hr        = S_OK;
  ITypeInfo *ptinfoT   = NULL;
  TYPEATTR  *ptypeattr = NULL;

  // Arg checking
  if (itinfo != 0)
    return DISP_E_BADINDEX;

  if (!pptlibInOut)
    return E_POINTER;

  // Check to see if we have a TypeLib, load it if not.
  if (!*pptlibInOut)
    {
    hr = LoadRegTypeLib(libid, dwMaj, dwMin, lcid, pptlibInOut);
    if (hr)
      {
      ASSERT(hinst, "hinst==NULL means in-memory TL; shouldn't have to load from resource");

      // 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.
      DWORD dwPathLen;
      char  szDllPath[MAX_PATH];

      dwPathLen = GetModuleFileName(hinst, szDllPath, MAX_PATH);
      if (!dwPathLen)
        return E_FAIL;

      MAKE_WIDEPTR_FROMANSI(pwsz, szDllPath);
      CHECK(LoadTypeLib(pwsz, pptlibInOut));
      }
    }
  ASSERT(*pptlibInOut, "Don't have a pTypeLib");

  // If the user wants a coclass TypeInfo...
  if (pptinfoClassInOut)
    {
    // ...Check to see if we already have it, and find it in the TypeLib, if not.
    if (!*pptinfoClassInOut)
      {
      // Get coclass TypeInfo
      CHECK((*pptlibInOut)->GetTypeInfoOfGuid(clsid, pptinfoClassInOut));
      }
    }

  // If the user wants an interface TypeInfo...
  if (pptinfoIntInOut)
    {
    // ...Check to see if we already have it, and find it in the TypeLib, if not.
    if (!*pptinfoIntInOut)
      {
      // Get interface TypeInfo
      CHECK((*pptlibInOut)->GetTypeInfoOfGuid(iid, &ptinfoT));
      CHECK(ptinfoT->GetTypeAttr(&ptypeattr));

      // If we want a TKIND_DISPATCH, but we have a TKIND_INTERFACE, then switch if it's dual
      if (fDispOnly && ptypeattr->typekind == TKIND_INTERFACE)
        {
        // We're on a TKIND_INTERFACE but want a dispatch.  If this is not a dual interface,
        // we're out of luck, so stuff a NULL, and return S_FALSE.
        if (!(ptypeattr->wTypeFlags & TYPEFLAG_FDUAL))
          {
          *pptinfoIntInOut = NULL;
          hr = S_FALSE;
          }

        // This is a dual interface, so get the corresponding Disp interface;
        // 0xffffffff is a magic number which does this for us.
        HREFTYPE hrefType;
        CHECK(ptinfoT->GetRefTypeOfImplType(0xffffffff, &hrefType));
        CHECK(ptinfoT->GetRefTypeInfo(hrefType, pptinfoIntInOut));
        }
      else
        {
        *pptinfoIntInOut = ptinfoT;
        (*pptinfoIntInOut)->AddRef();
        }
      }
    }

CleanUp:
  // Release all frame variables
  if (ptypeattr)
    ptinfoT->ReleaseTypeAttr(ptypeattr);
  if (ptinfoT)
    ptinfoT->Release();

  return hr;
}



//***************************************************************************
// IDispatch Interface
//***************************************************************************

//---------------------------------------------------------------------------
// 
//---------------------------------------------------------------------------
STDMETHODIMP COleAuto::GetTypeInfoCount
(
  UINT* pctinfo
)
{
  // Validate args
  if (!pctinfo)
    return E_INVALIDARG;

  // Return requested info
  *pctinfo = 1;
  return S_OK;
}


//---------------------------------------------------------------------------
// 
//---------------------------------------------------------------------------
STDMETHODIMP COleAuto::GetTypeInfo
(
  UINT        itinfo,
  LCID        lcid,
  ITypeInfo** pptinfoOut
)
{
  HRESULT hr = S_OK;

  // Validate args
  if (!pptinfoOut)
    return E_INVALIDARG;
  *pptinfoOut = NULL;

  // Make sure we have the TypeInfo
  CHECK(this->CheckTypeInfo(itinfo, lcid));

  // Return requested info
  *pptinfoOut = *this->GetTinfoIntAddr();
  (*pptinfoOut)->AddRef();    // For *pptiOut

CleanUp:
  return hr;
}


//---------------------------------------------------------------------------
// 
//---------------------------------------------------------------------------
HRESULT COleAuto::CheckTypeInfo
(
  UINT itinfo,
  LCID lcid
)
{
  HRESULT      hr;
  HINSTANCE    hinst;
  SHORT        wMaj, wMin;
  const GUID  *plibid;
  const CLSID *pclsid;
  const IID   *piid;
  ITypeLib   **pptl;
  ITypeInfo  **pptinfoCls;
  ITypeInfo  **pptinfoInt;

  pptinfoCls = this->GetTinfoClsAddr();
  pptinfoInt = this->GetTinfoIntAddr();
  if (*pptinfoCls && *pptinfoInt)
    return S_OK;

  hr = GetTypeLibInfo(&hinst, &plibid, &wMaj, &wMin, &pclsid, &piid, &pptl);
  if (hr)
    return hr;

  return LoadTypeInfo(hinst, itinfo, wMaj, wMin, lcid, *plibid, *pclsid, *piid, FALSE,
                      pptl, pptinfoCls, pptinfoInt);
}


//---------------------------------------------------------------------------
// 
//---------------------------------------------------------------------------
STDMETHODIMP COleAuto::GetIDsOfNames
(
  REFIID    iid,
  OLECHAR** rgszNames,
  UINT      cNames,
  LCID      lcid,
  DISPID*   prgdispid
)
{
  HRESULT hr;

  // Validate Args
  if (iid != IID_NULL)
      return E_INVALIDARG;

  // Make sure we have the TypeInfo
  CHECK(this->CheckTypeInfo(0, lcid));

  // Use the standard provided routines to do all the work for us.
  hr = (*this->GetTinfoIntAddr())->GetIDsOfNames(rgszNames, cNames, prgdispid);

  // Try the base object iff there is a base object and the previous call failed
  if (FAILED(hr) && m_pdispBaseObject)
    {
    hr = m_pdispBaseObject->GetIDsOfNames(iid, rgszNames, cNames, lcid, prgdispid);
    if (SUCCEEDED(hr))
      {
      // Adjust extender dispid.
      if (IS_EXTENDER_DISPID(prgdispid[0]))
        prgdispid[0] += NUM_RESERVED_EXTENDER_DISPIDS;
      }
    }

CleanUp:
  return hr;
}


//---------------------------------------------------------------------------
// 
//---------------------------------------------------------------------------
STDMETHODIMP COleAuto::Invoke
(
  DISPID      dispidMember,
  REFIID      iid,
  LCID        lcid,
  WORD        wFlags,
  DISPPARAMS* pdispparams,
  VARIANT*    pvarResult,
  EXCEPINFO*  pexcepinfo,
  UINT*       puArgErr
)
{
  HRESULT hr;

  // Validate args, clear out params
  if (!pdispparams)
    return E_INVALIDARG;
  if (pvarResult)
    VariantInit(pvarResult);
  SetErrorInfo(0L, NULL);
  if (puArgErr)
    *puArgErr = 0;
  if (iid != IID_NULL)
    return E_INVALIDARG;

  // Make sure we have the TypeInfo
  CHECK(this->CheckTypeInfo(0, lcid));

  // Use the standard provided routines to do all the work for us.
  hr = (*this->GetTinfoIntAddr())->Invoke(this->GetPrimary(), dispidMember, wFlags,
                                          pdispparams, pvarResult, pexcepinfo, puArgErr);
  // Try the base object iff there is a base object and the previous invoke failed
  if (FAILED(hr) && m_pdispBaseObject)
    {
    if (IS_EXTENDER_DISPID(dispidMember))
      dispidMember -= NUM_RESERVED_EXTENDER_DISPIDS;

    hr = m_pdispBaseObject->Invoke(dispidMember, iid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
    }

CleanUp:
  return hr;
}


//***************************************************************************
// IProvideMultipleClassInfo Interface
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP COleAuto::GetMultiTypeInfoCount
(
  ULONG *pc
)
  {
  HRESULT hr;
  ULONG count;
  IProvideClassInfo         *pclsinfo   = NULL;
  IProvideMultipleClassInfo *pclsinfoex = NULL;

  // This should not be possible!
  ASSERT(m_pdispBaseObject != NULL, "");

  hr = m_pdispBaseObject->QueryInterface(IID_IProvideMultipleClassInfo, (void**)&pclsinfoex);
  if (SUCCEEDED(hr))
    {
    hr = pclsinfoex->GetMultiTypeInfoCount(&count);
    if (SUCCEEDED(hr))
      *pc = count + 1;
    }
  else
    {
    hr = m_pdispBaseObject->QueryInterface(IID_IProvideClassInfo, (void**)&pclsinfo);
    if (FAILED(hr))
      goto Exit;
    *pc = 2;
    }

Exit:
  // Release the temp refs
  if (pclsinfoex)
    pclsinfoex->Release();
  if (pclsinfo)
    pclsinfo->Release();
  return hr;
  }


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
STDMETHODIMP COleAuto::GetInfoOfIndex
(
  ULONG       itinfo,
  DWORD       dwFlags,
  ITypeInfo** pptinfoCoClass,
  DWORD*      pdwTIFlags,
  ULONG*      pcdispidReserved,
  IID*        piidPrimary,
  IID*        piidSource
)
  {
  HRESULT                    hr         = S_OK;
  IProvideClassInfo         *pclsinfo   = NULL;
  IProvideMultipleClassInfo *pclsinfoex = NULL;

  // This should not be possible!
  ASSERT(m_pdispBaseObject != NULL, "");

  if (itinfo == 0)
    {
    // Return info on ourselves
    if (dwFlags & MULTICLASSINFO_GETTYPEINFO)
      {
      // Make sure we have the TypeInfo
      hr = this->CheckTypeInfo(0, 0x0409);
      if (hr)
        goto Exit;

      // Return requested info
      *pptinfoCoClass = *this->GetTinfoClsAddr();
      (*pptinfoCoClass)->AddRef();    // For *pptiOut
      if (pdwTIFlags)
        *pdwTIFlags = 0;
      }
    if (dwFlags & MULTICLASSINFO_GETNUMRESERVEDDISPIDS)
      *pcdispidReserved = NUM_RESERVED_EXTENDER_DISPIDS;
    if (dwFlags & MULTICLASSINFO_GETIIDPRIMARY)
      // BUGBUG: Not implemented
      *piidPrimary = IID_NULL;
    if (dwFlags & MULTICLASSINFO_GETIIDSOURCE)
      // BUGBUG: Not implemented
      *piidSource = IID_NULL;
    else
      {
      // Look for an extender
      hr = m_pdispBaseObject->QueryInterface(IID_IProvideMultipleClassInfo, (void**)&pclsinfoex);
      if (SUCCEEDED(hr))
        {
        hr = pclsinfoex->GetInfoOfIndex(itinfo-1, dwFlags, pptinfoCoClass, pdwTIFlags,
                                       pcdispidReserved, piidPrimary, piidSource);
        if (SUCCEEDED(hr) && (dwFlags & MULTICLASSINFO_GETTYPEINFO)) 
          *pdwTIFlags = TIFLAGS_EXTENDDISPATCHONLY;
	}
      else
        {
        if (itinfo != 1)
          hr = TYPE_E_ELEMENTNOTFOUND;

        hr = m_pdispBaseObject->QueryInterface(IID_IProvideClassInfo, (void**)&pclsinfo);
        if (FAILED(hr))
          goto Exit;
	        
        // Return info on unextended base object
        if (dwFlags & MULTICLASSINFO_GETTYPEINFO)
          {
          hr = pclsinfo->GetClassInfo(pptinfoCoClass);
          if (FAILED(hr))
            goto Exit;
          if (pdwTIFlags)
            *pdwTIFlags = TIFLAGS_EXTENDDISPATCHONLY;
          }
        if (dwFlags & MULTICLASSINFO_GETNUMRESERVEDDISPIDS)
          *pcdispidReserved = 0;
        if (dwFlags & MULTICLASSINFO_GETIIDPRIMARY)
          // BUGBUG: Not implemented
          *piidPrimary = IID_NULL;
        if (dwFlags & MULTICLASSINFO_GETIIDSOURCE)
          // BUGBUG: Not implemented
          *piidSource = IID_NULL;
        }
      }
    }

Exit:
  // Release the temp refs
  if (pclsinfoex)
    pclsinfoex->Release();
  if (pclsinfo)
    pclsinfo->Release();
  return hr;
  }



//***************************************************************************
// Misc 
//***************************************************************************

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
HRESULT COleAuto::SetBaseObject
(
  IDispatch *pdisp
)
  {
  if (!pdisp)
    {
    if (m_pdispBaseObject)
      {
      IDispatch* pdispTemp = m_pdispBaseObject;

      m_pdispBaseObject = NULL;
      pdispTemp->Release();
      }	
    }
  else
    {
    // Only allow the base object to be set once
    if (m_pdispBaseObject)
      return E_UNEXPECTED;
    m_pdispBaseObject = pdisp;
    m_pdispBaseObject->AddRef();
    }

  return S_OK;
  }

//--- EOF -------------------------------------------------------------------
