//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1999 - 1999 // // File: nodemgr.cpp // //-------------------------------------------------------------------------- #include "stdafx.h" #ifdef BUILD_FOR_1381 #if defined(_UNICODE) inline LPOLESTR CharNextO(LPCOLESTR lp) {return CharNextW(lp);} #elif defined(OLE2ANSI) inline LPOLESTR CharNextO(LPCOLESTR lp) {return CharNext(lp);} #else //CharNextW doesn't work on Win95 so we use this inline LPOLESTR CharNextO(LPCOLESTR lp) {return (LPOLESTR)(lp+1);} #endif #endif #include "atlimpl.cpp" #include "atlwin.cpp" #include "atlctl.cpp" #include "initguid.h" #include "doccnfg.h" #include "NodeMgr.h" #include "msgview.h" #include "fldrsnap.h" #include "tasksymbol.h" #include "power.h" #include "viewext.h" #include "IconControl.h" #include "mmcprotocol.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define IID_DEFINED /* * define our own Win64 symbol to make it easy to include 64-bit only * code in the 32-bit build, so we can exercise some code on 32-bit Windows * where the debuggers are better */ #ifdef _WIN64 #define MMC_WIN64 #endif DECLARE_INFOLEVEL(AMCNodeMgr) CComModule _Module; //############################################################################ //############################################################################ // // The nodemgr proxy exports to support IMMCClipboardDataObject interface marshalling. // //############################################################################ //############################################################################ extern "C" BOOL WINAPI NDMGRProxyDllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/); STDAPI NDMGRProxyDllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv); STDAPI NDMGRProxyDllCanUnloadNow(void); STDAPI NDMGRProxyDllRegisterServer(void); STDAPI NDMGRProxyDllUnregisterServer(void); //############################################################################ //############################################################################ // // Implementation of class CMMCVersionInfo // //############################################################################ //############################################################################ class CMMCVersionInfo: public IMMCVersionInfo, public CComObjectRoot, public CComCoClass { typedef CMMCVersionInfo ThisClass; public: BEGIN_COM_MAP(ThisClass) COM_INTERFACE_ENTRY(IMMCVersionInfo) END_COM_MAP() DECLARE_NOT_AGGREGATABLE(ThisClass) DECLARE_MMC_OBJECT_REGISTRATION ( g_szMmcndmgrDll, // implementing DLL CLSID_MMCVersionInfo, // CLSID _T("MMCVersionInfo 1.0 Object"), // class name _T("NODEMGR.MMCVersionInfo.1"), // ProgID _T("NODEMGR.MMCVersionInfo")) // version-independent ProgID STDMETHOD(GetMMCVersion)(long *pVersionMajor, long *pVersionMinor) { DECLARE_SC(sc, TEXT("CMMCVersionInfo::GetMMCVersion")); sc = ScCheckPointers(pVersionMajor, pVersionMinor); if(sc) return sc.ToHr(); *pVersionMajor = MMC_VERSION_MAJOR; *pVersionMinor = MMC_VERSION_MINOR; return sc.ToHr(); } }; /****************************************************************************/ // forward declarations class CMMCEventConnector; /***************************************************************************\ * * CLASS: CEventForwarder * * PURPOSE: Helper class. It is used to plug into AppEvents as an event sink * to forward received events to CMMCEventConnector class. * It implements IDispatch interface by: * - Having own implementation of QueryInterface * - Forwarding AddRef and Release to CMMCEventConnector's * WeakAddRef and WeakRelease * - Forwarding Invoke to CMMCEventConnector's ScInvokeOnSinks * - using CMMCEventConnector to imlement the rest of IDispatch * USAGE: Used as member object in CMMCEventConnector; * \***************************************************************************/ class CEventForwarder : public IDispatch { public: CEventForwarder(CMMCEventConnector& connector) : m_Connector(connector) { static CMMCTypeInfoHolderWrapper wrapper(GetInfoHolder()); } // IUnknown implementation STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)(); STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject); // IDispatch implementation STDMETHOD(GetTypeInfoCount)( unsigned int FAR* pctinfo ); STDMETHOD(GetTypeInfo)( unsigned int iTInfo, LCID lcid, ITypeInfo FAR* FAR* ppTInfo ); STDMETHOD(GetIDsOfNames)( REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgDispId ); STDMETHOD(Invoke)( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo, unsigned int FAR* puArgErr ); private: CMMCEventConnector& m_Connector; static CComTypeInfoHolder m_TypeInfo; public: // the porpose of this static function is to ensure m_TypeInfo is a static variable, // since static wrapper will hold on its address - it must be always valid static CComTypeInfoHolder& GetInfoHolder() { return m_TypeInfo; } }; /***************************************************************************\ * * CLASS: CMMCEventConnector * * PURPOSE: Implementation of coclass AppEventsDHTMLConnector * Objects of this class are used as event source for application events, * in cases when it's easyier to have cocreatible object to connect to * these events and MMC application is already created (DHTML scripts) * Class does not generate event's itself, it plugs into Allpication * as an event sink for AppEvents dispinterface and keeps forwarding the events * \***************************************************************************/ class CMMCEventConnector : public CMMCIDispatchImpl<_EventConnector, &CLSID_AppEventsDHTMLConnector>, public CComCoClass, // support for connection points (script events) public IConnectionPointContainerImpl, public IConnectionPointImpl, public INodeManagerProvideClassInfoImpl<&CLSID_AppEventsDHTMLConnector, &DIID_AppEvents, &LIBID_MMC20>, public IObjectSafetyImpl { public: BEGIN_MMC_COM_MAP(CMMCEventConnector) COM_INTERFACE_ENTRY(IProvideClassInfo) COM_INTERFACE_ENTRY(IProvideClassInfo2) COM_INTERFACE_ENTRY(IConnectionPointContainer) COM_INTERFACE_ENTRY(IObjectSafety) END_MMC_COM_MAP() DECLARE_NOT_AGGREGATABLE(CMMCEventConnector) DECLARE_MMC_OBJECT_REGISTRATION ( g_szMmcndmgrDll, // implementing DLL CLSID_AppEventsDHTMLConnector, // CLSID _T("AppEventsDHTMLConnector 1.0 Object"), // class name _T("NODEMGR.AppEventsDHTMLConnector.1"), // ProgID _T("NODEMGR.AppEventsDHTMLConnector")) // version-independent ProgID BEGIN_CONNECTION_POINT_MAP(CMMCEventConnector) CONNECTION_POINT_ENTRY(DIID_AppEvents) END_CONNECTION_POINT_MAP() private: public: CMMCEventConnector(); ~CMMCEventConnector(); ULONG InternalRelease(); // overriding the one from CComObjectRoot ULONG WeakAddRef(); ULONG WeakRelease(); STDMETHOD(ConnectTo)(PAPPLICATION Application); STDMETHOD(Disconnect)(); // invokes same event w/ same params on all connected sinks ::SC ScInvokeOnSinks( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo, unsigned int FAR* puArgErr ); private: CEventForwarder m_Forwarder; DWORD m_dwWeakRefs; DWORD m_dwCookie; IConnectionPointPtr m_spConnectionPoint; }; //############################################################################ //############################################################################ // // COM Object map // //############################################################################ //############################################################################ BEGIN_OBJECT_MAP(ObjectMap) OBJECT_ENTRY(CLSID_MMCVersionInfo, CMMCVersionInfo) OBJECT_ENTRY(CLSID_TaskSymbol, CTaskSymbol) OBJECT_ENTRY(CLSID_NodeInit, CNodeInitObject) OBJECT_ENTRY(CLSID_ScopeTree, CScopeTree) OBJECT_ENTRY(CLSID_MMCDocConfig, CMMCDocConfig) OBJECT_ENTRY(CLSID_MessageView, CMessageView) OBJECT_ENTRY(CLSID_FolderSnapin, CFolderSnapin) OBJECT_ENTRY(CLSID_HTMLSnapin, CHTMLSnapin) OBJECT_ENTRY(CLSID_OCXSnapin, COCXSnapin) OBJECT_ENTRY(CLSID_ConsolePower, CConsolePower) OBJECT_ENTRY(CLSID_AppEventsDHTMLConnector, CMMCEventConnector) OBJECT_ENTRY(CLSID_ViewExtSnapin, CViewExtensionSnapin) OBJECT_ENTRY(CLSID_IconControl, CIconControl) OBJECT_ENTRY(CLSID_ComCacheCleanup, CMMCComCacheCleanup) OBJECT_ENTRY(CLSID_MMCProtocol, CMMCProtocol) END_OBJECT_MAP() CNodeMgrApp theApp; void CNodeMgrApp::Init() { DECLARE_SC(sc, TEXT("CNodeMgrApp::Init")); /* register the mmc:// protocol */ /* the protocol is required for taskpads and pagebreaks */ sc = CMMCProtocol::ScRegisterProtocol(); if(sc) sc.TraceAndClear(); } void CNodeMgrApp::DeInit() { SetSnapInsCache(NULL); } /***************************************************************************\ * * METHOD: CNodeMgrApp::ScOnReleaseCachedOleObjects * * PURPOSE: Called prior to ole de-initialization to release any cached ole objects * * PARAMETERS: * * RETURNS: * SC - result code * \***************************************************************************/ SC CNodeMgrApp::ScOnReleaseCachedOleObjects() { DECLARE_SC(sc, TEXT("CNodeMgrApp::ScOnReleaseCachedOleObjects")); // release snapin cache - thats all the class have cached... SetSnapInsCache(NULL); return sc; } void CNodeMgrApp::SetSnapInsCache(CSnapInsCache* pSIC) { if (m_pSnapInsCache != NULL) delete m_pSnapInsCache; m_pSnapInsCache = pSIC; } ///////////////////////////////////////////////////////////////////////////// // DLL Entry Point extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) { if (dwReason == DLL_PROCESS_ATTACH) { // NATHAN FIX !!w //_set_new_handler( _standard_new_handler ); _Module.Init(ObjectMap, hInstance); theApp.Init(); DisableThreadLibraryCalls(hInstance); } else if (dwReason == DLL_PROCESS_DETACH) { theApp.DeInit(); _Module.Term(); } NDMGRProxyDllMain(hInstance, dwReason, NULL); return TRUE; // ok } ///////////////////////////////////////////////////////////////////////////// // Used to determine whether the DLL can be unloaded by OLE STDAPI DllCanUnloadNow(void) { if (_Module.GetLockCount()!=0) return S_FALSE; return NDMGRProxyDllCanUnloadNow(); } ///////////////////////////////////////////////////////////////////////////// // Returns a class factory to create an object of the requested type STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) { if (IsEqualIID(IID_IMMCClipboardDataObject, rclsid)) return NDMGRProxyDllGetClassObject(rclsid, riid, ppv); return _Module.GetClassObject(rclsid, riid, ppv); } ///////////////////////////////////////////////////////////////////////////// // DllRegisterServer - Adds entries to the system registry // Use own routine to register typelib because we don't want // a full pathname, just a module name static HRESULT RegisterTypeLib() { USES_CONVERSION; TCHAR szModule[_MAX_PATH+10] = { 0 }; GetModuleFileName(_Module.GetModuleInstance(), szModule, _MAX_PATH); ITypeLib* pTypeLib; LPOLESTR lpszModule = T2OLE(szModule); HRESULT hr = LoadTypeLib(lpszModule, &pTypeLib); ASSERT(SUCCEEDED(hr)); if (SUCCEEDED(hr)) { hr = ::RegisterTypeLib(pTypeLib, const_cast(T2CW (g_szMmcndmgrDll)), NULL); ASSERT(SUCCEEDED(hr)); } if (pTypeLib != NULL) pTypeLib->Release(); return hr; } STDAPI DllRegisterServer(void) { DECLARE_SC (sc, _T("DllRegisterServer")); // registers objects sc = _Module.RegisterServer(FALSE); if (sc) { sc.Trace_(); return ((sc = SELFREG_E_CLASS).ToHr()); } CRegKeyEx regkeySoftware; CRegKeyEx regkeyMMC; CRegKeyEx regkeySnapIns; CRegKeyEx regkeyNodeTypes; if ((sc = regkeySoftware. ScOpen (HKEY_LOCAL_MACHINE, _T("Software\\Microsoft"))).IsError() || (sc = regkeyMMC. ScCreate (regkeySoftware, _T("MMC"))). IsError() || (sc = regkeySnapIns. ScCreate (regkeyMMC, _T("SnapIns"))). IsError() || (sc = regkeyNodeTypes.ScCreate (regkeyMMC, _T("NodeTypes"))). IsError()) { sc.Trace_(); return ((sc = SELFREG_E_CLASS).ToHr()); } sc = ::RegisterTypeLib(); if (sc) { sc.Trace_(); return ((sc = SELFREG_E_TYPELIB).ToHr()); } sc = NDMGRProxyDllRegisterServer(); if (sc) return sc.ToHr(); /* * register mmc.exe to complete the process * note: mmc.exe is never unregistered */ // fix to windows bug #233372. ntbug09, 11/28/00 // [ mmc.exe launched from current directory, not from where it is supposed to be] // get the path of node manager dll TCHAR szPath[_MAX_PATH]; DWORD dwPathLen = ::GetModuleFileName(_Module.GetModuleInstance(), szPath, countof(szPath) ); szPath[countof(szPath) -1] = 0; // if node manager path is found - put same directory to mmc path tstring strMMCPath; if ( dwPathLen > 0 ) { tstring strNodeMgr = szPath; int iLastSlashPos = strNodeMgr.rfind('\\'); if (iLastSlashPos != tstring::npos) strMMCPath = strNodeMgr.substr(0, iLastSlashPos + 1); } else { sc = E_UNEXPECTED; sc.TraceAndClear(); // ignore and continue without a path } strMMCPath += _T("mmc.exe"); #if defined(MMC_WIN64) LPCTSTR szRegParams = _T("-64 -RegServer"); #else LPCTSTR szRegParams = _T("-32 -RegServer"); #endif HINSTANCE hInst = ShellExecute (NULL, NULL, strMMCPath.c_str(), szRegParams, NULL, SW_SHOWNORMAL); if ((DWORD_PTR) hInst <= 32) { switch ((DWORD_PTR) hInst) { case 0: sc = E_OUTOFMEMORY; break; case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: case ERROR_BAD_FORMAT: sc.FromWin32 ((DWORD_PTR) hInst); break; default: sc = E_FAIL; break; } return (sc.ToHr()); } return (sc.ToHr()); } ///////////////////////////////////////////////////////////////////////////// // DllUnregisterServer - Adds entries to the system registry STDAPI DllUnregisterServer(void) { HRESULT hRes = S_OK; _Module.UnregisterServer(); NDMGRProxyDllUnregisterServer(); return hRes; } /***************************************************************************\ * * STATIC OBJECT: CEventForwarder::m_TypeInfo * * PURPOSE: manages ITypeInfo used by CEventForwarder * \***************************************************************************/ CComTypeInfoHolder CEventForwarder::m_TypeInfo = { &DIID_AppEvents, &LIBID_MMC20, 1, 0, NULL, 0, NULL, 0 }; /***************************************************************************\ * * METHOD: CEventForwarder::AddRef * * PURPOSE: Implements IUnknown::AddRef * This class is always contained within m_Connector, so it * relies on outer object to count the references. * To differentiate between regular references and those occuring * beacuse of connecting to the sink, it calls WeakAddRef, * not the regular AddRef on connector * * PARAMETERS: * * RETURNS: * ULONG - reference count * \***************************************************************************/ STDMETHODIMP_(ULONG) CEventForwarder::AddRef() { return m_Connector.WeakAddRef(); } /***************************************************************************\ * * METHOD: CEventForwarder::Release * * PURPOSE: Implements IUnknown::Release * This class is always contained within m_Connector, so it * relies on outer object to count the references. * To differentiate between regular references and those occuring * beacuse of connecting to the sink, it calls WeakRelease, * not the regular Release on connector * * PARAMETERS: * * RETURNS: * ULONG - reference count * \***************************************************************************/ STDMETHODIMP_(ULONG) CEventForwarder::Release() { return m_Connector.WeakRelease(); } /***************************************************************************\ * * METHOD: CEventForwarder::QueryInterface * * PURPOSE: Implements IUnknown::QueryInterface * returns self, when requested for IUnknow, IDispatch, AppEvents * * PARAMETERS: * REFIID iid * void ** ppvObject * * RETURNS: * SC - result code * \***************************************************************************/ STDMETHODIMP CEventForwarder::QueryInterface(REFIID iid, void ** ppvObject) { DECLARE_SC(sc, TEXT("")); // parameter check sc = ScCheckPointers(ppvObject); if (sc) return sc.ToHr(); // initialization *ppvObject = NULL; // check IID if (IsEqualGUID(iid, IID_IUnknown) || IsEqualGUID(iid, IID_IDispatch) || IsEqualGUID(iid, DIID_AppEvents)) { *ppvObject = this; AddRef(); return sc.ToHr(); } // not an error - do not assign to sc return E_NOINTERFACE; } /***************************************************************************\ * * METHOD: CEventForwarder::GetTypeInfoCount * * PURPOSE: implements method on IDispatch * * PARAMETERS: * unsigned int FAR* pctinfo * * RETURNS: * HRESULT - result code * \***************************************************************************/ STDMETHODIMP CEventForwarder::GetTypeInfoCount( unsigned int FAR* pctinfo ) { if (pctinfo == NULL) return E_INVALIDARG; *pctinfo = 1; return S_OK; } /***************************************************************************\ * * METHOD: CEventForwarder::GetTypeInfo * * PURPOSE: implements method on IDispatch * * PARAMETERS: * unsigned int iTInfo * LCID lcid * ITypeInfo FAR* FAR* ppTInfo * * RETURNS: * HRESULT - result code * \***************************************************************************/ STDMETHODIMP CEventForwarder::GetTypeInfo( unsigned int iTInfo, LCID lcid, ITypeInfo FAR* FAR* ppTInfo ) { return m_TypeInfo.GetTypeInfo( iTInfo, lcid, ppTInfo ); } /***************************************************************************\ * * METHOD: CEventForwarder::GetIDsOfNames * * PURPOSE:implements method on IDispatch * * PARAMETERS: * riid * rgszNames * cNames * lcid * rgDispId * * RETURNS: * HRESULT - result code * \***************************************************************************/ STDMETHODIMP CEventForwarder::GetIDsOfNames( REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgDispId ) { return m_TypeInfo.GetIDsOfNames( riid, rgszNames, cNames, lcid, rgDispId ); } /***************************************************************************\ * * METHOD: CEventForwarder::Invoke * * PURPOSE: implements method on IDispatch. Forwards calls to connector. * In order to distinguish between calls made on itself, connector * must provide method, which has different name : ScInvokeOnSinks * * PARAMETERS: * DISPID dispIdMember * REFIID riid * LCID lcid * WORD wFlags * DISPPARAMS FAR* pDispParams * VARIANT FAR* pVarResult * EXCEPINFO FAR* pExcepInfo * unsigned int FAR* puArgErr * * RETURNS: * HRESULT - result code * \***************************************************************************/ STDMETHODIMP CEventForwarder::Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo, unsigned int FAR* puArgErr ) { DECLARE_SC(sc, TEXT("CEventForwarder::Invoke")); sc = m_Connector.ScInvokeOnSinks( dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr ); return sc.ToHr(); } /***************************************************************************\ * * METHOD: CMMCEventConnector::CMMCEventConnector * * PURPOSE: constructor * \***************************************************************************/ CMMCEventConnector::CMMCEventConnector() : m_Forwarder(*this), m_dwCookie(0), m_dwWeakRefs(0) { } /***************************************************************************\ * * METHOD: CMMCEventConnector::~CMMCEventConnector * * PURPOSE: Destructor * \***************************************************************************/ CMMCEventConnector::~CMMCEventConnector() { Disconnect(); // most likely not needed. Just for sanity } /***************************************************************************\ * * METHOD: CMMCEventConnector::InternalRelease * * PURPOSE: Overrides method from CComObjectRoot to detect when last "real" * reference is released. Refs made because of connecting to the * sink does not count - we would have a deadlock else * * PARAMETERS: * * RETURNS: * ULONG - ref count * \***************************************************************************/ ULONG CMMCEventConnector::InternalRelease() { ULONG uRefsLeft = CComObjectRoot::InternalRelease(); if ((uRefsLeft != 0) && (uRefsLeft == m_dwWeakRefs)) { // seems like we are alive just because we still connected to the connection point // disconnect ( no-one uses it anyway ) InternalAddRef(); // Addref to have balance Disconnect(); // disconnect from the connection point uRefsLeft = CComObjectRoot::InternalRelease(); // release again } return uRefsLeft; } /***************************************************************************\ * * METHOD: CMMCEventConnector::WeakAddRef * * PURPOSE: counts reference from connection points. AddRefs regularly as well * * PARAMETERS: * * RETURNS: * ULONG - ref count * \***************************************************************************/ ULONG CMMCEventConnector::WeakAddRef() { ++m_dwWeakRefs; return AddRef(); } /***************************************************************************\ * * METHOD: CMMCEventConnector::WeakRelease * * PURPOSE: counts reference from connection points. Releases regularly as well * * PARAMETERS: * * RETURNS: * ULONG - ref count * \***************************************************************************/ ULONG CMMCEventConnector::WeakRelease() { --m_dwWeakRefs; return Release(); } /***************************************************************************\ * * METHOD: CMMCEventConnector::ScInvokeOnSinks * * PURPOSE: This method has a signature of IDispath::Invoke and is * called from connection point to inform about the event * Method's job is to fork the call to each own connected sink * * RETURNS: * SC - result code * \***************************************************************************/ SC CMMCEventConnector::ScInvokeOnSinks( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo, unsigned int FAR* puArgErr ) { DECLARE_SC(sc, TEXT("CMMCEventConnector::ScInvokeOnSinks")); // find connection point IConnectionPointPtr spConnectionPoint; sc = FindConnectionPoint(DIID_AppEvents, &spConnectionPoint); if (sc) return sc; // get connections IEnumConnectionsPtr spEnumConnections; sc = spConnectionPoint->EnumConnections(&spEnumConnections); if (sc) return sc.ToHr(); // recheck the pointer sc = ScCheckPointers(spEnumConnections, E_UNEXPECTED); if (sc) return sc.ToHr(); // reset iterator sc = spEnumConnections->Reset(); if (sc) return sc.ToHr(); // iterate thru sinks until Next returns S_FALSE. CONNECTDATA connectdata; SC sc_last_error; while (1) // will use to exit { // get the next sink ZeroMemory(&connectdata, sizeof(connectdata)); sc = spEnumConnections->Next( 1, &connectdata, NULL ); if (sc) return sc.ToHr(); // done if no more sinks if (sc == SC(S_FALSE)) break; // recheck the pointer sc = ScCheckPointers(connectdata.pUnk, E_UNEXPECTED); if (sc) return sc.ToHr(); // QI for IDispatch IDispatchPtr spDispatch = (IDispatch *)connectdata.pUnk; connectdata.pUnk->Release(); // recheck the pointer sc = ScCheckPointers(spDispatch, E_UNEXPECTED); if (sc) return sc.ToHr(); // invoke on the sink sc = spDispatch->Invoke( dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr ); if (sc) { sc_last_error = sc; // continue even if some calls failed sc.TraceAndClear(); } } return sc_last_error.ToHr(); } /***************************************************************************\ * * METHOD: CMMCEventConnector::ConnectTo * * PURPOSE: Connects to Application object and starts forwarding its events * * PARAMETERS: * PAPPLICATION Application * * RETURNS: * SC - result code * \***************************************************************************/ STDMETHODIMP CMMCEventConnector::ConnectTo(PAPPLICATION Application) { DECLARE_SC(sc, TEXT("ConnectTo")); // disconnect from former connections sc = Disconnect(); if (sc) return sc.ToHr(); // check if com object supports IConnectionPointContainer; IConnectionPointContainerPtr spContainer = Application; sc = ScCheckPointers(spContainer); if (sc) return sc.ToHr(); // get connection point sc = spContainer->FindConnectionPoint(DIID_AppEvents, &m_spConnectionPoint); if (sc) return sc.ToHr(); sc = m_spConnectionPoint->Advise(&m_Forwarder, &m_dwCookie); if (sc) return sc.ToHr(); return S_OK; } /***************************************************************************\ * * METHOD: CMMCEventConnector::Disconnect * * PURPOSE: Disconnects from connection point if is connected to one * * PARAMETERS: * * RETURNS: * HRESULT - result code * \***************************************************************************/ STDMETHODIMP CMMCEventConnector::Disconnect() { DECLARE_SC(sc, TEXT("CMMCEventConnector::Disconnect")); if (m_dwCookie) { if (m_spConnectionPoint != NULL) { sc = m_spConnectionPoint->Unadvise(m_dwCookie); if (sc) sc.TraceAndClear(); } m_dwCookie = 0; m_spConnectionPoint = NULL; } return sc.ToHr(); }