//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 
//
// File: olecontrol.cpp
//
// History:
//         7-31-96  by dli
//------------------------------------------------------------------------

#include "priv.h"

class COleControlHost;

//---------------------------------------------------------------------------
// Event sink
class CEventSink : public IDispatch
//---------------------------------------------------------------------------
{
public:
    CEventSink( BOOL bAutoDelete = FALSE ) ;

    //  Connect/disconnect
    BOOL  Connect( HWND hwndOwner, HWND hwndSite, LPUNKNOWN punkOC ) ;
    BOOL  Disconnect() ;

//  IUnknown methods
    STDMETHOD (QueryInterface)( REFIID riid, void** ppvObj ) ;
    STDMETHOD_(ULONG, AddRef)() ;
    STDMETHOD_(ULONG, Release)() ;

//  IDispatch methods
    STDMETHOD (GetTypeInfoCount)( UINT *pctinfo )
        { return E_NOTIMPL ; }

    STDMETHOD (GetTypeInfo)( UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo )
        { return E_NOTIMPL ; }

    STDMETHOD (GetIDsOfNames)( REFIID riid, LPOLESTR *rgszNames, UINT cNames,
                                LCID lcid, DISPID *rgDispId )
        { return E_NOTIMPL ; }

    STDMETHOD (Invoke)( 
        IN DISPID dispIdMember,
        IN REFIID riid,
        IN LCID lcid,
        IN WORD wFlags,
        IN OUT DISPPARAMS *pDispParams,
        OUT VARIANT *pVarResult,
        OUT EXCEPINFO *pExcepInfo,
        OUT UINT *puArgErr) ;

private:
    static HRESULT _GetDefaultEventIID( LPUNKNOWN punkOC, IID* piid ) ;
    BOOL           _Connect( HWND hwndOwner, HWND hwndSite, LPUNKNOWN punkOC, REFIID iid ) ;
    BOOL           _IsConnected( REFIID iid ) ;

    ULONG       _dwCookie ;   // connection cookie
    IID         _iid ;        // connection interface
    IID         _iidDefault ; // OC's default event dispatch interface
    LPUNKNOWN   _punkOC ;     // OC's unknown
    LONG        _cRef ;       // ref count
    HWND        _hwndSite,    // 
                _hwndOwner ;
    BOOL        _bAutoDelete ;
} ;

class CProxyUIHandler : 
    public IDocHostUIHandler2
{
public:
    
    // *** IUnknown methods *** 
    STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
    STDMETHODIMP_(ULONG) AddRef(void);
    STDMETHODIMP_(ULONG) Release(void);
    
    // *** IDocHostUIHandler methods *** 
    virtual STDMETHODIMP ShowContextMenu(DWORD dwID, POINT *ppt, IUnknown *pcmdtReserved, IDispatch *pdispReserved);
    virtual STDMETHODIMP GetHostInfo(DOCHOSTUIINFO *pInfo);
    virtual STDMETHODIMP ShowUI(DWORD dwID, IOleInPlaceActiveObject *pActiveObject, IOleCommandTarget *pCommandTarget, IOleInPlaceFrame *pFrame, IOleInPlaceUIWindow *pDoc);
    virtual STDMETHODIMP HideUI();
    virtual STDMETHODIMP UpdateUI();
    virtual STDMETHODIMP EnableModeless(BOOL fActivate);
    virtual STDMETHODIMP OnDocWindowActivate(BOOL fActivate);
    virtual STDMETHODIMP OnFrameWindowActivate(BOOL fActivate);
    virtual STDMETHODIMP ResizeBorder(LPCRECT prcBorder, IOleInPlaceUIWindow *pUIWindow, BOOL fRameWindow);
    virtual STDMETHODIMP TranslateAccelerator(LPMSG lpMsg, const GUID *pguidCmdGroup, DWORD nCmdID);
    virtual STDMETHODIMP GetOptionKeyPath(LPOLESTR *pchKey, DWORD dw);
    virtual STDMETHODIMP GetDropTarget(IDropTarget *pDropTarget, IDropTarget **ppDropTarget);
    virtual STDMETHODIMP GetExternal(IDispatch **ppDispatch);
    virtual STDMETHODIMP TranslateUrl(DWORD dwTranslate, OLECHAR *pchURLIn, OLECHAR **ppchURLOut);
    virtual STDMETHODIMP FilterDataObject( IDataObject *pDO, IDataObject **ppDORet);

    // *** IDocHostUIHandler2 methods ***
    virtual STDMETHODIMP GetOverrideKeyPath( LPOLESTR *pchKey, DWORD dw);
};

