AUTOOBJ.CPP
//=--------------------------------------------------------------------------= 
// 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(); 
}