/*++ Copyright (c) 1994-1999 Microsoft Corporation Module Name : inetmgr.cpp Abstract: Main MMC snap-in code Author: Ronald Meijer (ronaldm) Project: Internet Services Manager Revision History: --*/ #include "stdafx.h" #include "common.h" #include "InetMgrApp.h" #include "iisobj.h" #include "guids.h" #ifdef _DEBUG #undef THIS_FILE static char BASED_CODE THIS_FILE[] = __FILE__; #endif #define new DEBUG_NEW //const GUID * CCompMgrExtData::m_NODETYPE = &cCompMgmtService; //const OLECHAR * CCompMgrExtData::m_SZNODETYPE = OLESTR("476e6446-aaff-11d0-b944-00c04fd8d5b0"); //const OLECHAR * CCompMgrExtData::m_SZDISPLAY_NAME = OLESTR("CMSnapin"); //const CLSID * CCompMgrExtData::m_SNAPIN_CLASSID = &CLSID_InetMgr; extern CInetmgrApp theApp; static HRESULT GetHelpTopic(LPOLESTR *lpCompiledHelpFile) { if (lpCompiledHelpFile == NULL) return E_INVALIDARG; CString strFilePath, strWindowsPath, strBuffer; AFX_MANAGE_STATE(AfxGetStaticModuleState()); // Use system API to get windows directory. UINT uiResult = GetWindowsDirectory(strWindowsPath.GetBuffer(MAX_PATH), MAX_PATH); strWindowsPath.ReleaseBuffer(); if (uiResult <= 0 || uiResult > MAX_PATH) { return E_FAIL; } if (!strFilePath.LoadString(IDS_HELPFILE)) { return E_FAIL; } strBuffer = strWindowsPath; strBuffer += _T('\\'); strBuffer += strFilePath; *lpCompiledHelpFile = reinterpret_cast(CoTaskMemAlloc((strBuffer.GetLength() + 1) * sizeof(_TCHAR))); if (*lpCompiledHelpFile == NULL) return E_OUTOFMEMORY; USES_CONVERSION; _tcscpy(*lpCompiledHelpFile, T2OLE((LPTSTR)(LPCTSTR)strBuffer)); return S_OK; } // // CInetMgrComponent Implementation // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< CInetMgrComponent::CInetMgrComponent() /*++ Routine Description: Constructor Arguments: None Return Value: N/A --*/ { } HRESULT CInetMgrComponent::Notify( IN LPDATAOBJECT lpDataObject, IN MMC_NOTIFY_TYPE event, IN LPARAM arg, IN LPARAM param ) /*++ Routine Description: Notification handler. Arguments: LPDATAOBJECT lpDataObject : Data object MMC_NOTIFY_TYPE event : Notification event long arg : Event specific argument long param : Event specific parameter Return Value: HRESULT --*/ { if (lpDataObject != NULL) { // // Pass it on to IComponentImpl // return IComponentImpl::Notify(lpDataObject, event, arg, param); } return E_NOTIMPL; } HRESULT CInetMgrComponent::GetClassID( OUT CLSID * pClassID ) /*++ Routine Description: Get class ID for storage stream. Arguments: CLSID * pClassID : Returns class ID information Return Value: HRESULT --*/ { *pClassID = CLSID_InetMgr; return S_OK; } STDMETHODIMP CInetMgrComponent::GetHelpTopic(LPOLESTR *lpCompiledHelpFile) { return ::GetHelpTopic(lpCompiledHelpFile); } STDMETHODIMP CInetMgrComponent::GetLinkedTopics(LPOLESTR *lpCompiledHelpFile) { return S_FALSE; } HRESULT CInetMgrComponent::IsDirty() /*++ Routine Description: Check to see if we need to write to the cache. Arguments: None Return Value: S_OK if dirty, S_FALSE if not --*/ { TRACEEOLID("CInetMgrComponent::IsDirty"); return S_FALSE; } HRESULT CInetMgrComponent::InitNew( IN OUT IStorage * pStg ) /*++ Routine Description: Initialize storage stream. Arguments: IStorage * pStg : Storage stream Return Value: HRESULT --*/ { TRACEEOLID("CInetMgrComponent::InitNew"); return S_OK; } HRESULT CInetMgrComponent::Load( IN OUT IStorage * pStg ) /*++ Routine Description: Load from the storage stream Arguments: IStorage * pStg : Storage stream Return Value: HRESULT --*/ { TRACEEOLID("CInetMgrComponent::Load"); return S_OK; } /* virtual */ HRESULT STDMETHODCALLTYPE CInetMgrComponent::Save( IN OUT IStorage * pStgSave, IN BOOL fSameAsLoad ) /*++ Routine Description: Save to to the storage stream. Arguments: IStorage * pStgSave : Storage stream BOOL fSameAsLoad : TRUE if same as load Return Value: HRESULT --*/ { TRACEEOLID("CInetMgrComponent::Save"); return S_OK; } /* virtual */ HRESULT STDMETHODCALLTYPE CInetMgrComponent::SaveCompleted(IStorage * pStgNew) /*++ Routine Description: Save completed. Arguments: IStorage * pStgNew : Storage stream Return Value: HRESULT --*/ { TRACEEOLID("CInetMgrComponent::SaveCompleted"); return S_OK; } /* virtual */ HRESULT STDMETHODCALLTYPE CInetMgrComponent::HandsOffStorage() /*++ Routine Description: Hands off storage. Arguments: None Return Value: HRESULT --*/ { TRACEEOLID("CInetMgrComponent::HandsOffStorage"); return S_OK; } /* virtual */ HRESULT CInetMgrComponent::SetControlbar( IN LPCONTROLBAR lpControlBar ) /*++ Routine Description: Set/Reset the control bar Arguments: LPCONTROLBAR lpControlBar : Control bar pointer or NULL Return Value: HRESULT --*/ { return CIISObject::__SetControlbar(lpControlBar, this); } /* virtual */ HRESULT CInetMgrComponent::ControlbarNotify( IN MMC_NOTIFY_TYPE event, IN LPARAM arg, IN LPARAM param ) /*++ Routine Description: Handle control bar notification message. Figure out the CIISObject selected, and pass the notification message off to it. Arguments: MMC_NOTIFY_TYPE event : Notification message long arg : Message specific argument long param : Message specific parameter Return Value: HRESULT --*/ { HRESULT hr = S_OK; CSnapInItem * pItem = NULL; DATA_OBJECT_TYPES type; // // Special casing this is annoying... // // CODEWORK: Handle MMCN_HELP and others // if (event == MMCN_BTN_CLICK) { hr = m_pComponentData->GetDataClass((IDataObject *)arg, &pItem, &type); } else if (event == MMCN_SELECT) { hr = m_pComponentData->GetDataClass((IDataObject *)param, &pItem, &type); } // // Find out CIISObject this belongs to and pass on // the message // CIISObject * pObject = (CIISObject *)pItem; if (SUCCEEDED(hr) && pObject != NULL) { hr = pObject->ControlbarNotify(event, arg, param); } return hr; } /* virtual */ HRESULT CInetMgrComponent::Compare( IN RDCOMPARE * prdc, OUT int * pnResult ) /*++ Routine Description: Compare method used for sorting the result and scope panes. Arguments: RDCOMPARE * prdc : Compare structure int * pnResult : Returns result Return Value: HRESULT --*/ { if (!pnResult || !prdc || !prdc->prdch1->cookie || !prdc->prdch2->cookie) { ASSERT_MSG("Invalid parameter(s)"); return E_POINTER; } CIISObject * pObjectA = (CIISObject *)prdc->prdch1->cookie; CIISObject * pObjectB = (CIISObject *)prdc->prdch2->cookie; *pnResult = pObjectA->CompareResultPaneItem(pObjectB, prdc->nColumn); return S_OK; } /* virtual */ HRESULT CInetMgrComponent::CompareObjects( IN LPDATAOBJECT lpDataObjectA, IN LPDATAOBJECT lpDataObjectB ) /*++ Routine Description: Compare two data objects. This method is used to see if a property sheet for the given data object is already open Arguments: LPDATAOBJECT lpDataObjectA : A data object LPDATAOBJECT lpDataObjectB : B data object Return Value: S_OK if they match, S_FALSE otherwise --*/ { // // Pass it on to IComponentImpl // return IComponentImpl::CompareObjects(lpDataObjectA, lpDataObjectB); } // // CInetMgr Implementation // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< /* static */ DWORD CInetMgr::_dwSignature = 0x3517; /* static */ LPCTSTR CInetMgr::_szStream = _T("CInetMgr"); /* static */ void WINAPI CInetMgr::ObjectMain( IN bool bStarting ) /*++ Routine Description: CInetMgr main entry point Arguments: bool bStarting : TRUE if starting Return Value: None --*/ { if (bStarting) { // // Register clipboard formats // CSnapInItem::Init(); CIISObject::Init(); } } CInetMgr::CInetMgr() { // // Initialize the root node // CIISObject::Initialize(); m_pNode = new CIISRoot; ASSERT_PTR(m_pNode); m_pComponentData = this; } CInetMgr::~CInetMgr() /*++ Routine Description: Destructor Arguments: N/A Return Value: N/A --*/ { // // Clean up the root node // delete m_pNode; m_pNode = NULL; CIISObject::Destroy(); } HRESULT CInetMgr::Initialize( IN LPUNKNOWN lpUnknown ) /*++ Routine Description: Initialize the snap-in Arguments: LPUNKNOWN lpUnknown : IUnknown Return Value: HRESULT --*/ { AFX_MANAGE_STATE(AfxGetStaticModuleState()); HRESULT hr = IComponentDataImpl::Initialize(lpUnknown); if (FAILED(hr)) { return hr; } hr = CIISObject::AttachScopeView(lpUnknown); if (FAILED(hr)) { TRACEEOLID("failed to query console name space interface"); return hr; } CComPtr lpImageList; hr = m_spConsole->QueryScopeImageList(&lpImageList); if (FAILED(hr) || lpImageList == NULL) { TRACEEOLID("IConsole::QueryScopeImageList failed"); return E_UNEXPECTED; } return CIISObject::SetImageList(lpImageList); } HRESULT CInetMgr::OnPropertyChange(LPARAM arg, LPARAM param) { HRESULT hr = S_OK; if (param != 0) { CSnapInItem * pNode = (CSnapInItem *)param; LPDATAOBJECT pDataObject = NULL; pNode->GetDataObject(&pDataObject, CCT_SCOPE); hr = m_spConsole->UpdateAllViews(pDataObject, param, 0); } return hr; } HRESULT CInetMgr::Notify( LPDATAOBJECT lpDataObject, MMC_NOTIFY_TYPE event, LPARAM arg, LPARAM param ) { HRESULT hr = S_OK; if (lpDataObject == NULL) { switch (event) { case MMCN_PROPERTY_CHANGE: hr = OnPropertyChange(arg, param); break; case MMCN_SNAPINHELP: break; default: break; } } else { hr = IComponentDataImpl::Notify( lpDataObject, event, arg, param); } return hr; } HRESULT CInetMgr::GetClassID(CLSID * pClassID) /*++ Routine Description: Get class ID for storage stream Arguments: CLSID * pClassID : Returns class ID information Return Value: HRESULT --*/ { *pClassID = CLSID_InetMgr; return S_OK; } STDMETHODIMP CInetMgr::GetHelpTopic(LPOLESTR *lpCompiledHelpFile) { return ::GetHelpTopic(lpCompiledHelpFile); } STDMETHODIMP CInetMgr::GetLinkedTopics(LPOLESTR *lpCompiledHelpFile) { return S_FALSE; } HRESULT CInetMgr::IsDirty() /*++ Routine Description: Check to see if we need to write to the cache. Arguments: None Return Value: S_OK if dirty, S_FALSE if not --*/ { TRACEEOLID("CInetMgrComponent::IsDirty"); ASSERT_PTR(m_pNode); if (IsExtension()) { return FALSE; } else { return ((CIISRoot *)m_pNode)->m_scServers.IsDirty() ? S_OK : S_FALSE; } } HRESULT CInetMgr::InitNew(IStorage * pStg) /*++ Routine Description: Initialize new storage stream (newly created console file) Arguments: IStorage * pStg : Storage stream Return Value: HRESULT --*/ { TRACEEOLID("CInetMgrComponent::InitNew"); // // We could create the stream here, but it's just as easy to // create it inside Save(). // return S_OK; } HRESULT CInetMgr::Load(IStorage * pStg) /*++ Routine Description: Load machine cache from the storage stream. Arguments: IStorage * pStg : Storage stream Return Value: HRESULT --*/ { AFX_MANAGE_STATE(AfxGetStaticModuleState()); TRACEEOLID("CInetMgrComponent::Load"); if (IsExtension()) { return S_OK; } ASSERT_READ_WRITE_PTR(pStg); DWORD cBytesRead; DWORD dw; HRESULT hr = S_OK; CIISServerCache & cache = ((CIISRoot *)m_pNode)->m_scServers; IStream * pStream = NULL; ASSERT(cache.IsEmpty()); do { hr = pStg->OpenStream( _szStream, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0L, &pStream ); if (FAILED(hr)) { break; } // // Read and verify the signature // hr = pStream->Read(&dw, sizeof(dw), &cBytesRead); ASSERT(SUCCEEDED(hr) && cBytesRead == sizeof(dw)); if (FAILED(hr)) { break; } if (dw != _dwSignature) { hr = HRESULT_FROM_WIN32(ERROR_FILE_CORRUPT); break; } // // Read number of machines in the cache // DWORD cMachines; hr = pStream->Read(&cMachines, sizeof(cMachines), &cBytesRead); ASSERT(SUCCEEDED(hr) && cBytesRead == sizeof(cMachines)); if (FAILED(hr)) { break; } TRACEEOLID("Reading " << cMachines << " machines from cache"); CIISMachine * pMachine; // // Read each machine from the cache // for (dw = 0; dw < cMachines; ++dw) { hr = CIISMachine::ReadFromStream(pStream, &pMachine); if (FAILED(hr)) { break; } if (!cache.Add(pMachine)) { delete pMachine; } } } while(FALSE); if (pStream) { pStream->Release(); } if (hr == STG_E_FILENOTFOUND) { // // Stream was not initialized. This is acceptable. // hr = S_OK; } // // Mark cache as clean // cache.SetDirty(FALSE); return hr; } /* virtual */ HRESULT STDMETHODCALLTYPE CInetMgr::Save(IStorage * pStgSave, BOOL fSameAsLoad) /*++ Routine Description: Save computer cache to to the storage stream. Arguments: IStorage * pStgSave : Storage stream BOOL fSameAsLoad : TRUE if same as load Return Value: HRESULT --*/ { AFX_MANAGE_STATE(AfxGetStaticModuleState()); TRACEEOLID("CInetMgrComponent::Save"); if (IsExtension()) { return S_OK; } // // Write the computer names to the cache // ASSERT_READ_WRITE_PTR(pStgSave); DWORD cBytesWritten; HRESULT hr = STG_E_CANTSAVE; IStream * pStream = NULL; CIISServerCache & cache = ((CIISRoot *)m_pNode)->m_scServers; do { hr = pStgSave->CreateStream( _szStream, STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0L, 0L, &pStream ); if (FAILED(hr)) { break; } // // Write the signature // hr = pStream->Write(&_dwSignature, sizeof(_dwSignature), &cBytesWritten); ASSERT(SUCCEEDED(hr) && cBytesWritten == sizeof(_dwSignature)); if (FAILED(hr)) { break; } // // Write number of entries. // INT_PTR dw = cache.GetCount(); hr = pStream->Write(&dw, sizeof(dw), &cBytesWritten); ASSERT(SUCCEEDED(hr) && cBytesWritten == sizeof(dw)); if (FAILED(hr)) { break; } // // Write each string -- but write them in reverse // order to improve our sort performance when we load // the cache. // CIISMachine * pMachine = cache.GetLast(); while(pMachine) { hr = pMachine->WriteToStream(pStream); if (FAILED(hr)) { break; } pMachine = cache.GetPrev(); } } while(FALSE); if (pStream) { pStream->Release(); } if (SUCCEEDED(hr)) { // // Mark cache as clean // cache.SetDirty(FALSE); } return hr; } /* virtual */ HRESULT STDMETHODCALLTYPE CInetMgr::SaveCompleted(IStorage * pStgNew) /*++ Routine Description: Save completed notification. Arguments: IStorage * pStgNew : Storage stream Return Value: HRESULT --*/ { TRACEEOLID("CInetMgrComponent::SaveCompleted"); // // Nothing to do // return S_OK; } /* virtual */ HRESULT STDMETHODCALLTYPE CInetMgr::HandsOffStorage() /*++ Routine Description: Hands off storage. Arguments: None Return Value: HRESULT --*/ { TRACEEOLID("CInetMgrComponent::HandsOffStorage"); // // Nothing to do // return S_OK; } /* virtual */ HRESULT CInetMgr::CompareObjects(LPDATAOBJECT lpDataObjectA, LPDATAOBJECT lpDataObjectB) /*++ Routine Description: Compare two data objects. This method is used by MMC to see if a property sheet for the given data object is already open. Arguments: LPDATAOBJECT lpDataObjectA : A data object LPDATAOBJECT lpDataObjectB : B data object Return Value: S_OK if they match, S_FALSE otherwise --*/ { HRESULT hr = E_POINTER; do { if (!lpDataObjectA || !lpDataObjectB) { TRACEEOLID("IComponentData::CompareObjects called with NULL ptr"); break; } CSnapInItem * pItemA; CSnapInItem * pItemB; DATA_OBJECT_TYPES type; hr = m_pComponentData->GetDataClass(lpDataObjectA, &pItemA, &type); if (SUCCEEDED(hr)) { hr = m_pComponentData->GetDataClass(lpDataObjectB, &pItemB, &type); } if (FAILED(hr)) { break; } if (!pItemA || !pItemB) { hr = E_POINTER; break; } if (pItemA == pItemB) { // // Literally the same object // hr = S_OK; break; } CIISObject * pObjectA = (CIISObject *)pItemA; CIISObject * pObjectB = (CIISObject *)pItemB; hr = !pObjectA->CompareScopeItem(pObjectB) ? S_OK : S_FALSE; } while(FALSE); return hr; } HRESULT CInetMgr::GetDataClass( IDataObject * pDataObject, CSnapInItem ** ppItem, DATA_OBJECT_TYPES * pType) { if (ppItem == NULL) return E_POINTER; if (pType == NULL) return E_POINTER; *ppItem = NULL; *pType = CCT_UNINITIALIZED; STGMEDIUM stgmedium = { TYMED_HGLOBAL, NULL }; FORMATETC formatetc = { CSnapInItem::m_CCF_NODETYPE, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; stgmedium.hGlobal = GlobalAlloc(0, sizeof(GUID)); if (stgmedium.hGlobal == NULL) return E_OUTOFMEMORY; HRESULT hr = pDataObject->GetDataHere(&formatetc, &stgmedium); if (FAILED(hr)) { GlobalFree(stgmedium.hGlobal); return hr; } GUID guid; memcpy(&guid, stgmedium.hGlobal, sizeof(GUID)); GlobalFree(stgmedium.hGlobal); hr = S_OK; if (IsEqualGUID(guid, cCompMgmtService)) { if (!IsExtension()) { CIISRoot * pRootExt = new CIISRoot; if (pRootExt == NULL) { return E_OUTOFMEMORY; } hr = pRootExt->InitAsExtension(pDataObject); if (FAILED(hr)) { return hr; } if (m_pNode != NULL) { delete m_pNode; } m_pNode = pRootExt; } *ppItem = m_pNode; return hr; } return CSnapInItem::GetDataClass(pDataObject, ppItem, pType); }; BOOL CInetMgr::IsExtension() { ASSERT(m_pNode != NULL); CIISRoot * pRoot = (CIISRoot *)m_pNode; return pRoot->IsExtension(); } // // CInetMrgAbout Class // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< HRESULT CInetMgrAbout::GetStringHelper(UINT nStringID, LPOLESTR * lpString) /*++ Routine Description: Get resource string helper function. Called from inline string fetcher methods. Arguments: UINT nStringID : String ID from local resource segment LPOLESTR * lpString : Returns the string Return Value: HRESULT --*/ { USES_CONVERSION; TCHAR szBuf[256]; if (::LoadString( _Module.GetResourceInstance(), nStringID, szBuf, 256) == 0) { return E_FAIL; } *lpString = (LPOLESTR)::CoTaskMemAlloc( (lstrlen(szBuf) + 1) * sizeof(OLECHAR) ); if (*lpString == NULL) { return E_OUTOFMEMORY; } ::ocscpy(*lpString, T2OLE(szBuf)); return S_OK; } HRESULT CInetMgrAbout::GetSnapinImage(HICON * hAppIcon) /*++ Routine Description: Get the icon for this snapin. Arguments: HICON * hAppIcon : Return handle to the icon Return Value: HRESULT --*/ { if (hAppIcon == NULL) { return E_POINTER; } m_hSnapinIcon = ::LoadIcon( _Module.GetModuleInstance(), MAKEINTRESOURCE(IDI_INETMGR) ); *hAppIcon = m_hSnapinIcon; ASSERT(*hAppIcon != NULL); return (*hAppIcon != NULL) ? S_OK : E_FAIL; } HRESULT CInetMgrAbout::GetStaticFolderImage( HBITMAP * phSmallImage, HBITMAP * phSmallImageOpen, HBITMAP * phLargeImage, COLORREF * prgbMask ) /*++ Routine Description: Get the static folder images. Arguments: HBITMAP * phSmallImage : Small folder HBITMAP * phSmallImageOpen : Small open folder HBITMAP * phLargeImage : Large image COLORREF * prgbMask : Mask Return Value: HRESULT --*/ { if (!phSmallImage || !phSmallImageOpen || !phLargeImage || !prgbMask) { return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); } m_hSmallImage = (HBITMAP)::LoadImage( _Module.GetModuleInstance(), MAKEINTRESOURCE(IDB_SMALL_ROOT), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR ); m_hLargeImage = (HBITMAP)::LoadImage( _Module.GetModuleInstance(), MAKEINTRESOURCE(IDB_LARGE_ROOT), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR ); *phSmallImage = m_hSmallImage; *phSmallImageOpen = m_hSmallImage; *phLargeImage = m_hLargeImage; *prgbMask = RGB_BK_IMAGES; return *phSmallImage && *phLargeImage ? S_OK : E_FAIL; } CInetMgrAbout::~CInetMgrAbout() { if (m_hSmallImage != NULL) { ::DeleteObject(m_hSmallImage); } if (m_hLargeImage != NULL) { ::DeleteObject(m_hLargeImage); } if (m_hSnapinIcon != NULL) { ::DestroyIcon(m_hSnapinIcon); } } #if 0 HRESULT ExtractComputerNameExt(IDataObject * pDataObject, CString& strComputer) { // // Find the computer name from the ComputerManagement snapin // CLIPFORMAT CCF_MyComputMachineName = (CLIPFORMAT)RegisterClipboardFormat(MYCOMPUT_MACHINE_NAME); STGMEDIUM stgmedium = { TYMED_HGLOBAL, NULL }; FORMATETC formatetc = { CCF_MyComputMachineName, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; // // Allocate memory for the stream // int len = MAX_PATH; stgmedium.hGlobal = GlobalAlloc(GMEM_SHARE, len); if(stgmedium.hGlobal == NULL) return ERROR_NOT_ENOUGH_MEMORY; HRESULT hr = pDataObject->GetDataHere(&formatetc, &stgmedium); ASSERT(SUCCEEDED(hr)); // // Get the computer name // strComputer = (LPTSTR)stgmedium.hGlobal; GlobalFree(stgmedium.hGlobal); return hr; } HRESULT CCompMgrExtData::Init(IDataObject * pDataObject) { return ExtractComputerNameExt(pDataObject, m_ExtMachineName); } HRESULT STDMETHODCALLTYPE CCompMgrExtData::Notify( MMC_NOTIFY_TYPE event, LPARAM arg, LPARAM param, IComponentData* pComponentData, IComponent* pComponent, DATA_OBJECT_TYPES type) { CError err; CComPtr pConsole; CComQIPtr pHeader; CComQIPtr pResultData; if (pComponentData != NULL) { pConsole = ((CInetMgr *)pComponentData)->m_spConsole; } else { pConsole = ((CInetMgrComponent *)pComponent)->m_spConsole; } CComQIPtr pScope = pConsole; switch (event) { case MMCN_EXPAND: err = EnumerateScopePane((HSCOPEITEM)param, pScope); break; default: err = CSnapInItemImpl::Notify(event, arg, param, pComponentData, pComponent, type); break; } return err; } HRESULT CCompMgrExtData::EnumerateScopePane(HSCOPEITEM hParent, IConsoleNameSpace2 * pScope) { CError err; ASSERT_PTR(pScope); DWORD dwMask = SDI_PARENT; SCOPEDATAITEM scopeDataItem; ::ZeroMemory(&scopeDataItem, sizeof(SCOPEDATAITEM)); scopeDataItem.mask = SDI_STR | SDI_IMAGE | SDI_CHILDREN | SDI_OPENIMAGE | SDI_PARAM | dwMask; scopeDataItem.displayname = MMC_CALLBACK; scopeDataItem.nImage = scopeDataItem.nOpenImage = MMC_IMAGECALLBACK;//QueryImage(); scopeDataItem.lParam = (LPARAM)this; scopeDataItem.relativeID = hParent; scopeDataItem.cChildren = 1; err = pScope->InsertItem(&scopeDataItem); if (err.Succeeded()) { // // Cache the scope item handle // ASSERT(m_hScopeItem == NULL); m_hScopeItem = scopeDataItem.ID; // MMC_IMAGECALLBACK doesn't work in InsertItem. Update it here. scopeDataItem.mask = SDI_IMAGE | SDI_OPENIMAGE; err = pScope->SetItem(&scopeDataItem); } return err; } #endif