//---------------------------------------------------------------------------
//  Ole control container object
class COleControlHost : 
        public IOleClientSite,
        public IAdviseSink,
        public IOleInPlaceSite,
        public IOleInPlaceFrame,
        public IServiceProvider,
        public IOleCommandTarget,
        public IDispatch            // For ambient properties
{
friend CProxyUIHandler;

protected:
    static LRESULT CALLBACK OCHostWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

    HRESULT _Draw(HDC hdc);
    HRESULT _PersistInit();
    HRESULT _Init();
    HRESULT _Activate();
    HRESULT _Deactivate();
    HRESULT _DoVerb(long iVerb, LPMSG lpMsg);
    HRESULT _Exit();
    HRESULT _InitOCStruct(LPOCHINITSTRUCT lpocs);
    LRESULT _OnPaint();
    LRESULT _OnSize(HWND hwnd, LPARAM lParam);
    LRESULT _OnCreate(HWND hwnd, LPCREATESTRUCT);
    LRESULT _OnDestroy();
    LRESULT _OnQueryInterface(WPARAM wParam, LPARAM lParam);
    LRESULT _SetOwner(IUnknown * punkOwner);
    LRESULT _ConnectEvents( LPUNKNOWN punkOC, BOOL bConnect ) ;
    LRESULT _SetServiceProvider(IServiceProvider* pSP);
    LRESULT _SendNotify(UINT code, LPNMHDR pnmhdr);
    
    // IUnknown 
    UINT _cRef;
    
    DWORD _dwAspect;
    DWORD _dwMiscStatus;    // OLE misc status 
    DWORD _dwConnection;    // Token for Advisory connections
   
    BOOL _bInPlaceActive;   // Flag indicating if the OC is in place active
        
    HWND _hwnd;
    HWND _hwndParent;
    CLSID _clsidOC;
   
    IUnknown *_punkOC;
    IViewObject *_pIViewObject; 
    IOleObject *_pIOleObject;
    IOleInPlaceObject *_pIOleIPObject;

    IUnknown *_punkOwner;
    CEventSink  _eventSink ;
    CProxyUIHandler     _xuih;
    IDocHostUIHandler  *_pIDocHostUIParent;
    IDocHostUIHandler2 *_pIDocHostUIParent2;

    IDispatch *_pdispSiteDelegate;

public:
    COleControlHost(HWND hwnd);

    static void _RegisterClass();

    // *** IUnknown methods *** 
    STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
    STDMETHODIMP_(ULONG) AddRef(void);
    STDMETHODIMP_(ULONG) Release(void);    
    
    // IServiceProvider
    STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, LPVOID* ppvObj);
    
    // *** IOleClientSite methods *** 
    STDMETHOD (SaveObject)();
    STDMETHOD (GetMoniker)(DWORD, DWORD, LPMONIKER *);
    STDMETHOD (GetContainer)(LPOLECONTAINER *);
    STDMETHOD (ShowObject)();
    STDMETHOD (OnShowWindow)(BOOL);
    STDMETHOD (RequestNewObjectLayout)();
    
    // *** IAdviseSink methods *** 
    STDMETHOD_(void,OnDataChange)(FORMATETC *, STGMEDIUM *);
    STDMETHOD_(void,OnViewChange)(DWORD, LONG);
    STDMETHOD_(void,OnRename)(LPMONIKER);
    STDMETHOD_(void,OnSave)();
    STDMETHOD_(void,OnClose)();
    
    // *** IOleWindow Methods ***
    STDMETHOD (GetWindow) (HWND * phwnd);
    STDMETHOD (ContextSensitiveHelp) (BOOL fEnterMode);
    
    // *** IOleInPlaceSite Methods *** 
    STDMETHOD (CanInPlaceActivate) (void);
    STDMETHOD (OnInPlaceActivate) (void);
    STDMETHOD (OnUIActivate) (void);
    STDMETHOD (GetWindowContext) (IOleInPlaceFrame ** ppFrame, IOleInPlaceUIWindow ** ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo);
    STDMETHOD (Scroll) (SIZE scrollExtent);
    STDMETHOD (OnUIDeactivate) (BOOL fUndoable);
    STDMETHOD (OnInPlaceDeactivate) (void);
    STDMETHOD (DiscardUndoState) (void);
    STDMETHOD (DeactivateAndUndo) (void);
    STDMETHOD (OnPosRectChange) (LPCRECT lprcPosRect); 

    // IOleInPlaceUIWindow methods.
    STDMETHOD (GetBorder)(LPRECT lprectBorder);
    STDMETHOD (RequestBorderSpace)(LPCBORDERWIDTHS lpborderwidths);
    STDMETHOD (SetBorderSpace)(LPCBORDERWIDTHS lpborderwidths);
    STDMETHOD (SetActiveObject)(IOleInPlaceActiveObject * pActiveObject,
                                LPCOLESTR lpszObjName);

    // IOleInPlaceFrame methods
    STDMETHOD (InsertMenus)(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths);
    STDMETHOD (SetMenu)(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject);
    STDMETHOD (RemoveMenus)(HMENU hmenuShared);
    STDMETHOD (SetStatusText)(LPCOLESTR pszStatusText);
    STDMETHOD (EnableModeless)(BOOL fEnable);
    STDMETHOD (TranslateAccelerator)(LPMSG lpmsg, WORD wID);

    // IOleCommandTarget
    virtual STDMETHODIMP QueryStatus(const GUID *pguid, ULONG cCmds, MSOCMD rgCmds[], MSOCMDTEXT *pcmdtext);
    virtual STDMETHODIMP Exec(const GUID *pguid, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut);

    // IDispatch (for ambient properties)
    STDMETHODIMP GetTypeInfoCount(UINT* pctinfo);
    STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo);
    STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames,
        LCID lcid, DISPID *rgDispId);
    STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid,
        LCID lcid, WORD wFlags, DISPPARAMS  *pDispParams, VARIANT  *pvarResult,
        EXCEPINFO *pExcepInfo, UINT *puArgErr);
};

HRESULT COleControlHost::GetTypeInfoCount(UINT* pctinfo)
{
    if (_pdispSiteDelegate)
    {
        return _pdispSiteDelegate->GetTypeInfoCount(pctinfo);
    }

    return E_NOTIMPL;
}

HRESULT COleControlHost::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
{
    if (_pdispSiteDelegate)
    {
        return _pdispSiteDelegate->GetTypeInfo(iTInfo, lcid, ppTInfo);
    }

    return E_NOTIMPL;
}

HRESULT COleControlHost::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames,
        LCID lcid, DISPID *rgDispId)
{
    if (_pdispSiteDelegate)
    {
        return _pdispSiteDelegate->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId);
    }

    return E_NOTIMPL;
}

HRESULT COleControlHost::Invoke(DISPID dispIdMember, REFIID riid,
        LCID lcid, WORD wFlags, DISPPARAMS  *pDispParams, VARIANT  *pvarResult,
        EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
    if (_pdispSiteDelegate)
    {
        return _pdispSiteDelegate->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams, pvarResult, pExcepInfo, puArgErr);
    }

    return DISP_E_MEMBERNOTFOUND;
}

HRESULT COleControlHost::_Draw(HDC hdc)
{
    HRESULT hr = E_FAIL;
        
    if (_hwnd && _punkOC && !_bInPlaceActive)
    {
        RECT rc;
        GetClientRect(_hwnd, &rc);
        hr = OleDraw(_punkOC, _dwAspect, hdc, &rc);
    }
    return(hr);
}

HRESULT COleControlHost::_PersistInit()
{
    IPersistStreamInit * pIPersistStreamInit;

    if (_SendNotify(OCN_PERSISTINIT, NULL) == OCNPERSISTINIT_HANDLED)
        return S_FALSE;
    
    HRESULT hr = _punkOC->QueryInterface(IID_IPersistStreamInit, (void **)&pIPersistStreamInit);
    if (SUCCEEDED(hr))
    {
        hr = pIPersistStreamInit->InitNew();
        pIPersistStreamInit->Release();
    }
    else    
    {
        IPersistStorage * pIPersistStorage;
        hr = _punkOC->QueryInterface(IID_IPersistStorage, (void **)&pIPersistStorage);
        if (SUCCEEDED(hr))
        {
            // Create a zero sized ILockBytes.
            ILockBytes *pILockBytes;
            hr = CreateILockBytesOnHGlobal(NULL, TRUE, &pILockBytes);
            if (SUCCEEDED(hr)) {
                // Use the ILockBytes to create a storage.
                IStorage    *pIStorage;
                hr = StgCreateDocfileOnILockBytes(pILockBytes,
                                                  STGM_CREATE |
                                                  STGM_READWRITE |
                                                  STGM_SHARE_EXCLUSIVE,
                                                  0, &pIStorage);
                if (SUCCEEDED(hr)) {
                    // Call InitNew to initialize the object.
                    hr = pIPersistStorage->InitNew(pIStorage);
                    // Clean up
                    pIStorage->Release();
                } // IStorage
                pILockBytes->Release();
            } // ILockBytes
            pIPersistStorage->Release();
        }   
    }
    return hr;
}

