Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

943 lines
28 KiB

//+-------------------------------------------------------------------------
//
// 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<CMMCVersionInfo, &CLSID_MMCVersionInfo>
{
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<CMMCEventConnector, &CLSID_AppEventsDHTMLConnector>,
// support for connection points (script events)
public IConnectionPointContainerImpl<CMMCEventConnector>,
public IConnectionPointImpl<CMMCEventConnector, &DIID_AppEvents, CComDynamicUnkArray>,
public INodeManagerProvideClassInfoImpl<&CLSID_AppEventsDHTMLConnector, &DIID_AppEvents, &LIBID_MMC20>,
public IObjectSafetyImpl<CMMCEventConnector, INTERFACESAFE_FOR_UNTRUSTED_CALLER>
{
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<LPWSTR>(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 <break> 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();
}