#include "priv.h" class CMyHlinkSrc : public IHlinkSource { friend HRESULT CMyHlinkSrc_CreateInstance(REFCLSID rclsid, DWORD grfContext, REFIID riid, LPVOID* ppvOut); public: // *** IUnknown methods *** virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj); virtual STDMETHODIMP_(ULONG) AddRef(void) ; virtual STDMETHODIMP_(ULONG) Release(void); // *** IHlinkSource methods *** virtual STDMETHODIMP SetBrowseContext( IHlinkBrowseContext *pihlbc); virtual STDMETHODIMP GetBrowseContext( IHlinkBrowseContext **ppihlbc); virtual STDMETHODIMP Navigate( DWORD grfHLNF, LPCWSTR pwzJumpLocation); virtual STDMETHODIMP GetMoniker( LPCWSTR pwzLocation, DWORD dwAssign, IMoniker **ppimkLocation); virtual STDMETHODIMP GetFriendlyName( LPCWSTR pwzLocation, LPWSTR *ppwzFriendlyName); protected: CMyHlinkSrc(); ~CMyHlinkSrc(); UINT _cRef; IUnknown* _punkInner; // aggregated inner object IHlinkSource* _phlsrc; // cached IHlinkSource IHlinkBrowseContext* _phlbc; }; CMyHlinkSrc::CMyHlinkSrc() : _cRef(1), _punkInner(NULL), _phlsrc(NULL), _phlbc(NULL) { DllAddRef(); } CMyHlinkSrc::~CMyHlinkSrc() { DllRelease(); } // // This function returns an aggregated object // HRESULT CMyHlinkSrc_CreateInstance(REFCLSID rclsid, DWORD grfContext, REFIID riid, LPVOID* ppvOut) { HRESULT hres = E_OUTOFMEMORY; *ppvOut = NULL; CMyHlinkSrc* phlsrcOuter = new CMyHlinkSrc(); if (phlsrcOuter) { hres = CoCreateInstance(rclsid, phlsrcOuter, grfContext, IID_IUnknown, (LPVOID*)&phlsrcOuter->_punkInner); if (SUCCEEDED(hres)) { // TraceMsg(DM_TRACE, "sdv TR CMyHlinkSrc_CreateInstenace CoCreateSucceeded"); // Cache IHlinkSource of the inner object (if any). HRESULT hresT = phlsrcOuter->_punkInner->QueryInterface( IID_IHlinkSource, (LPVOID*)&phlsrcOuter->_phlsrc); // TraceMsg(DM_TRACE, "sdv TR CMyHlinkSrc_CreateInstenace QI(IID_IHlinkSource) returned (%x)", hres); if (SUCCEEDED(hresT)) { // // Decrement the reference count to avoid cycled reference. // See "The COM Programmer's Cookbook for detail. // phlsrcOuter->Release(); } hres = phlsrcOuter->QueryInterface(riid, ppvOut); } else { TraceMsg(DM_TRACE, "sdv TR CMyHlinkSrc_CreateInstenace CoCI failed (%x)", hres); } phlsrcOuter->Release(); } // TraceMsg(DM_TRACE, "sdv TR CMyHlinkSrc_CreateInstenace leaving"); return hres; } HRESULT CMyHlinkSrc::QueryInterface(REFIID riid, LPVOID * ppvObj) { if (IsEqualIID(riid, IID_IUnknown)) { *ppvObj = (IUnknown*)this; _cRef++; return S_OK; } else if (IsEqualIID(riid, IID_IHlinkSource)) { // // If the inner object supports IHlinkSource, return it; // otherwise, return our own. // TraceMsg(DM_TRACE, "sdv TR CMyHlinkSrc::QueryInterface IID_IHlinkSource called"); *ppvObj = _phlsrc ? _phlsrc : (IHlinkSource*)this; _cRef++; return S_OK; } else if (_punkInner) { // // Delegate QI down to the inner object. This technique is // called "Blind QueryInterfcae" in the COM Programmer's Cookbook. // This book says, we shouldn't use this technique unless we modify // any behavior of other interfaces. In this case, we don't modify // any behavior and it's safe to use this technique. // // TraceMsg(DM_TRACE, "sdv TR CMyHlinkSrc::QueryInterface delegating QI to inner object"); return _punkInner->QueryInterface(riid, ppvObj); } *ppvObj = NULL; return E_NOINTERFACE; } ULONG CMyHlinkSrc::AddRef(void) { // TraceMsg(DM_TRACE, "sdv TR CMyHlinkSrc::AddRef new _cRef is %d", _cRef+1); return ++_cRef; } ULONG CMyHlinkSrc::Release(void) { if (--_cRef > 0) { // TraceMsg(DM_TRACE, "sdv TR CMyHlinkSrc::Release new _cRef is %d", _cRef); return _cRef; } TraceMsg(DM_TRACE, "sdv TR CMyHlinkSrc::Release deleting this object ----- (YES!)"); if (_phlbc) { _phlbc->Release(); } _cRef = 1; // guard (to be recursively hit this code) if (_phlsrc) { AddRef(); // balance the ref. count _phlsrc->Release(); // release the cached interface } if (_punkInner) { _punkInner->Release(); } ASSERT(_cRef == 1); delete this; return 0; } // *** IHlinkSource methods *** HRESULT CMyHlinkSrc::SetBrowseContext( IHlinkBrowseContext *pihlbc) { if (_phlbc) { _phlbc->Release(); } _phlbc = pihlbc; if (_phlbc) { _phlbc->AddRef(); } return S_OK; } HRESULT CMyHlinkSrc::GetBrowseContext( IHlinkBrowseContext **ppihlbc) { *ppihlbc = _phlbc; if (_phlbc) { _phlbc->AddRef(); } return S_OK; } HRESULT CMyHlinkSrc::Navigate( DWORD grfHLNF, LPCWSTR pwzJumpLocation) { IOleDocumentView* pmsov = NULL; HRESULT hres = _punkInner->QueryInterface(IID_IOleDocumentView, (LPVOID*)&pmsov); if (SUCCEEDED(hres)) { hres = pmsov->UIActivate(TRUE); TraceMsg(DM_TRACE, "sdv TR CHS::Navigate pmsov->UIActivate() returned %x", hres); if (SUCCEEDED(hres)) { // HlinkOnNavigate } pmsov->Release(); } else { TraceMsg(DM_TRACE, "sdv TR CHS::Navigate _punkInner->QI(IID_Mso) failed"); } return S_OK; } HRESULT CMyHlinkSrc::GetMoniker( LPCWSTR pwzLocation, DWORD dwAssign, IMoniker **ppimkLocation) { return E_NOTIMPL; } HRESULT CMyHlinkSrc::GetFriendlyName( LPCWSTR pwzLocation, LPWSTR *ppwzFriendlyName) { return E_NOTIMPL; } // // Almost identical copy of OleCreate, which allows us to pass // the punkOuter. // HRESULT CMyHlinkSrc_OleCreate(CLSID rclsid, REFIID riid, DWORD renderOpt, FORMATETC* pFormatEtc, IOleClientSite* pclient, IStorage* pstg, LPVOID* ppvOut) { HRESULT hres; *ppvOut = NULL; // assume error IUnknown* punk; hres = CMyHlinkSrc_CreateInstance(rclsid, CLSCTX_INPROC, IID_IUnknown, (LPVOID*)&punk); if (SUCCEEDED(hres)) { // Artificial one-time loop, which allows us to easily // handle error cases by saying "if (FAILED(hres)) break;" do { // Call IPersistStorage::InitNew IPersistStorage* ppstg; hres = punk->QueryInterface(IID_IPersistStorage, (LPVOID*)&ppstg); if (FAILED(hres)) break; hres = ppstg->InitNew(pstg); ppstg->Release(); if (FAILED(hres)) break; // Call IOleObject::SetClientSite IOleObject* pole; hres = punk->QueryInterface(IID_IOleObject, (LPVOID*)&pole); if (FAILED(hres)) break; hres = pole->SetClientSite(pclient); pole->Release(); if (FAILED(hres)) break; hres = punk->QueryInterface(riid, ppvOut); } while (0); punk->Release(); } return hres; } // // Almost identical copy of OleLoad, which allows us to pass // the punkOuter. // HRESULT CMyHlinkSrc_OleLoad(IStorage* pstg, REFIID riid, IOleClientSite* pclient, LPVOID* ppvOut) { // TraceMsg(DM_TRACE, "sdv TR CMyHlinkSrc_OleLoad called"); HRESULT hres; *ppvOut = NULL; // assume error STATSTG statstg; hres = pstg->Stat(&statstg, STATFLAG_NONAME); if (SUCCEEDED(hres)) { IUnknown* punk; hres = CMyHlinkSrc_CreateInstance(statstg.clsid, CLSCTX_INPROC, IID_IUnknown, (LPVOID*)&punk); if (SUCCEEDED(hres)) { // Artificial one-time loop, which allows us to easily // handle error cases by saying "if (FAILED(hres)) break;" do { // TraceMsg(DM_TRACE, "sdv TR CMyHlinkSrc_OladLoad calling IPS::Load"); // Call IPersistStorage::Load IPersistStorage* ppstg; hres = punk->QueryInterface(IID_IPersistStorage, (LPVOID*)&ppstg); if (FAILED(hres)) break; hres = ppstg->Load(pstg); ppstg->Release(); if (FAILED(hres)) break; // TraceMsg(DM_TRACE, "sdv TR CMyHlinkSrc_OladLoad calling IOO::SetClientSite"); // Call IOleObject::SetClientSite IOleObject* pole; hres = punk->QueryInterface(IID_IOleObject, (LPVOID*)&pole); if (FAILED(hres)) break; hres = pole->SetClientSite(pclient); pole->Release(); if (FAILED(hres)) break; // TraceMsg(DM_TRACE, "sdv TR CMyHlinkSrc_OladLoad calling IUnk::QI"); hres = punk->QueryInterface(riid, ppvOut); } while (0); punk->Release(); } } // TraceMsg(DM_TRACE, "sdv TR CMyHlinkSrc_OleLoad is leaving"); return hres; }