HRESULT COleControlHost::_Init()
{
    HRESULT hr = E_FAIL;

    OCNCOCREATEMSG ocm = {0};
    ocm.clsidOC = _clsidOC;
    ocm.ppunk = &_punkOC;
    if(_SendNotify(OCN_COCREATEINSTANCE, &ocm.nmhdr) != OCNCOCREATE_HANDLED)
    {
        hr = CoCreateInstance(_clsidOC, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, 
                                  IID_IUnknown, (LPVOID *)&_punkOC);
        if (FAILED(hr))
        {
            TraceMsg(TF_OCCONTROL, "_Init: Unable to CoCreateInstance this Class ID -- hr = %lX -- hr = %lX", _clsidOC, hr);
            return hr;
        }
        
    }
    
    ASSERT(_punkOC != NULL);
        
    if (_punkOC == NULL)
        return E_FAIL;
    
    hr = _punkOC->QueryInterface(IID_IOleObject, (void **)&_pIOleObject);    
    if (FAILED(hr))
    {
        TraceMsg(TF_OCCONTROL, "_Init: Unable to QueryInterface IOleObject -- hr = %s", hr);
        return hr;
    }

    hr = _pIOleObject->GetMiscStatus(_dwAspect, &_dwMiscStatus);

    // Set the inplace active flag here
    // If this fails, we will assume that we can setclientsite later

    if (_dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST)
    {   
        hr = _pIOleObject->SetClientSite(this);
        _PersistInit();
    }
    else
    {
        _PersistInit();
        hr = _pIOleObject->SetClientSite(this);
    }
    
    if (FAILED(hr))
    {
        TraceMsg(TF_OCCONTROL, "_Init: Unable to set client site -- hr = %lX", hr);
        return hr;
    }
    
    
    if (SUCCEEDED(_punkOC->QueryInterface(IID_IViewObject, (void **)&_pIViewObject)))
    {    
        _pIViewObject->SetAdvise(_dwAspect, 0, this);
    }
    
    //FEATURE: this is not really useful because we do not handle the cases, yet 
    _pIOleObject->Advise(this, &_dwConnection);
    
    _pIOleObject->SetHostNames(TEXTW("OC Host Window"), TEXTW("OC Host Window"));
    
    return S_OK;
}

// 
HRESULT COleControlHost::_Activate()
{
    HRESULT hr = E_FAIL;
    
    RECT rcClient;
    ASSERT(_hwnd);
    
    _SendNotify(OCN_ACTIVATE, NULL);
    
    if (!GetClientRect(_hwnd, &rcClient))
        SetRectEmpty(&rcClient);
    
    hr = _pIOleObject->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, this, 0, _hwnd, &rcClient);
    
    if (SUCCEEDED(hr))
        _bInPlaceActive = TRUE;
    
    // Calling second DoVerb with OLEIVERB_SHOW because:
    // 1. If the above DoVerb fails, this is a back up activation call
    // 2. If the above DoVerb succeeds, this is also necessary because 
    //    Some embeddings needs to be explicitly told to show themselves.
    
    if (!(_dwMiscStatus & OLEMISC_INVISIBLEATRUNTIME)) 
        hr = _pIOleObject->DoVerb(OLEIVERB_SHOW, NULL, this, 0, _hwnd, &rcClient);      
    
    if (FAILED(hr))
        TraceMsg(TF_OCCONTROL, "_Activate: %d Unable to DoVerb! Error = %lX", _bInPlaceActive, hr);

    return hr;
}

HRESULT COleControlHost::_Deactivate()
{
    _SendNotify(OCN_DEACTIVATE, NULL);
    if (_pIOleIPObject)
    {
        _pIOleIPObject->InPlaceDeactivate();
        // Should be set to NULL by the above function call
        ASSERT(_pIOleIPObject == NULL);
        
        return S_OK;
    }
    
    return S_FALSE;
}

HRESULT COleControlHost::_DoVerb(long iVerb, LPMSG lpMsg)
{
    HRESULT hr = E_FAIL;
    
    RECT rcClient;
    ASSERT(_hwnd && IsWindow(_hwnd));
    
    if (!GetClientRect(_hwnd, &rcClient))
        SetRectEmpty(&rcClient);
    
    hr = _pIOleObject->DoVerb(iVerb, lpMsg, this, 0, _hwnd, &rcClient);
    
    if (SUCCEEDED(hr))
        _bInPlaceActive = TRUE;
    
#if 0 // we'll count on DocHost::DoVerb to do this if needed (or our caller?)
    // note that DocHost does this always (no OLEMISC_* check)
    if (iVerb == OLEIVERB_INPLACEACTIVATE) {
        // Calling second DoVerb with OLEIVERB_SHOW because:
        // 1. If the above DoVerb fails, this is a back up activation call
        // 2. If the above DoVerb succeeds, this is also necessary because 
        //    Some embeddings needs to be explicitly told to show themselves.
        
        if (!(_dwMiscStatus & OLEMISC_INVISIBLEATRUNTIME)) 
            hr = _pIOleObject->DoVerb(OLEIVERB_SHOW, lpMsg, this, 0, _hwnd, &rcClient);      
    }
#endif
    
    if (FAILED(hr))
        TraceMsg(TF_OCCONTROL, "_Activate: %d Unable to DoVerb! Error = %lX", _bInPlaceActive, hr);

    return hr;
}

// Clean up and Release all of interface pointers used in this object
HRESULT COleControlHost::_Exit()
{
    _SendNotify(OCN_EXIT, NULL);
    if (_pIViewObject)
    {
        _pIViewObject->SetAdvise(_dwAspect, 0, NULL);
        _pIViewObject->Release();
        _pIViewObject = NULL;
    }
    
    if (_pIOleObject)
    {
        if (_dwConnection)
        {
            _pIOleObject->Unadvise(_dwConnection);
            _dwConnection = 0;
        }
        
        _pIOleObject->Close(OLECLOSE_NOSAVE);
        _pIOleObject->SetClientSite(NULL);
        _pIOleObject->Release();
        _pIOleObject = NULL;
    }

    if (_punkOC)
    {
        ULONG ulRef;
        ulRef = _punkOC->Release();
        _punkOC = NULL;
        if (ulRef != 0)
            TraceMsg(TF_OCCONTROL, "OCHOST _Exit: After last release ref = %d > 0", ulRef);
    }
    
    ATOMICRELEASE(_pIDocHostUIParent);
    ATOMICRELEASE(_pIDocHostUIParent2);

    if (_punkOwner) {
        _punkOwner->Release();
        _punkOwner = NULL;
    }
        
    if (_pdispSiteDelegate) {
        _pdispSiteDelegate->Release();
        _pdispSiteDelegate = NULL;
    }
        
    return S_OK;
}

COleControlHost::COleControlHost(HWND hwnd)
    : _cRef(1), _dwAspect(DVASPECT_CONTENT), _hwnd(hwnd)
    {
    // These variables should be initialized to zeros automatically
    ASSERT(_dwMiscStatus == 0);
    ASSERT(_dwConnection == 0);
    ASSERT(_bInPlaceActive == FALSE);
    ASSERT(_pIDocHostUIParent  == NULL);
    ASSERT(_pIDocHostUIParent2 == NULL);
    ASSERT(_clsidOC == CLSID_NULL);
    ASSERT(_punkOC == NULL);
    ASSERT(_pIViewObject == NULL);
    ASSERT(_pIOleIPObject == NULL);
    ASSERT(_pdispSiteDelegate == NULL);

    ASSERT(_hwnd);
    
}


#ifdef DEBUG
#define _AddRef(psz) { ++_cRef; TraceMsg(TF_OCCONTROL, "CDocObjectHost(%x)::QI(%s) is AddRefing _cRef=%lX", this, psz, _cRef); }
#else
#define _AddRef(psz)    ++_cRef
#endif

// *** IUnknown Methods ***

HRESULT COleControlHost::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
    // ppvObj must not be NULL
    ASSERT(ppvObj != NULL);

    if (ppvObj == NULL)
        return E_INVALIDARG;
    
    *ppvObj = NULL;

    if ((IsEqualIID(riid, IID_IUnknown)) ||
        (IsEqualIID(riid, IID_IOleWindow)) || 
        (IsEqualIID(riid, IID_IOleInPlaceUIWindow)) || 
        (IsEqualIID(riid, IID_IOleInPlaceFrame)))
    {
        *ppvObj = SAFECAST(this, IOleInPlaceFrame *);
        TraceMsg(TF_OCCONTROL, "QI IOleInPlaceFrame succeeded");
    }
    else if (IsEqualIID(riid, IID_IServiceProvider)) 
    {
        *ppvObj = SAFECAST(this, IServiceProvider *);
        TraceMsg(TF_OCCONTROL, "QI IServiceProvider succeeded");
    }
        
    else if (IsEqualIID(riid, IID_IOleClientSite))
    {
        *ppvObj = SAFECAST(this, IOleClientSite *);
        TraceMsg(TF_OCCONTROL, "QI IOleClientSite succeeded");
    }
    else if (IsEqualIID(riid, IID_IAdviseSink))
    {
        *ppvObj = SAFECAST(this, IAdviseSink *);
        TraceMsg(TF_OCCONTROL, "QI IAdviseSink succeeded");
    }
    else if (IsEqualIID(riid, IID_IOleInPlaceSite))
    {
        *ppvObj = SAFECAST(this, IOleInPlaceSite *);
        TraceMsg(TF_OCCONTROL, "QI IOleInPlaceSite succeeded");
    } 
    else if (IsEqualIID(riid, IID_IOleCommandTarget))
    {
        *ppvObj = SAFECAST(this, IOleCommandTarget *);
        TraceMsg(TF_OCCONTROL, "QI IOleCommandTarget succeeded");
    }
    else if (NULL != _pIDocHostUIParent  && 
            IsEqualIID(riid, IID_IDocHostUIHandler))
    {
        // only implement this if the host implements it
        *ppvObj = SAFECAST(&_xuih, IDocHostUIHandler *);
        TraceMsg(TF_OCCONTROL, "QI IDocHostUIHandler succeeded");
    }
    else if (NULL != _pIDocHostUIParent2  && 
            IsEqualIID(riid, IID_IDocHostUIHandler2))
    {
        // only implement this if the host implements it
        *ppvObj = SAFECAST(&_xuih, IDocHostUIHandler2 *);
        TraceMsg(TF_OCCONTROL, "QI IDocHostUIHandler2 succeeded");
    }
    else if (IsEqualIID(riid, IID_IDispatch))
    {
        *ppvObj = SAFECAST(this, IDispatch *);
        TraceMsg(TF_OCCONTROL, "QI IDispatch succeeded");
    }
    else
        return E_NOINTERFACE;  // Otherwise, don't delegate to HTMLObj!!
     
    
    _AddRef(TEXT("IOleInPlaceSite"));
    return S_OK;
}


ULONG COleControlHost::AddRef()
{
    _cRef++;
    TraceMsg(TF_OCCONTROL, "COleControlHost(%x)::AddRef called, new _cRef=%lX", this, _cRef);
    return _cRef;
}

ULONG COleControlHost::Release()
{
    _cRef--;
    TraceMsg(TF_OCCONTROL, "COleControlHost(%x)::Release called, new _cRef=%lX", this, _cRef);
    if (_cRef > 0)
        return _cRef;

    delete this;
    return 0;
}

// ServiceProvider interfaces
HRESULT COleControlHost::QueryService(REFGUID guidService,
                                    REFIID riid, void **ppvObj)
{
    HRESULT hres = E_FAIL;
    *ppvObj = NULL;
    
    if (_punkOwner) {
        IServiceProvider *psp;
        
        _punkOwner->QueryInterface(IID_IServiceProvider, (LPVOID*)&psp);
        if (psp) {
            hres = psp->QueryService(guidService, riid, ppvObj);
            psp->Release();
        }
    }
    
    return hres;
}

// ************************ IOleClientSite methods ****************** 

HRESULT COleControlHost::SaveObject()
{
    //FEATURE: default set to E_NOTIMPL may not be correct
    HRESULT hr = E_NOTIMPL;
    
    IStorage * pIs;
    if (SUCCEEDED(_punkOC->QueryInterface(IID_IStorage, (void **)&pIs)))
    {
        IPersistStorage *pIps;
        if (SUCCEEDED(_punkOC->QueryInterface(IID_IPersistStorage, (void **)&pIps)))
        {
            OleSave(pIps, pIs, TRUE);
            pIps->SaveCompleted(NULL);
            pIps->Release();
            hr = S_OK;
        }
        pIs->Release();
    }
    
    return hr;   
}

HRESULT COleControlHost::GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER * ppMk)
{
     return E_NOTIMPL;   
}

HRESULT COleControlHost::GetContainer(LPOLECONTAINER * ppContainer)
{
    *ppContainer = NULL;       
    return E_NOINTERFACE;
}

HRESULT COleControlHost::ShowObject()
{
//    RECTL rcl;
//    POINT pt1, pt2;
    
    return S_OK;   
}

HRESULT COleControlHost::OnShowWindow(BOOL fShow)
{
    return S_OK;
}

HRESULT COleControlHost::RequestNewObjectLayout()
{
     return E_NOTIMPL;   
}

// ************************ IAdviseSink methods ********************* 
void COleControlHost::OnDataChange(FORMATETC * pFmt, STGMEDIUM * pStgMed)
{
    // NOTES: This is optional
    return;   
}
    
void COleControlHost::OnViewChange(DWORD dwAspect, LONG lIndex)
{
    // FEATURE: need to let the container know the colors might have changed
    // but don't want to deal with the paletts now

    // Draw only if not inplace active and this is the right aspect.  Inplace
    // active objects have their own window and are responsible for painting
    // themselves.
    
    // WARNING: _bInPlaceActive is not determined, yet. 
    // This funtion is called as a result of calling doverb, however, 
    // _bInPlaceActive will only be determined as DoVerb returns
    // works fine for now, but could be trouble later. 
    if ((_hwnd) && (!_bInPlaceActive) && (dwAspect == _dwAspect))
    {
        HDC hdc = GetDC(_hwnd);

        if (hdc)
        {
            _Draw(hdc);
            ReleaseDC(_hwnd, hdc);
        }
    }
}

void COleControlHost::OnRename(LPMONIKER pMoniker)
{
    return;   
}

void COleControlHost::OnSave()
{
    // NOTES: This is optional
    return;   
}

void COleControlHost::OnClose()
{
    // FEATURE: need to let the container know the colors might have changed
    return;   
}

// ************************ IOleWindow Methods ********************** 
HRESULT COleControlHost::GetWindow(HWND * lphwnd)
{
    *lphwnd = _hwnd;
    return S_OK;
}

HRESULT COleControlHost::ContextSensitiveHelp(BOOL fEnterMode)
{
    // NOTES: This is optional
    return E_NOTIMPL;   
}

// *********************** IOleInPlaceSite Methods *****************
HRESULT COleControlHost::CanInPlaceActivate(void)
{
    return S_OK;   
}

HRESULT COleControlHost::OnInPlaceActivate(void)
{
    if (!_pIOleIPObject)
        return (_punkOC->QueryInterface(IID_IOleInPlaceObject, (void **)&_pIOleIPObject));    
    else
        return S_OK;
}


HRESULT COleControlHost::OnUIActivate(void)
{
    LRESULT lres;
    OCNONUIACTIVATEMSG oam = {0};

    oam.punk = _punkOC;

    lres = _SendNotify(OCN_ONUIACTIVATE, &oam.nmhdr);
    return S_OK;
}

HRESULT COleControlHost::GetWindowContext (IOleInPlaceFrame ** ppFrame, IOleInPlaceUIWindow ** ppIIPUIWin, 
                                           LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo)
{
    *ppFrame = this;
    _AddRef("GetWindowContext");
        
    // This is set to NULL because the document window is the same as the frame 
    // window
    *ppIIPUIWin = NULL;
    
    ASSERT(_hwnd);
    if (!GetClientRect(_hwnd, lprcPosRect))
        SetRectEmpty(lprcPosRect);
    
    // Set the clip rectangle to be the same as the position rectangle
    
    CopyRect(lprcClipRect, lprcPosRect);
        
    lpFrameInfo->cb = sizeof(OLEINPLACEFRAMEINFO);
    
#ifdef MDI
    lpFrameInfo->fMDIApp = TRUE;
#else
    lpFrameInfo->fMDIApp = FALSE;
#endif
    lpFrameInfo->hwndFrame = _hwnd;
    lpFrameInfo->haccel = 0;
    lpFrameInfo->cAccelEntries = 0;
    return S_OK;
}

HRESULT COleControlHost::Scroll(SIZE scrollExtent)
{
    // Should implement later
    return E_NOTIMPL;   
}

HRESULT COleControlHost::OnUIDeactivate(BOOL fUndoable)
{
    
    return E_NOTIMPL;   
}


HRESULT COleControlHost::OnInPlaceDeactivate(void)
{
    if (_pIOleIPObject)
    {
        _pIOleIPObject->Release();
        _pIOleIPObject = NULL;
    }
    
    return S_OK;
}

HRESULT COleControlHost::DiscardUndoState(void)
{
    // Should implement later
    return E_NOTIMPL;   
}

HRESULT COleControlHost::DeactivateAndUndo(void)
{
    // Should implement later
    return E_NOTIMPL;   
}

HRESULT COleControlHost::OnPosRectChange(LPCRECT lprcPosRect) 
{
    // We do not allow the children to change the size themselves
    OCNONPOSRECTCHANGEMSG opcm = {0};
    opcm.prcPosRect = lprcPosRect;
    _SendNotify(OCN_ONPOSRECTCHANGE, &opcm.nmhdr);
    return S_OK;
}
// ************************ IOleInPlaceUIWindow methods *************

HRESULT COleControlHost::GetBorder(LPRECT lprectBorder)
{
    return E_NOTIMPL;
}

HRESULT COleControlHost::RequestBorderSpace(LPCBORDERWIDTHS lpborderwidths)
{
    return E_NOTIMPL;
}

HRESULT COleControlHost::SetBorderSpace(LPCBORDERWIDTHS lpborderwidths)
{
    return E_NOTIMPL;
}

HRESULT COleControlHost::SetActiveObject(IOleInPlaceActiveObject * pActiveObject,
                                LPCOLESTR lpszObjName)
{
    return E_NOTIMPL;
}

// *********************** IOleInPlaceFrame Methods *****************
HRESULT COleControlHost::InsertMenus(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths)
{
    // Should implement later
    return E_NOTIMPL;   
}

HRESULT COleControlHost::SetMenu(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject)
{
    // Should implement later
    return E_NOTIMPL;   
}

HRESULT COleControlHost::RemoveMenus(HMENU hmenuShared)
{
    // Should implement later
    return E_NOTIMPL;   
}

HRESULT COleControlHost::SetStatusText(LPCOLESTR pszStatusText)
{
    OCNONSETSTATUSTEXTMSG osst = {0};
    osst.pwszStatusText = pszStatusText;
    _SendNotify(OCN_ONSETSTATUSTEXT, &osst.nmhdr);
    return S_OK;
}

HRESULT COleControlHost::EnableModeless(BOOL fEnable)
{
    // Should implement later
    return E_NOTIMPL;   
}

HRESULT COleControlHost::TranslateAccelerator(LPMSG lpmsg, WORD wID)
{
    // Should implement later
    return E_NOTIMPL;   
}

// ************************ IOleCommandTarget Methods *************
HRESULT COleControlHost::QueryStatus(const GUID *pguid, ULONG cCmds, MSOCMD rgCmds[], MSOCMDTEXT *pcmdtext)
{
    return IUnknown_QueryStatus(_punkOwner, pguid, cCmds, rgCmds, pcmdtext);
}

HRESULT COleControlHost::Exec(const GUID *pguid, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
{
    return IUnknown_Exec(_punkOwner, pguid, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
}

HRESULT COleControlHost::_InitOCStruct(LPOCHINITSTRUCT lpocs)
{               
    HRESULT hres = E_FAIL;

    if (_punkOC)
        return S_FALSE;
    
    if (lpocs)
    {
        if (lpocs->cbSize != SIZEOF(OCHINITSTRUCT))
            return hres;

        if (lpocs->clsidOC == CLSID_NULL)
            return hres;

        _clsidOC = lpocs->clsidOC;
        _SetOwner(lpocs->punkOwner);
    }
    else 
        return hres;

    hres = _Init();
    if (SUCCEEDED(hres))
        hres = _Activate();    
    
    return hres;
}

LRESULT COleControlHost::_OnPaint()
{
    ASSERT(_hwnd);

    PAINTSTRUCT ps;

    HDC hdc = BeginPaint(_hwnd, &ps);    
    _Draw(hdc);
    EndPaint(_hwnd, &ps);
    return 0;
}

LRESULT COleControlHost::_OnSize(HWND hwnd, LPARAM lParam)
{
    if (_pIOleIPObject)
    {
        RECT rcPos, rcClip ;
        SetRect( &rcPos, 0, 0, LOWORD(lParam), HIWORD(lParam) ) ;
        rcClip = rcPos ;
        _pIOleIPObject->SetObjectRects(&rcPos, &rcClip);
    }
    return 0;
}

LRESULT COleControlHost::_OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{   
    TCHAR szClsid[50];
    _hwndParent = GetParent(hwnd);
    SetWindowLongPtr(hwnd, 0, (LONG_PTR)this);
    
    LPOCHINITSTRUCT lpois = (LPOCHINITSTRUCT)lpcs->lpCreateParams;
    HRESULT hres = S_OK;
        
    if (lpois)
        hres = _InitOCStruct(lpois);
    else if (GetWindowText(hwnd, szClsid, ARRAYSIZE(szClsid)))
    {
        OCHINITSTRUCT ois;
        ois.cbSize = SIZEOF(OCHINITSTRUCT);
        if (FAILED(SHCLSIDFromString(szClsid, &ois.clsidOC)))
            ois.clsidOC = CLSID_NULL;
        ois.punkOwner = NULL;
        
        hres = _InitOCStruct(&ois);
    }
    
    if (FAILED(hres))
        return -1;
    return 0;
}

LRESULT COleControlHost::_OnDestroy()
{    
    ASSERT(_hwnd);
    SetWindowLongPtr(_hwnd, 0, 0);
    _ConnectEvents( _punkOC, FALSE ) ;
    _Deactivate();
    _Exit();
    Release();
   
    return 0;
}

LRESULT COleControlHost::_OnQueryInterface(WPARAM wParam, LPARAM lParam)
{
    if (lParam)
    {
        QIMSG * qiMsg = (QIMSG *)lParam;
        return _punkOC->QueryInterface(*qiMsg->qiid, qiMsg->ppvObject);
    }
    return -1;
}

LRESULT COleControlHost::_SetOwner(IUnknown * punkNewOwner)
{
    if (_punkOwner)
        _punkOwner->Release();
    _punkOwner = punkNewOwner;
    if (_punkOwner)
        _punkOwner->AddRef();

    ATOMICRELEASE(_pIDocHostUIParent);
    ATOMICRELEASE(_pIDocHostUIParent2);

    // Query if owner supports IDocHostUIHandler, if so then
    // we turn on our delegating wrapper
    if (punkNewOwner)
    {
        punkNewOwner->QueryInterface(IID_IDocHostUIHandler,  (LPVOID *)&_pIDocHostUIParent);
        punkNewOwner->QueryInterface(IID_IDocHostUIHandler2, (LPVOID *)&_pIDocHostUIParent2);
    }
    return 0;
}

LRESULT COleControlHost::_ConnectEvents( LPUNKNOWN punkOC, BOOL bConnect )
{
    if( bConnect )
    {
        ASSERT( punkOC ) ;
        return _eventSink.Connect( _hwndParent, _hwnd, punkOC ) ;
    }
    return _eventSink.Disconnect() ;
}

LRESULT COleControlHost::_SetServiceProvider(IServiceProvider* pSP)
{
    // Free any existing delegates
    if (_pdispSiteDelegate)
    {
        _pdispSiteDelegate->Release();
    }
    
    // For now, we just delegate IDispatch (Ambient properties) calls
    HRESULT hr = pSP->QueryService(SID_OleControlSite, IID_PPV_ARG(IDispatch, &_pdispSiteDelegate));

    if (FAILED(hr))
    {
        _pdispSiteDelegate = NULL;
    }

    return 0;
}

LRESULT COleControlHost::_SendNotify(UINT code, LPNMHDR pnmhdr)
{
    NMHDR nmhdr;
    ASSERT(_hwnd);

    if (!_hwndParent)
        return 0;
   
    if (!pnmhdr)
        pnmhdr = &nmhdr;
    pnmhdr->hwndFrom = _hwnd;  
    pnmhdr->idFrom = GetDlgCtrlID( _hwnd ) ;
    pnmhdr->code = code;
    
    return SendMessage(_hwndParent, WM_NOTIFY, 0, (LPARAM)pnmhdr);
}



LRESULT CALLBACK COleControlHost::OCHostWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    COleControlHost *pcoch = (COleControlHost *)GetWindowPtr(hwnd, 0);

    if (!pcoch && (uMsg != WM_CREATE))
        return DefWindowProcWrap(hwnd, uMsg, wParam, lParam);            
    
    switch(uMsg)
    {
    case WM_CREATE:
        pcoch = new COleControlHost(hwnd);
        if (pcoch)
            return pcoch->_OnCreate(hwnd, (LPCREATESTRUCT)lParam);
        return -1;

    case WM_ERASEBKGND:
        if (pcoch->_punkOC && pcoch->_bInPlaceActive)
        {
            //  Now tell windows we don't need no stinkin'
            //  erased background because our view object
            //  is in-place active and he/she will be
            //  taking over from here.
            return TRUE;
        }
        break;
        
    case WM_PAINT:
        return pcoch->_OnPaint();
        
    case WM_SIZE:
        return pcoch->_OnSize(hwnd, lParam);
        
    case WM_DESTROY:
        return pcoch->_OnDestroy();
        
    case OCM_QUERYINTERFACE:
        return  pcoch->_OnQueryInterface(wParam, lParam);
        
    case OCM_INITIALIZE:
        return pcoch->_InitOCStruct((LPOCHINITSTRUCT)lParam);
        
    case OCM_SETOWNER:
        return pcoch->_SetOwner((IUnknown*)lParam);
    
    case OCM_DOVERB:
        return pcoch->_DoVerb((long)wParam, (LPMSG)lParam);

    case OCM_ENABLEEVENTS:
        return pcoch->_ConnectEvents( pcoch->_punkOC, (BOOL)wParam ) ;

    case OCM_SETSERVICEPROVIDER:
        return pcoch->_SetServiceProvider((IServiceProvider*) lParam);
    
    case WM_PALETTECHANGED:
        if (pcoch->_pIOleIPObject) {
            HWND hwnd;
            if (SUCCEEDED(pcoch->_pIOleIPObject->GetWindow(&hwnd))) {
                SendMessage(hwnd, WM_PALETTECHANGED, wParam, lParam);
                }
        }   
        break;

    case WM_SETFOCUS:
        
        //  OC doesn't respond to OLEIVERB_UIACTIVATE ?
        if( pcoch->_dwMiscStatus & OLEMISC_NOUIACTIVATE )
        {
            //  so explicitly assign focus
            HWND hwndObj ;
            if( pcoch->_pIOleIPObject && 
                SUCCEEDED( pcoch->_pIOleIPObject->GetWindow( &hwndObj ) ) )
                SetFocus( hwndObj ) ;
        }
        else
            pcoch->_DoVerb( OLEIVERB_UIACTIVATE, NULL ) ;

        break ;

        
    default:
        return DefWindowProcWrap(hwnd, uMsg, wParam, lParam);            
    }
    
    return 0;
}

void COleControlHost::_RegisterClass()
{
    WNDCLASS wc = {0};

    wc.style         = CS_GLOBALCLASS;
    wc.lpfnWndProc   = OCHostWndProc;
    //wc.cbClsExtra    = 0;
    wc.cbWndExtra    = SIZEOF(LPVOID);
    wc.hInstance     = HINST_THISDLL;
    //wc.hIcon         = NULL;
    wc.hCursor       = LoadCursor (NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND + 1);
    //wc.lpszMenuName  = NULL;
    wc.lpszClassName = OCHOST_CLASS;
    SHRegisterClass(&wc);
}


HRESULT CProxyUIHandler::QueryInterface(REFIID riid, LPVOID * ppvObj)
{   
    COleControlHost *poch = IToClass(COleControlHost, _xuih, this);

    return poch->QueryInterface(riid, ppvObj); 
};

ULONG CProxyUIHandler::AddRef(void)
{   
    COleControlHost *poch = IToClass(COleControlHost, _xuih, this);

    return poch->AddRef(); 
};

ULONG CProxyUIHandler::Release(void)
{   
    COleControlHost *poch = IToClass(COleControlHost, _xuih, this);

    return poch->Release(); 
};

HRESULT CProxyUIHandler::ShowContextMenu(DWORD dwID, POINT *ppt, IUnknown *pcmdtReserved, IDispatch *pdispReserved)
{
    COleControlHost *poch = IToClass(COleControlHost, _xuih, this);

    return poch->_pIDocHostUIParent ? poch->_pIDocHostUIParent->ShowContextMenu(dwID, ppt, pcmdtReserved, pdispReserved) : E_NOTIMPL;
}

HRESULT CProxyUIHandler::GetHostInfo(DOCHOSTUIINFO *pInfo)
{
    COleControlHost *poch = IToClass(COleControlHost, _xuih, this);

    return poch->_pIDocHostUIParent ? poch->_pIDocHostUIParent->GetHostInfo(pInfo) : E_NOTIMPL;
}

HRESULT CProxyUIHandler::ShowUI(DWORD dwID, IOleInPlaceActiveObject *pActiveObject, IOleCommandTarget *pCommandTarget, IOleInPlaceFrame *pFrame, IOleInPlaceUIWindow *pDoc)
{
    COleControlHost *poch = IToClass(COleControlHost, _xuih, this);

    return poch->_pIDocHostUIParent ? poch->_pIDocHostUIParent->ShowUI(dwID, pActiveObject, pCommandTarget, pFrame, pDoc): E_NOTIMPL;
}

HRESULT CProxyUIHandler::HideUI()
{
    COleControlHost *poch = IToClass(COleControlHost, _xuih, this);

    return poch->_pIDocHostUIParent ? poch->_pIDocHostUIParent->HideUI(): E_NOTIMPL;
}

HRESULT CProxyUIHandler::UpdateUI()
{
    COleControlHost *poch = IToClass(COleControlHost, _xuih, this);

    return poch->_pIDocHostUIParent ? poch->_pIDocHostUIParent->UpdateUI(): E_NOTIMPL;
}

HRESULT CProxyUIHandler::EnableModeless(BOOL fActivate)
{
    COleControlHost *poch = IToClass(COleControlHost, _xuih, this);

    return poch->_pIDocHostUIParent ? poch->_pIDocHostUIParent->EnableModeless(fActivate): E_NOTIMPL;
}

HRESULT CProxyUIHandler::OnDocWindowActivate(BOOL fActivate)
{
    COleControlHost *poch = IToClass(COleControlHost, _xuih, this);

    return poch->_pIDocHostUIParent ? poch->_pIDocHostUIParent->OnDocWindowActivate(fActivate): E_NOTIMPL;
}

HRESULT CProxyUIHandler::OnFrameWindowActivate(BOOL fActivate)
{
    COleControlHost *poch = IToClass(COleControlHost, _xuih, this);

    return poch->_pIDocHostUIParent ? poch->_pIDocHostUIParent->OnFrameWindowActivate(fActivate): E_NOTIMPL;
}

HRESULT CProxyUIHandler::ResizeBorder(LPCRECT prcBorder, IOleInPlaceUIWindow *pUIWindow, BOOL fRameWindow)
{
    COleControlHost *poch = IToClass(COleControlHost, _xuih, this);

    return poch->_pIDocHostUIParent ? poch->_pIDocHostUIParent->ResizeBorder(prcBorder, pUIWindow, fRameWindow): E_NOTIMPL;
}

HRESULT CProxyUIHandler::TranslateAccelerator(LPMSG lpMsg, const GUID *pguidCmdGroup, DWORD nCmdID)
{
    COleControlHost *poch = IToClass(COleControlHost, _xuih, this);

    return poch->_pIDocHostUIParent ? poch->_pIDocHostUIParent->TranslateAccelerator(lpMsg, pguidCmdGroup, nCmdID): E_NOTIMPL;
}

HRESULT CProxyUIHandler::GetOptionKeyPath(LPOLESTR *pchKey, DWORD dw)
{
    COleControlHost *poch = IToClass(COleControlHost, _xuih, this);

    return poch->_pIDocHostUIParent ? poch->_pIDocHostUIParent->GetOptionKeyPath(pchKey, dw): E_NOTIMPL;
}

HRESULT CProxyUIHandler::GetDropTarget(IDropTarget *pDropTarget, IDropTarget **ppDropTarget)
{
    COleControlHost *poch = IToClass(COleControlHost, _xuih, this);

    return poch->_pIDocHostUIParent ? poch->_pIDocHostUIParent->GetDropTarget(pDropTarget, ppDropTarget) : E_NOTIMPL;
}

HRESULT CProxyUIHandler::GetExternal(IDispatch **ppDispatch)
{
    COleControlHost *poch = IToClass(COleControlHost, _xuih, this);

    return poch->_pIDocHostUIParent ? poch->_pIDocHostUIParent->GetExternal(ppDispatch) : E_NOTIMPL;
}

HRESULT CProxyUIHandler::TranslateUrl(DWORD dwTranslate, OLECHAR *pchURLIn, OLECHAR **ppchURLOut)
{
    COleControlHost *poch = IToClass(COleControlHost, _xuih, this);

    return poch->_pIDocHostUIParent ? poch->_pIDocHostUIParent->TranslateUrl(dwTranslate, pchURLIn, ppchURLOut) : E_NOTIMPL;
}

HRESULT CProxyUIHandler::FilterDataObject( IDataObject *pDO, IDataObject **ppDORet)
{
    COleControlHost *poch = IToClass(COleControlHost, _xuih, this);

    return poch->_pIDocHostUIParent ? poch->_pIDocHostUIParent->FilterDataObject(pDO, ppDORet) : E_NOTIMPL;
}

HRESULT CProxyUIHandler::GetOverrideKeyPath( LPOLESTR *pchKey, DWORD dw)
{
    COleControlHost *poch = IToClass(COleControlHost, _xuih, this);

    return poch->_pIDocHostUIParent2 ? poch->_pIDocHostUIParent2->GetOverrideKeyPath(pchKey, dw) : E_NOTIMPL;    
}

STDAPI_(BOOL) DllRegisterWindowClasses(const SHDRC * pshdrc)
{
    if (pshdrc && pshdrc->cbSize == SIZEOF(SHDRC) && !(pshdrc->dwFlags & ~SHDRCF_ALL))
    {
        if (pshdrc->dwFlags & SHDRCF_OCHOST)
        {
            COleControlHost::_RegisterClass();
            return TRUE;
        }
    }
    return FALSE;
}

//---------------------------------------------------------------------------
//  CEventSink constructor
CEventSink::CEventSink( BOOL bAutoDelete )
    :    _hwndSite(NULL),
         _hwndOwner(NULL),
         _punkOC(NULL),
         _dwCookie(0),
         _cRef(1),
         _bAutoDelete( bAutoDelete )
{
    _iid = _iidDefault = IID_NULL ;
}

//  CEventSink IUnknown impl
STDMETHODIMP CEventSink::QueryInterface( REFIID riid, void** ppvObj )
{
    *ppvObj = NULL ;
    if( IsEqualGUID( riid, IID_IUnknown ) || 
        IsEqualGUID( riid, IID_IDispatch )||
        IsEqualGUID( riid, _iidDefault ) )
    {
        *ppvObj = this ;
        return S_OK ;
    }
    return E_NOINTERFACE ;
}

STDMETHODIMP_(ULONG) CEventSink::AddRef()
{ 
    return InterlockedIncrement( &_cRef ) ;
}

STDMETHODIMP_(ULONG) CEventSink::Release()
{
    if( InterlockedDecrement( &_cRef ) <= 0 )
    {
        if( _bAutoDelete )
            delete this ;
        return 0 ; 
    }
    return _cRef ;
}

//  Connects the sink to the OC's default event dispatch interface.
BOOL CEventSink::Connect( HWND hwndOwner, HWND hwndSite, LPUNKNOWN punkOC )
{
    ASSERT( punkOC ) ;
    IID iidDefault = IID_NULL ;

    if( SUCCEEDED( _GetDefaultEventIID( punkOC, &iidDefault ) ) )
    {
        _iidDefault = iidDefault ;
        return _Connect( hwndOwner, hwndSite, punkOC, iidDefault ) ;
    }
    return FALSE ;
}

//  Establishes advise connection on the specified interface
BOOL CEventSink::_Connect( HWND hwndOwner, HWND hwndSite, LPUNKNOWN punkOC, REFIID iid )
{
    LPCONNECTIONPOINTCONTAINER pcpc;
    ASSERT(punkOC != NULL) ;
    HRESULT hr = CONNECT_E_CANNOTCONNECT ;

    if( _IsConnected( iid ) )
        return TRUE ;

    if( _dwCookie )
        Disconnect() ;

    if( punkOC &&
        SUCCEEDED( punkOC->QueryInterface(IID_IConnectionPointContainer, (LPVOID*)&pcpc )))
    {
        LPCONNECTIONPOINT pcp = NULL;
        DWORD             dwCookie = 0;
        ASSERT(pcpc != NULL);

        if( SUCCEEDED(pcpc->FindConnectionPoint( iid, &pcp )))
        {
            ASSERT(pcp != NULL);
            hr = pcp->Advise( this, &dwCookie ) ;
            
            if( SUCCEEDED( hr ) )
            {
                _iid = iid ;
                _dwCookie  = dwCookie ;
                _hwndOwner = hwndOwner ;
                _hwndSite  = hwndSite ;
                _punkOC    = punkOC ;
                _punkOC->AddRef() ;
            }
            pcp->Release();
        }
        pcpc->Release();
    }

    return SUCCEEDED( hr ) ;
}

//  Retrieves default event dispatch interface from the OC.
HRESULT CEventSink::_GetDefaultEventIID( LPUNKNOWN punkOC, IID* piid )
{
    HRESULT hr ;

    ASSERT( punkOC ) ;
    ASSERT( piid ) ;

    IProvideClassInfo  *pci ;
    IProvideClassInfo2 *pci2 ;
    *piid = IID_NULL ;

    #define IMPLTYPE_MASK \
        (IMPLTYPEFLAG_FDEFAULT|IMPLTYPEFLAG_FSOURCE|IMPLTYPEFLAG_FRESTRICTED)
    #define IMPLTYPE_DEFAULTSOURCE \
        (IMPLTYPEFLAG_FDEFAULT|IMPLTYPEFLAG_FSOURCE)

    //  Retrieve default outbound dispatch IID using OC's IProvideClassInfo2
    if( SUCCEEDED( (hr = punkOC->QueryInterface( IID_IProvideClassInfo2, (void**)&pci2 )) ) )
    {
        hr = pci2->GetGUID( GUIDKIND_DEFAULT_SOURCE_DISP_IID, piid ) ;
        pci2->Release() ;
    }
    else // no IProvideClassInfo2; try IProvideClassInfo:
    if( SUCCEEDED( (hr = punkOC->QueryInterface( IID_IProvideClassInfo, (void**)&pci )) ) )
    {
        ITypeInfo* pClassInfo = NULL;

        if( SUCCEEDED( (hr = pci->GetClassInfo( &pClassInfo )) ) )
        {
            LPTYPEATTR pClassAttr;
            ASSERT( pClassInfo );

            if( SUCCEEDED( (hr = pClassInfo->GetTypeAttr( &pClassAttr )) ) )
            {
                ASSERT( pClassAttr ) ;
                ASSERT( pClassAttr->typekind == TKIND_COCLASS ) ;

                // Enumerate implemented interfaces looking for default source IID.
                HREFTYPE hRefType;
                int      nFlags;

                for( UINT i = 0; i < pClassAttr->cImplTypes; i++ )
                {
                    if( SUCCEEDED( (hr = pClassInfo->GetImplTypeFlags( i, &nFlags )) ) &&
                        ((nFlags & IMPLTYPE_MASK) == IMPLTYPE_DEFAULTSOURCE) )
                    {
                        // Got the interface, now retrieve its IID:
                        ITypeInfo* pEventInfo = NULL ;

                        if( SUCCEEDED( (hr = pClassInfo->GetRefTypeOfImplType( i, &hRefType )) ) &&
                            SUCCEEDED( (hr = pClassInfo->GetRefTypeInfo( hRefType, &pEventInfo )) ) )
                        {
                            LPTYPEATTR pEventAttr;
                            ASSERT( pEventInfo ) ;

                            if( SUCCEEDED( (hr = pEventInfo->GetTypeAttr( &pEventAttr )) ) )
                            {
                                *piid = pEventAttr->guid ; 
                                pEventInfo->ReleaseTypeAttr(pEventAttr);
                            }
                            pEventInfo->Release();
                        }
                        break;
                    }
                }
                pClassInfo->ReleaseTypeAttr(pClassAttr);
            }
            pClassInfo->Release();
        }
        pci->Release() ;
    }

    if( SUCCEEDED( hr ) && IsEqualIID( *piid, IID_NULL ) )
        hr = E_FAIL ;

    return hr ;
}

//  reports whether the sink is connected to the indicated sink
BOOL CEventSink::_IsConnected( REFIID iid )
{
    return _dwCookie != 0L && 
           IsEqualIID( iid, _iid ) ;
}

//  disconnects the sink
BOOL CEventSink::Disconnect()
{
    LPCONNECTIONPOINTCONTAINER pcpc;

    if( _dwCookie != 0 &&
        _punkOC &&
        SUCCEEDED( _punkOC->QueryInterface(IID_IConnectionPointContainer, (LPVOID*)&pcpc)))
    {
        LPCONNECTIONPOINT pcp = NULL;
        ASSERT(pcpc != NULL);

        if (SUCCEEDED(pcpc->FindConnectionPoint(_iid, &pcp)))
        {
            ASSERT(pcp != NULL);
            pcp->Unadvise(_dwCookie);
            pcp->Release();

            _iid        = IID_NULL ;
            _dwCookie   = 0L ;
            _hwndOwner = NULL ;
            _hwndSite  = NULL ;
            _punkOC->Release() ;
            _punkOC     = NULL ;
        }
        pcpc->Release();
        return TRUE ;
    }

    return FALSE ;
}

//  CEventSink IDispatch interface
STDMETHODIMP CEventSink::Invoke( 
    IN DISPID dispIdMember,
    IN REFIID riid,
    IN LCID lcid,
    IN WORD wFlags,
    IN OUT DISPPARAMS *pDispParams,
    OUT VARIANT *pVarResult,
    OUT EXCEPINFO *pExcepInfo,
    OUT UINT *puArgErr)
{
    //  Copy method args to notification block
    NMOCEVENT   event ;
    ZeroMemory( &event, sizeof(event) ) ;
    event.hdr.hwndFrom = _hwndSite;  
    event.hdr.idFrom   = GetDlgCtrlID( _hwndSite ) ;
    event.hdr.code     = OCN_OCEVENT ;
    event.dispID       = dispIdMember ;
    event.iid          = riid ;
    event.lcid         = lcid ;
    event.wFlags       = wFlags ;
    event.pDispParams  = pDispParams ;
    event.pVarResult   = pVarResult ;
    event.pExepInfo    = pExcepInfo ;
    event.puArgErr     = puArgErr ;

    //  Notify parent of event
    ::SendMessage( _hwndOwner, WM_NOTIFY, event.hdr.idFrom, (LPARAM)&event ) ;
    
    //  Cleanup args
    if (pVarResult != NULL)
        VariantClear( pVarResult ) ;

    return S_OK ;
}
