/*++ Copyright (c) 1994-1998 Microsoft Corporation Module Name : cinetmgr.cpp Abstract: Snapin object Author: Ronald Meijer (ronaldm) Project: Internet Services Manager Revision History: --*/ // // Include Files // #include "stdafx.h" #include "inetmgr.h" #include "cinetmgr.h" #include "connects.h" #include "dataobj.h" #include "afxdlgs.h" #include "constr.h" #include "metaback.h" #include "shutdown.h" #include #if !MSDEV_BUILD // // An odd difference between vc++ and sdk environments. // #include #endif !MSDEV_BUILD #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif // // Image background colour for the toolbar buttons // #define RGB_BK_IMAGES (RGB(255,0,255)) // purple #define ARRAYLEN(x) (sizeof(x) / sizeof((x)[0])) static HRESULT GetSnapinHelpFile(LPOLESTR * lpCompiledHelpFile); // // Toolbar Definition. String IDs for menu and tooltip text will be resolved at initialization // static MMCBUTTON SnapinButtons[] = { { IDM_CONNECT - 1, IDM_CONNECT, TBSTATE_ENABLED, TBSTYLE_BUTTON, (BSTR)IDS_MENU_CONNECT, (BSTR)IDS_MENU_TT_CONNECT }, // { IDM_DISCOVER - 1, IDM_DISCOVER, TBSTATE_ENABLED, TBSTYLE_BUTTON, (BSTR)IDS_MENU_DISCOVER, (BSTR)IDS_MENU_TT_CONNECT }, { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, _T(" "), _T("") }, { IDM_START - 1, IDM_START, TBSTATE_ENABLED, TBSTYLE_BUTTON, (BSTR)IDS_MENU_START, (BSTR)IDS_MENU_TT_START }, { IDM_STOP - 1, IDM_STOP, TBSTATE_ENABLED, TBSTYLE_BUTTON, (BSTR)IDS_MENU_STOP, (BSTR)IDS_MENU_TT_STOP }, { IDM_PAUSE - 1, IDM_PAUSE, TBSTATE_ENABLED, TBSTYLE_BUTTON, (BSTR)IDS_MENU_PAUSE, (BSTR)IDS_MENU_TT_PAUSE }, { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, _T(" "), _T("") }, // // Add-on tools come here // }; #define NUM_BUTTONS (ARRAYLEN(SnapinButtons)) #define NUM_BITMAPS (5) // // Name of our taskpad group // const LPCTSTR g_cszTaskGroup = _T("CMTP1"); template TYPE * Extract( IN LPDATAOBJECT lpDataObject, IN unsigned int cf, IN int len = -1 ) /*++ Routine Description: Template function to extract information of type TYPE from the data object Arguments: LPDATAOBJECT lpDataObject : Data object to extract data from unsigned int cf : Clipboard format describing the info type Return Value: Pointer to type TYPE --*/ { ASSERT(lpDataObject != NULL); TYPE * p = NULL; STGMEDIUM stgmedium = { TYMED_HGLOBAL, NULL }; FORMATETC formatetc = { (CLIPFORMAT)cf, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; // // Allocate memory for the stream // if (len < 0) { len = sizeof(TYPE); } stgmedium.hGlobal = GlobalAlloc(GMEM_SHARE, len); do { if (stgmedium.hGlobal == NULL) { break; } HRESULT hr = lpDataObject->GetDataHere(&formatetc, &stgmedium); if (FAILED(hr)) { break; } p = (TYPE *)stgmedium.hGlobal; if (p == NULL) { break; } } while (FALSE); return p; } // // Data object extraction helpers // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< /* BOOL IsMMCMultiSelectDataObject( IN LPDATAOBJECT lpDataObject ) { if (lpDataObject == NULL) { return FALSE; } static UINT s_cf = 0; if (s_cf == 0) { // // Multi-select clipboard format not registered -- do it now // s_cf = RegisterClipboardFormat(CCF_MMC_MULTISELECT_DATAOBJECT); } FORMATETC fmt = {s_cf, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; return (lpDataObject->QueryGetData(&fmt) == S_OK); } */ CLSID * ExtractClassID( IN LPDATAOBJECT lpDataObject ) { return Extract(lpDataObject, CDataObject::m_cfCoClass); } GUID * ExtractNodeType( IN LPDATAOBJECT lpDataObject ) { return Extract(lpDataObject, CDataObject::m_cfNodeType); } wchar_t * ExtractMachineName( IN LPDATAOBJECT lpDataObject ) { wchar_t * lpszMachineName = Extract( lpDataObject, CDataObject::m_cfISMMachineName, (MAX_PATH + 1) * sizeof(wchar_t) ); if (lpszMachineName == NULL) { // // This is an extension -- grab the computer management // name instead. // lpszMachineName = Extract( lpDataObject, CDataObject::m_cfMyComputMachineName, (MAX_PATH + 1) * sizeof(wchar_t) ); } return lpszMachineName; } INTERNAL * ExtractInternalFormat( IN LPDATAOBJECT lpDataObject ) { ASSERT(lpDataObject != NULL); return Extract(lpDataObject, CDataObject::m_cfInternal); } // // IEnumTask Implementation // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< CEnumTasks::CEnumTasks() /*++ Routine Description: Constructor Arguments: None Return Value: N/A --*/ : m_pObject(NULL) { } CEnumTasks::~CEnumTasks() /*++ Routine Description: Destructor Arguments: None Return Value: None --*/ { } HRESULT CEnumTasks::Next( IN ULONG celt, IN MMC_TASK * rgelt, IN ULONG * pceltFetched ) /*++ Routine Description: Add next task to taskpad Arguments: ULONG celt, MMC_TASK * rgelt, ULONG * pceltFetched Return Value: HRESULT --*/ { // // Callee fills MMC_TASK elements (via CoTaskMemAlloc) // ASSERT(!IsBadWritePtr(rgelt, celt * sizeof(MMC_TASK))); if (m_pObject == NULL) { // // Must be at the snap-in's root handle // return S_FALSE; } // // celt will actually always only be 1 // ASSERT(celt == 1); HRESULT hr = S_FALSE; for (ULONG i = 0; i < celt; ++i) { MMC_TASK * task = &rgelt[i]; hr = m_pObject->AddNextTaskpadItem(task); if (FAILED(hr)) { if (pceltFetched) { *pceltFetched = i; } break; } } // // If we get here all is well // if(pceltFetched) { *pceltFetched = celt; } return hr; } HRESULT CEnumTasks::Skip( IN ULONG celt ) /*++ Routine Description: Skip the task index Arguments: ULONG celt : Number of tasks to skip Return Value: HRESULT --*/ { return E_NOTIMPL; } HRESULT CEnumTasks::Reset() /*++ Routine Description: Result the taskpad enumeration index Arguments: None Return Value: HRESULT --*/ { return E_NOTIMPL; } HRESULT CEnumTasks::Clone( IN IEnumTASK ** ppenum ) /*++ Routine Description: Close a task -- obsolete, no longer supported by mmc Arguments: IEnumTASK ** ppenum : Source task to clone Return Value: HRESULT --*/ { return E_NOTIMPL; } HRESULT CEnumTasks::Init( IN IDataObject * pdo, IN LPOLESTR szTaskGroup ) /*++ Routine Description: Here is where we see what taskpad we are providing tasks for. In our case we know that we only have one taskpad. The string we test for is "CMTP1". This was the string following the '#' that we passed in GetResultViewType. Arguments: IDataObject * pdo : Data object LPOLESTR szTaskGroup : Taskpad group name Return Value: HRESULT --*/ { // // Return ok if we can handle data object and group. // if (!lstrcmp(szTaskGroup, g_cszTaskGroup)) { // // CODEWORK: How about a helper for this! // INTERNAL * pInternal = ExtractInternalFormat(pdo); if (pInternal == NULL) { // // Extension // return S_OK; } m_pObject = (CIISObject *)pInternal->m_cookie; return S_OK; } // // Should never happen // ASSERT(FALSE); return S_FALSE; } // // CSnapin class // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< DEBUG_DECLARE_INSTANCE_COUNTER(CSnapin); long CSnapin::lDataObjectRefCount = 0; CSnapin::CSnapin() /*++ Routine Description: Constructor Arguments: None Return Value: N/A --*/ : m_pConsole(NULL), m_pHeader(NULL), m_pResult(NULL), m_pImageResult(NULL), m_pComponentData(NULL), m_pToolbar(NULL), m_pControlbar(NULL), m_pbmpToolbar(NULL), m_pConsoleVerb(NULL), m_oblResultItems(), m_strlRef(), m_fTaskView(FALSE), m_fSettingsChanged(FALSE), m_fIsExtension(FALSE), m_fWinSockInit(FALSE) { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); DEBUG_INCREMENT_INSTANCE_COUNTER(CSnapin); #ifdef _DEBUG dbg_cRef = 0; #endif // _DEBUG } CSnapin::~CSnapin() /*++ Routine Description: Destructor Arguments: N/A Return Value: N/A --*/ { #ifdef _DEBUG ASSERT(dbg_cRef == 0); #endif // _DEBUG DEBUG_DECREMENT_INSTANCE_COUNTER(CSnapin); SAFE_RELEASE(m_pToolbar); SAFE_RELEASE(m_pControlbar); // // Make sure the interfaces have been released // ASSERT(m_pConsole == NULL); ASSERT(m_pHeader == NULL); ASSERT(m_pToolbar == NULL); if (m_pbmpToolbar) { m_pbmpToolbar->DeleteObject(); delete m_pbmpToolbar; } // // Should have been removed via notification, but just in case: // ASSERT(m_oblResultItems.GetCount() == 0); m_oblResultItems.RemoveAll(); } LPTSTR CSnapin::StringReferenceFromResourceID( IN UINT nID ) /*++ Routine Description: Load a string from the resource segment, add it to the internal string table, and return a pointer to it. The pointer will be valid for the entire scope of the CSnapin object Arguments UINT nID : Resource ID Return Value: A pointer to the string. --*/ { CString str; VERIFY(str.LoadString(nID)); m_strlRef.AddTail(str); CString & strRef = m_strlRef.GetAt(m_strlRef.GetTailPosition()); return (LPTSTR)(LPCTSTR)strRef; } BOOL CSnapin::IsEnumerating( IN LPDATAOBJECT lpDataObject ) /*++ Routine Description: Return TRUE if we are enumerating our main folder Arguments: LPDATAOBJECT lpDataObject : Data object Return Value: TRUE if we are enumerating our main folder, FALSE if not --*/ { BOOL bResult = FALSE; ASSERT(lpDataObject); GUID * nodeType = ExtractNodeType(lpDataObject); // // Is this my main node (static folder node type) // bResult = ::IsEqualGUID(*nodeType, cInternetRootNode); // // Free resources // FREE_DATA(nodeType); return bResult; } // // CSnapin's IComponent implementation // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< STDMETHODIMP CSnapin::GetResultViewType( IN MMC_COOKIE cookie, OUT BSTR * ppViewType, OUT long * pViewOptions ) /*++ Routine Description: Tell MMC what our result view looks like Arguments: MMC_COOKIE cookie : Currently selected cookie BSTR * ppViewType : Return view type here long * pViewOptions : View options Return Value: S_FALSE to use default view type, S_OK indicates the view type is returned in *ppViewType --*/ { // // Taskpads not supported for beta2 // #if TASKPADS_SUPPORTED if (m_fTaskView) { // // We will use the default DHTML provided by MMC. It actually // resides as a resource inside MMC.EXE. We just get the path // to it and use that. // // The one piece of magic here is the text following the '#'. That // is the special way we have of identifying they taskpad we are // talking about. Here we say we are wanting to show a taskpad // that we refer to as "CMTP1". We will actually see this string // pass back to us later. If someone is extending our taskpad, // they also need to know what this secret string is. // *pViewOptions = MMC_VIEW_OPTIONS_NONE; CString strResURL; HRESULT hr = BuildResURL(strResURL, NULL); if (FAILED(hr)) { return hr; } strResURL += _T("/default.htm#"); strResURL += g_cszTaskGroup; TRACEEOLID("Taskpad URL is " << strResURL); *ppViewType = CoTaskDupString((LPCOLESTR)strResURL); return S_OK; } #endif // TASKPADS_SUPPORTED // // Use default view // *pViewOptions = MMC_VIEW_OPTIONS_USEFONTLINKING; //*pViewOptions = MMC_VIEW_OPTIONS_USEFONTLINKING // | MMC_VIEW_OPTIONS_LEXICAL_SORT; return S_FALSE; } STDMETHODIMP CSnapin::Initialize( IN LPCONSOLE lpConsole ) /*++ Routine Description: Initialize the console Arguments" LPCONSOLE lpConsole : Pointer to console Return Value: HRESULT --*/ { ASSERT(lpConsole != NULL); ::AfxEnableControlContainer(); // // Initialize IISUI extension DLL // #ifndef _COMSTATIC // // Initialize IISUI extension DLL // InitIISUIDll(); #endif // _COMSTATIC // // Initialise winsock // // ISSUE: If proxy is installed, this might take a long time // if the proxy server is in a hung state. // WSADATA wsaData; m_fWinSockInit = (::WSAStartup(MAKEWORD(1, 1), &wsaData) == 0); // // Save the IConsole pointer // m_pConsole = lpConsole; m_pConsole->AddRef(); // // Load resource strings // LoadResources(); // // QI for a IHeaderCtrl // HRESULT hr = m_pConsole->QueryInterface( IID_IHeaderCtrl, (void **)&m_pHeader ); // // Give the console the header control interface pointer // if (SUCCEEDED(hr)) { m_pConsole->SetHeader(m_pHeader); } m_pConsole->QueryInterface( IID_IResultData, (void **)&m_pResult ); m_pConsole->QueryResultImageList(&m_pImageResult); m_pConsole->QueryConsoleVerb(&m_pConsoleVerb); // // Resolve tool tips texts // CString str; static BOOL fInitialised = FALSE; if (!fInitialised) { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); for (int i = 0; i < NUM_BUTTONS; ++i) { if (SnapinButtons[i].idCommand != 0) { SnapinButtons[i].lpButtonText = StringReferenceFromResourceID( (UINT)PtrToUlong(SnapinButtons[i].lpButtonText)); SnapinButtons[i].lpTooltipText = StringReferenceFromResourceID( (UINT)PtrToUlong(SnapinButtons[i].lpTooltipText)); } } fInitialised = TRUE; } return S_OK; } STDMETHODIMP CSnapin::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 : Event LPARAM arg : Argument LPARAM param : Parameter Return Value: HRESULT --*/ { HRESULT hr = S_OK; INTERNAL * pInternal = NULL; MMC_COOKIE cookie; switch(event) { case MMCN_REFRESH: case MMCN_RENAME: // // Delegate it to the IComponentData // ASSERT(m_pComponentData != NULL); hr = m_pComponentData->Notify(lpDataObject, event, arg, param); case MMCN_PROPERTY_CHANGE: hr = OnPropertyChange(lpDataObject); break; case MMCN_DELETE: // // Let IComponentData do the deletion, then refresh the parent object // ASSERT(m_pComponentData != NULL); hr = m_pComponentData->Notify(lpDataObject, event, arg, param); m_pComponentData->Notify(lpDataObject, MMCN_REFRESH, arg, param); case MMCN_VIEW_CHANGE: hr = OnUpdateView(lpDataObject); break; case MMCN_SNAPINHELP: case MMCN_CONTEXTHELP: { LPOLESTR pCompiledHelpFile = NULL; if (SUCCEEDED(hr = GetSnapinHelpFile(&pCompiledHelpFile))) { IDisplayHelp * pdh; if (SUCCEEDED(hr = m_pConsole->QueryInterface( IID_IDisplayHelp, (void **)&pdh))) { CString topic = PathFindFileName(pCompiledHelpFile); topic += _T("::/iint1.htm"); LPTSTR p = topic.GetBuffer(topic.GetLength()); hr = pdh->ShowTopic(p); topic.ReleaseBuffer(); pdh->Release(); } } if (FAILED(hr)) { CError err(hr); err.MessageBoxOnFailure(); } CoTaskMemFree(pCompiledHelpFile); } break; default: // // don't process the internal format if the dataobject is null. // if (lpDataObject == NULL) { return S_OK; } INTERNAL * pInternal = ExtractInternalFormat(lpDataObject); if (pInternal == NULL) { // // Extension // TRACEEOLID("This is an (CSnapin) EXTENSION"); cookie = NULL; CIISObject::m_fIsExtension = m_fIsExtension = TRUE; } else { cookie = pInternal->m_cookie; FREE_DATA(pInternal); } switch(event) { case MMCN_CLICK: case MMCN_DBLCLICK: hr = OnResultItemClkOrDblClk(cookie, (event == MMCN_DBLCLICK)); break; case MMCN_ADD_IMAGES: OnAddImages(cookie, arg, param); break; case MMCN_SHOW: hr = OnShow(cookie, arg, param); break; case MMCN_ACTIVATE: hr = OnActivate(cookie, arg, param); break; case MMCN_MINIMIZED: hr = OnMinimize(cookie, arg, param); break; case MMCN_BTN_CLICK: break; case MMCN_SELECT: HandleStandardVerbs(arg, lpDataObject); break; case MMCN_COLUMN_CLICK: break; case MMCN_MENU_BTNCLICK: break; default: ASSERT(FALSE); // Handle new messages hr = E_UNEXPECTED; break; } } return hr; } STDMETHODIMP CSnapin::Destroy( IN MMC_COOKIE cookie ) /*++ Routine Description: Destruction handler Arguments: MMC_COOKIE cookie : Currently selected cookie Return Value: HRESULT --*/ { // // Release the interfaces that we QI'ed // AFX_MANAGE_STATE(AfxGetStaticModuleState()); if (m_pConsole != NULL) { // // Tell the console to release the header control interface // m_pConsole->SetHeader(NULL); SAFE_RELEASE_SETTONULL(m_pHeader); SAFE_RELEASE_SETTONULL(m_pResult); SAFE_RELEASE_SETTONULL(m_pImageResult); SAFE_RELEASE_SETTONULL(m_pConsole); SAFE_RELEASE_SETTONULL(m_pComponentData); SAFE_RELEASE_SETTONULL(m_pConsoleVerb); SAFE_RELEASE_SETTONULL(m_pToolbar); SAFE_RELEASE_SETTONULL(m_pControlbar); } // // Terminate use of the WinSock routines. // if (m_fWinSockInit) { ::WSACleanup(); m_fWinSockInit = FALSE; } // // Everything OK // return S_OK; } STDMETHODIMP CSnapin::QueryDataObject( IN MMC_COOKIE cookie, IN DATA_OBJECT_TYPES type, OUT LPDATAOBJECT * ppDataObject ) /*++ Routine Description: Get dataobject info Arguments: MMC_COOKIE cookie : cookie DATA_OBJECT_TYPES : Data object type LPDATAOBJECT * : Returns the data object Return Value: HRESULT --*/ { // // Delegate it to the IComponentData // ASSERT(m_pComponentData != NULL); return m_pComponentData->QueryDataObject(cookie, type, ppDataObject); } void CSnapin::LoadResources() /*++ Routine Description: Load resource belong to the main snapin Arguments: None Return Value: None --*/ { // // Load strings from resources // } // // IExtendPropertySheet Implementation // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< STDMETHODIMP CSnapin::CreatePropertyPages( IN LPPROPERTYSHEETCALLBACK lpProvider, IN LONG_PTR handle, IN LPDATAOBJECT lpDataObject ) /*++ Routine Description: Create the property pages for the given object Arguments: LPPROPERTYSHEETCALLBACK lpProvider : Provider LONG_PTR handle : Handle LPDATAOBJECT lpDataObject : Data object Return Value: HRESULT --*/ { return static_cast(m_pComponentData)->CreatePropertyPages( lpProvider, handle, lpDataObject ); } STDMETHODIMP CSnapin::QueryPagesFor( IN LPDATAOBJECT lpDataObject ) /*++ Routine Description: Check to see if a property sheet should be brought up for this data object Arguments: LPDATAOBJECT lpDataObject : Data object Return Value: S_OK, if properties may be brought up for this item, S_FALSE otherwise --*/ { return static_cast(m_pComponentData)->QueryPagesFor( lpDataObject ); } HRESULT CSnapin::InitializeHeaders( IN MMC_COOKIE cookie ) /*++ Routine Description: Initialize the result view headers for this cookie Arguments: MMC_COOKIE cookie : Current cookie Return Value: HRESULT --*/ { CIISObject * pObject = (CIISObject *)cookie; // // The result pane is always one level behind the scope pane // for a folder object // if (pObject == NULL) { CIISMachine::InitializeHeaders(m_pHeader); } else { pObject->InitializeChildHeaders(m_pHeader); } return S_OK; } class CImage : public CObjectPlus /*++ Class Description: A structure to tie a bitmap together with a background colour mask Public Interface: CImage : Constructor ~CImage : Destructor Notes: This image owns the bitmap and will delete it upon destruction. --*/ { // // Constructor/Destructor // public: CImage( IN CBitmap * pbmp, IN COLORREF rgb ) : m_pbmp(pbmp), m_rgb(rgb) { } ~CImage() { ASSERT(m_pbmp != NULL); m_pbmp->DeleteObject(); delete m_pbmp; } // // Access // public: // // Get the bitmap object // CBitmap & GetBitmap() { return *m_pbmp; } // // Get the background colour definition // COLORREF & GetBkColor() { return m_rgb; } private: CBitmap * m_pbmp; COLORREF m_rgb; }; // // Global oblists of service bitmaps -- used by scope and result side // CObListPlus g_obl16x16; CObListPlus g_obl32x32; // // Add-on-tools definitions // CObListPlus g_oblAddOnTools; HRESULT CSnapin::InitializeBitmaps( IN MMC_COOKIE cookie ) /*++ Routine Description: Initialize the service bitmaps Arguments: MMC_COOKIE cookie : Currently selected cookie (CIISObject *) Return Value: HRESULT --*/ { ASSERT(m_pImageResult != NULL); CBitmap bmp16x16, bmp32x32, bmpMgr16x16, bmpMgr32x32; { // // Load the bitmaps from the dll // AFX_MANAGE_STATE(::AfxGetStaticModuleState()); VERIFY(bmp16x16.LoadBitmap(IDB_VIEWS16)); VERIFY(bmp32x32.LoadBitmap(IDB_VIEWS32)); VERIFY(bmpMgr16x16.LoadBitmap(IDB_INETMGR16)); VERIFY(bmpMgr32x32.LoadBitmap(IDB_INETMGR16)); } // // Set the images // HRESULT hr = m_pImageResult->ImageListSetStrip( (LONG_PTR *)(HBITMAP)bmp16x16, (LONG_PTR *)(HBITMAP)bmp32x32, 0, RGB_BK_IMAGES ); ASSERT(hr == S_OK); // // Add on inetmgr bitmap // hr = m_pImageResult->ImageListSetStrip( (LONG_PTR *)(HBITMAP)bmpMgr16x16, (LONG_PTR *)(HBITMAP)bmpMgr32x32, BMP_INETMGR, RGB_BK_IMAGES ); // // Add the ones from the service config DLLs // POSITION pos16x16 = g_obl16x16.GetHeadPosition(); POSITION pos32x32 = g_obl32x32.GetHeadPosition(); int i = BMP_SERVICE; while (pos16x16 && pos32x32) { CImage * pimg16x16 = (CImage *)g_obl16x16.GetNext(pos16x16); CImage * pimg32x32 = (CImage *)g_obl32x32.GetNext(pos32x32); ASSERT(pimg16x16 && pimg32x32); hr = m_pImageResult->ImageListSetStrip( (LONG_PTR *)(HBITMAP)pimg16x16->GetBitmap(), (LONG_PTR *)(HBITMAP)pimg32x32->GetBitmap(), i++, pimg16x16->GetBkColor() ); ASSERT(hr == S_OK); } return hr; } STDMETHODIMP CSnapin::GetDisplayInfo( IN LPRESULTDATAITEM lpResultDataItem ) /*++ Routine Description: Get display info for result item Arguments: LPRESULTDATAITEM lpResultDataItem : Address of result data tiem Return Value: HRESULT --*/ { wchar_t * szString = _T(""); ASSERT(lpResultDataItem != NULL); static int nImage = -1; static CString str; if (lpResultDataItem) { CIISObject * pObject = (CIISObject *)lpResultDataItem->lParam; ASSERT(pObject != NULL); if (pObject != NULL && pObject->IsValidObject()) { pObject->GetResultDisplayInfo(lpResultDataItem->nCol, str, nImage); lpResultDataItem->str = (LPTSTR)(LPCTSTR)str; lpResultDataItem->nImage = nImage; return S_OK; } } return S_FALSE; } // // IExtendContextMenu Implementation // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<(m_pComponentData)->AddMenuItems( lpDataObject, lpContextMenuCallback, pInsertionAllowed ); } STDMETHODIMP CSnapin::Command( IN long nCommandID, IN LPDATAOBJECT lpDataObject ) /*++ Routine Description: Command handler -- pass it on the component data Arguments: long nCommandID : Command ID LPDTATAOBJECT lpDataObject : Data object Return Value: HRESULT --*/ { HRESULT hr = S_OK; INTERNAL * pInternal = NULL; switch(nCommandID) { case -1: // // Built-in views -- everything handled. // m_fTaskView = FALSE; m_fSettingsChanged = TRUE; break; #if TASKPADS_SUPPORTED // // No taskpad support in beta2 // case IDM_VIEW_TASKPAD: // // Handle view change // m_fTaskView = !m_fTaskView; m_fSettingsChanged = TRUE; // // Reselect current scope item to force view change // pInternal = ExtractInternalFormat(lpDataObject); if (pInternal != NULL) { CIISObject * pObject = (CIISObject *)pInternal->m_cookie; FREE_DATA(pInternal); if (pObject) { m_pConsole->SelectScopeItem(pObject->GetScopeHandle()); } else { // // Must be the root item // m_pConsole->SelectScopeItem( static_cast(m_pComponentData)->GetRootHandle() ); } } break; #endif // TASKPADS_SUPPORTED default: // // Pass it on to CComponentDataImpl // hr = static_cast(m_pComponentData)->Command( nCommandID, lpDataObject ); } return hr; } // // ITaskPad implementation // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< HRESULT CSnapin::TaskNotify( IN IDataObject * pdo, IN VARIANT * pvarg, IN VARIANT * pvparam ) /*++ Routine Description: Handle notification from taskpad Arguments: IDataObject * pdo, VARIANT * pvarg, VARIANT * pvparam Return Value: HRESULT --*/ { if (pvarg->vt == VT_I4) { // // Pass it on to CComponentDataImpl // return static_cast(m_pComponentData)->Command( pvarg->lVal, pdo ); } return S_OK; } HRESULT CSnapin::GetTitle( IN LPOLESTR szGroup, OUT LPOLESTR * lpszTitle ) /*++ Routine Description: Get the title of the taskpad Arguments: LPOLESTR szGroup : Group name LPOLESTR * szTitle : Returns title Return Value: HRESULT --*/ { OLECHAR sztitle[] = L"IIS TaskPad"; *lpszTitle = CoTaskDupString(sztitle); return *lpszTitle != NULL ? S_OK : E_OUTOFMEMORY; } HRESULT CSnapin::GetDescriptiveText( IN LPOLESTR szGroup, OUT LPOLESTR * lpszDescriptiveText ) /*++ Routine Description: Get the descriptive text Arguments: LPOLESTR szGroup : Group name LPOLESTR * szTitle : Returns title Return Value: HRESULT --*/ { return E_NOTIMPL; } HRESULT CSnapin::GetBanner( IN LPOLESTR szGroup, OUT LPOLESTR * pszBitmapResource ) /*++ Routine Description: Get the banner resource Arguments: LPOLESTR szGroup : Group name LPOLESTR * pszBitmapResource : Returns bitmap resource Return Value: HRESULT --*/ { CString strResURL; HRESULT hr = BuildResURL(strResURL); if (FAILED(hr)) { return hr; } strResURL += _T("/img\\ntbanner.gif"); TRACEEOLID(strResURL); *pszBitmapResource = CoTaskDupString((LPCOLESTR)strResURL); return *pszBitmapResource != NULL ? S_OK : E_OUTOFMEMORY; } HRESULT CSnapin::GetBackground( IN LPOLESTR szGroup, OUT MMC_TASK_DISPLAY_OBJECT * pTDO ) /*++ Routine Description: Get the background resource Arguments: LPOLESTR szGroup : Group name LPOLESTR * pszBitmapResource : Returns bitmap resource Return Value: HRESULT --*/ { return E_NOTIMPL; } HRESULT CSnapin::EnumTasks( IN IDataObject * pdo, IN LPOLESTR szTaskGroup, OUT IEnumTASK ** ppEnumTASK ) /*++ Routine Description: Enumerate the tasks on the taskpad Arguments: IDataObject * pdo : Data object selected; LPOLESTR szTaskGroup, : Taskgroup name IEnumTASK ** ppEnumTASK : Returns enumtask interface Return Value: HRESULT --*/ { HRESULT hr = S_OK; try { ASSERT(ppEnumTASK != NULL); CComObject* pObject; CComObject::CreateInstance(&pObject); ASSERT(pObject != NULL); VERIFY(SUCCEEDED(pObject->Init(pdo, szTaskGroup))); hr = pObject->QueryInterface( IID_IEnumTASK, reinterpret_cast(ppEnumTASK) ); } catch(CMemoryException * e) { e->Delete(); hr = E_OUTOFMEMORY; } return hr; } // // IExtendControlbar implementation // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< STDMETHODIMP CSnapin::SetControlbar( IN LPCONTROLBAR lpControlbar ) /*++ Routine Description: Set the control bar Arguments: LPCONTROLBAR lpControlbar : Pointer to control bar Return Value: HRESULT --*/ { if (lpControlbar != NULL) { // // Hold on to the controlbar interface. // if (m_pControlbar != NULL) { m_pControlbar->Release(); } m_pControlbar = lpControlbar; m_pControlbar->AddRef(); HRESULT hr = S_FALSE; // // Create the Toolbar // if (!m_pToolbar) { hr = m_pControlbar->Create(TOOLBAR, this, (LPUNKNOWN *)&m_pToolbar); ASSERT(SUCCEEDED(hr)); m_pbmpToolbar = new CBitmap; { // // Add the bitmap // AFX_MANAGE_STATE(AfxGetStaticModuleState()); m_pbmpToolbar->LoadBitmap(IDB_TOOLBAR); } // // Add 16x16 bitmaps // hr = m_pToolbar->AddBitmap( NUM_BITMAPS, (HBITMAP)*m_pbmpToolbar, 16, 16, TB_COLORMASK ); ASSERT(SUCCEEDED(hr)); // // Add the buttons to the toolbar // hr = m_pToolbar->AddButtons(NUM_BUTTONS, SnapinButtons); ASSERT(SUCCEEDED(hr)); // // Now add the add-on tools // POSITION pos = g_oblAddOnTools.GetHeadPosition(); // // Toolbar buttons were created using buttonface // background colour // COLORREF rgbMask = ::GetSysColor(COLOR_BTNFACE); while (pos != NULL) { CISMShellExecutable * pTool = (CISMShellExecutable *)g_oblAddOnTools.GetNext(pos); ASSERT(pTool != NULL); if (pTool->ShowInToolBar()) { if (pTool->InitializedOK()) { // // Add 16x16 image // hr = m_pToolbar->AddBitmap( 1, pTool->GetBitmap(), 16, 16, rgbMask ); ASSERT(SUCCEEDED(hr)); hr = m_pToolbar->AddButtons(1, pTool->GetButton()); ASSERT(SUCCEEDED(hr)); } } } } } else { SAFE_RELEASE_SETTONULL(m_pControlbar); } return S_OK; } CISMShellExecutable * CSnapin::GetCommandAt( IN CObListPlus & obl, IN int nIndex ) /*++ Routine Description: Get the add-on tool at the given index. Arguments: int nIndex : Index where to look for the add-on tool Return Value: Shell Executable object pointer, or NULL if the index was not valid --*/ { if (nIndex < 0 || nIndex >= obl.GetCount()) { TRACEEOLID("Invalid tool index requested"); return NULL; } return (CISMShellExecutable *)obl.GetAt(obl.FindIndex(nIndex)); } HRESULT CSnapin::SetToolbarStates( IN MMC_COOKIE cookie ) /*++ Routine Description: Set toolbar states based on current selection Arguments: MMC_COOKIE cookie : Currently selected scope item cookie (CIISObject *) Return Value: HRESULT --*/ { CIISObject * pObject = (CIISObject *)cookie; if (m_pToolbar) { m_pToolbar->SetButtonState( IDM_CONNECT, ENABLED, !CIISObject::m_fIsExtension ); m_pToolbar->SetButtonState( IDM_PAUSE, ENABLED, pObject && pObject->IsPausable() ); m_pToolbar->SetButtonState( IDM_START, ENABLED, pObject && pObject->IsStartable() ); m_pToolbar->SetButtonState( IDM_STOP, ENABLED, pObject && pObject->IsStoppable() ); m_pToolbar->SetButtonState( IDM_PAUSE, BUTTONPRESSED, pObject && pObject->IsPaused() ); } return S_OK; } HRESULT CSnapin::OnButtonClick( IN LPDATAOBJECT lpDataObject, IN long lID ) /*++ Routine Description: Handle toolbar button click Arguments: LPDATAOBJECT lpDataObject : Current data object LONG lID : Button ID pressed Return Value: HRESULT --*/ { // // Check to see if the button ID is in the add-on tools // range // if (lID >= IDM_TOOLBAR) { INTERNAL * pInternal = ExtractInternalFormat(lpDataObject); CIISObject * pObject = pInternal ? (CIISObject *)pInternal->m_cookie : NULL; // // Button ID was in the add-on tools range. Match // up the ID with the add-on tool, and execute same. // CISMShellExecutable * pTool = GetCommandAt( g_oblAddOnTools, lID - IDM_TOOLBAR ); ASSERT(pTool != NULL); if (pTool != NULL) { CError err; if (pObject == NULL) { // // Nothing selected, execute with no parameters // err = pTool->Execute(); } else { // // Pass server/service to the add-on tool // LPCTSTR lpstrServer = pObject->GetMachineName(); LPCTSTR lpstrService = pObject->GetServiceName(); err = pTool->Execute(lpstrServer, lpstrService); } if (err.Failed()) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); err.MessageBox(); } } FREE_DATA(pInternal); } else { // // Otherwise, it maps to a menu command, pass it on to the // component data // ASSERT(m_pComponentData != NULL); ((CComponentDataImpl *)m_pComponentData)->Command(lID, lpDataObject); } return S_OK; } void CSnapin::HandleStandardVerbs( IN LPARAM arg, IN LPDATAOBJECT lpDataObject ) /*++ Routine Description: Set the standard verb states based on current selection Arguments: LPARAM arg : Argument LPDATAOBJECT lpDataObject : Selected data object Return Value: None --*/ { if (lpDataObject == NULL || m_pConsoleVerb == NULL) { return; } CIISObject * pObject = NULL; INTERNAL * pInternal = ExtractInternalFormat(lpDataObject); if (pInternal != NULL) { pObject = (CIISObject *)pInternal->m_cookie; } m_pConsoleVerb->SetVerbState( MMC_VERB_RENAME, ENABLED, pObject && pObject->IsRenamable() ); m_pConsoleVerb->SetVerbState(MMC_VERB_COPY, HIDDEN, TRUE); m_pConsoleVerb->SetVerbState(MMC_VERB_PASTE, HIDDEN, TRUE); m_pConsoleVerb->SetVerbState( MMC_VERB_DELETE, ENABLED, pObject && pObject->IsDeletable() ); m_pConsoleVerb->SetVerbState( MMC_VERB_REFRESH, ENABLED, pObject && pObject->IsRefreshable() ); #ifdef MMC_PAGES BOOL fConfig = pObject && pObject->IsMMCConfigurable(); m_pConsoleVerb->SetVerbState(MMC_VERB_PROPERTIES, ENABLED,fConfig); if (pObject && pObject->IsLeafNode()) { m_pConsoleVerb->SetDefaultVerb(MMC_VERB_PROPERTIES); } else { m_pConsoleVerb->SetDefaultVerb(MMC_VERB_OPEN); } #else m_pConsoleVerb->SetVerbState(MMC_VERB_PROPERTIES, HIDDEN, TRUE); #endif } void CSnapin::HandleToolbar( IN LPARAM arg, IN LPARAM param ) /*++ Routine Description: Handle toolbar states Arguments: LPARAM arg : Selection change notification argument LPARAM param : Selection change notification parameter Return Value: None. --*/ { INTERNAL * pInternal = NULL; CIISObject * pObject = NULL; HRESULT hr; LPDATAOBJECT lpDataObject = NULL; BOOL bScope = (BOOL)LOWORD(arg); BOOL bSelect = (BOOL)HIWORD(arg); if (bScope) { lpDataObject = (LPDATAOBJECT)param; if (lpDataObject != NULL) { pInternal = ExtractInternalFormat(lpDataObject); } if (pInternal == NULL) { return; } pObject = (CIISObject *)pInternal->m_cookie; // // Attach the toolbars to the window // if (m_pControlbar != NULL) { hr = m_pControlbar->Attach(TOOLBAR, (LPUNKNOWN)m_pToolbar); ASSERT(SUCCEEDED(hr)); } } else { // // Result Pane -- disable all if nothing selected. // if (bSelect) { lpDataObject = (LPDATAOBJECT)param; if (lpDataObject != NULL) { pInternal = ExtractInternalFormat(lpDataObject); } if (pInternal == NULL) { return; } pObject = (CIISObject *)pInternal->m_cookie; // // Attach the toolbars to the window // hr = m_pControlbar->Attach(TOOLBAR, (LPUNKNOWN)m_pToolbar); ASSERT(SUCCEEDED(hr)); } } if (pInternal) { FREE_DATA(pInternal); } SetToolbarStates((MMC_COOKIE)pObject); } STDMETHODIMP CSnapin::ControlbarNotify( IN MMC_NOTIFY_TYPE event, IN LPARAM arg, IN LPARAM param ) /*++ Routine Description: Handle control bar notification Arguments: MMC_NOTIFY_TYPE event : Notification event LPARAM arg : argument as needed LPARAM param : Parameter ad needed Return Value: HRESULT --*/ { HRESULT hr = S_OK; switch (event) { case MMCN_BTN_CLICK: // // Note for MMC: it seems to be that arg and param // should be reversed here. The MMCN_SELECT // casts the dataobject to the param (which makes // more sense anyway). // TRACEEOLID("CSnapin::ControlbarNotify - MMCN_BTN_CLICK"); OnButtonClick((LPDATAOBJECT)arg, (long)PtrToUlong((PVOID)param)); break; case MMCN_SELECT: TRACEEOLID("CSnapin::ControlbarNotify - MMCN_SEL_CHANGE"); HandleToolbar(arg, param); break; case MMCN_HELP: break; // New default: // // Unhandled event // ASSERT(FALSE); } return hr; } STDMETHODIMP CSnapin::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 --*/ { // // Delegate it to the IComponentData // ASSERT(m_pComponentData != NULL); return m_pComponentData->CompareObjects(lpDataObjectA, lpDataObjectB); } /* virtual */ HRESULT CSnapin::Compare( IN RDCOMPARE * prdc, OUT int * pnResult ) /*++ Routine Description: Compare method Arguments: RDCOMPARE * prdc : Compare structure int * pnResult : Returns result Return Value: HRESULT --*/ { if (!pnResult || !prdc || !prdc->prdch1->cookie || !prdc->prdch2->cookie) { ASSERT(FALSE); return E_POINTER; } CIISObject * pObjectA = (CIISObject *)prdc->prdch1->cookie; CIISObject * pObjectB = (CIISObject *)prdc->prdch2->cookie; *pnResult = pObjectA->Compare(prdc->nColumn, pObjectB); return S_OK; } // // IPersistStream interface members // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< STDMETHODIMP CSnapin::GetClassID( OUT CLSID * pClassID ) /*++ Routine Description: Get class ID Arguments: CLSID * pClassID : Returns class ID Return Value: HRESULT --*/ { ASSERT(pClassID != NULL); // // Copy the CLSID for this snapin // *pClassID = CLSID_Snapin; return E_NOTIMPL; } STDMETHODIMP CSnapin::IsDirty() /*++ Routine Description: Checks to see if the snapin's persistence stream is dirty Arguments: None Return Value: S_OK if the stream needs to be updated, S_FALSE otherwise --*/ { return m_fSettingsChanged ? S_OK : S_FALSE; } STDMETHODIMP CSnapin::Load( IN IStream * pStm ) /*++ Routine Description: Load the persisted information Arguments: IStream * pStm : Persistence stream Return Value: HRESULT --*/ { ASSERT(pStm != NULL); // // Verify a simple signature // DWORD dw = 0x1234; DWORD dw2; DWORD cBytesRead; HRESULT hr = S_OK; do { hr = pStm->Read(&dw2, sizeof(dw2), &cBytesRead); if (FAILED(hr)) { break; } ASSERT(cBytesRead == sizeof(dw2) && dw2 == dw); // // Read settings // hr = pStm->Read( &m_fTaskView, sizeof(m_fTaskView), &cBytesRead ); if (cBytesRead == 0) { // // Old style console file // TRACEEOLID("Warning: old-style console file encountered"); m_fTaskView = FALSE; } else { ASSERT(cBytesRead == sizeof(m_fTaskView)); } } while(FALSE); #if !TASKPADS_SUPPORTED m_fTaskView = FALSE; #endif // TASKPADS_SUPPORTED return hr; } STDMETHODIMP CSnapin::Save( IN IStream * pStm, IN BOOL fClearDirty ) /*++ Routine Description: Save persistence information Arguments: IStream * pStm : Persistence stream BOOL fClearDirty : TRUE to clear the dirty flag Return Value: HRESULT --*/ { ASSERT(pStm != NULL); // // Write a simple signature // DWORD dw = 0x1234; DWORD cBytesWritten; HRESULT hr = STG_E_CANTSAVE; do { hr = pStm->Write(&dw, sizeof(dw), &cBytesWritten); if (FAILED(hr)) { break; } ASSERT(cBytesWritten == sizeof(dw)); hr = pStm->Write(&m_fTaskView, sizeof(m_fTaskView), &cBytesWritten); ASSERT(cBytesWritten == sizeof(m_fTaskView)); } while(FALSE); return hr; } STDMETHODIMP CSnapin::GetSizeMax( ULARGE_INTEGER * pcbSize ) /*++ Routine Description: Get max size of persistence information Arguments: ULARGE_INTEGER * pcbSize : Returns the size Return Value: HRESULT --*/ { ASSERT(pcbSize != NULL); // // Set the size of the string to be saved // pcbSize->QuadPart = (ULONGLONG)sizeof(DWORD) + sizeof(m_fTaskView); return S_OK; } STDMETHODIMP CSnapin::GetHelpTopic(LPOLESTR *lpCompiledHelpFile) { return GetSnapinHelpFile(lpCompiledHelpFile); } // // IComponentData implementation // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< DEBUG_DECLARE_INSTANCE_COUNTER(CComponentDataImpl); CComponentDataImpl::CComponentDataImpl() /*++ Routine Description: Contructor Arguments: None Return Value: N/A --*/ : m_pScope(NULL), m_pConsole(NULL), m_hIISRoot(NULL), m_fIsCacheDirty(FALSE), m_fIsExtension(FALSE), m_strlCachedServers(), m_ullDiscoveryMask((ULONGLONG)0L) { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); DEBUG_INCREMENT_INSTANCE_COUNTER(CComponentDataImpl); EmptyServerList(); #ifdef _DEBUG m_cDataObjects = 0; #endif } CComponentDataImpl::~CComponentDataImpl() /*++ Routine Description: Destructor Arguments: N/A Return Value: N/A --*/ { SAFE_RELEASE(m_pScope); SAFE_RELEASE(m_pConsole); DEBUG_DECREMENT_INSTANCE_COUNTER(CComponentDataImpl); // // Some snap-in is hanging on to data objects. // If they access, it will crash!!! // ASSERT(m_cDataObjects == 0); } STDMETHODIMP CComponentDataImpl::Initialize( IN LPUNKNOWN pUnknown ) /*++ Routine Description: Initalize the component data Arguments: LPUNKNOWN pUnknown : Pointer to IUnknown implementation Return Value: HRESULT --*/ { AFX_MANAGE_STATE(AfxGetStaticModuleState( )); ASSERT(pUnknown != NULL); HRESULT hr; // // MMC should only call ::Initialize once! // ASSERT(m_pScope == NULL); pUnknown->QueryInterface(IID_IConsoleNameSpace, (void **)&m_pScope); CIISObject::AttachScopeView(m_pScope); // // add the images for the scope tree // LPIMAGELIST lpScopeImage; hr = pUnknown->QueryInterface(IID_IConsole2, (void **)&m_pConsole); ASSERT(hr == S_OK); hr = m_pConsole->QueryScopeImageList(&lpScopeImage); ASSERT(hr == S_OK); // // Load the config services DLLs // GetServicesDLL(); // // Get the add-on tools // GetToolMenu(); // // Get the DLLs which contain computer property pages // GetISMMachinePages(); CBitmap bmp16x16, bmp32x32, bmpMgr16x16, bmpMgr32x32; // // Load the bitmaps from the dll // // Must AFX_MANAGE_STATE here // { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); VERIFY(bmp16x16.LoadBitmap(IDB_VIEWS16)); VERIFY(bmp32x32.LoadBitmap(IDB_VIEWS32)); VERIFY(bmpMgr16x16.LoadBitmap(IDB_INETMGR16)); VERIFY(bmpMgr32x32.LoadBitmap(IDB_INETMGR16)); } // // Set the images // hr = lpScopeImage->ImageListSetStrip( (LONG_PTR *)(HBITMAP)bmp16x16, (LONG_PTR *)(HBITMAP)bmp32x32, 0, RGB_BK_IMAGES ); ASSERT(hr == S_OK); // // Add on inetmgr bitmap // hr = lpScopeImage->ImageListSetStrip( (LONG_PTR *)(HBITMAP)bmpMgr16x16, (LONG_PTR *)(HBITMAP)bmpMgr32x32, BMP_INETMGR, RGB_BK_IMAGES ); ASSERT(hr == S_OK); // // Add the ones from the service config DLL's // POSITION pos16x16 = g_obl16x16.GetHeadPosition(); POSITION pos32x32 = g_obl32x32.GetHeadPosition(); int i = BMP_SERVICE; while (pos16x16 && pos32x32) { CImage * pimg16x16 = (CImage *)g_obl16x16.GetNext(pos16x16); CImage * pimg32x32 = (CImage *)g_obl32x32.GetNext(pos32x32); ASSERT(pimg16x16 && pimg32x32); hr = lpScopeImage->ImageListSetStrip( (LONG_PTR *)(HBITMAP)pimg16x16->GetBitmap(), (LONG_PTR *)(HBITMAP)pimg32x32->GetBitmap(), i++, pimg16x16->GetBkColor() ); ASSERT(hr == S_OK); } lpScopeImage->Release(); return hr; } STDMETHODIMP CComponentDataImpl::CreateComponent( IN LPCOMPONENT * ppComponent ) /*++ Routine Description: Create component Arguments: LPCOMPONENT * ppComponent : Address if COMPONENT Return Value: HRESULT --*/ { ASSERT(ppComponent != NULL); CComObject* pObject; CComObject::CreateInstance(&pObject); ASSERT(pObject != NULL); // // Store IComponentData // pObject->SetIComponentData(this); return pObject->QueryInterface(IID_IComponent, (void **)ppComponent); } BOOL CComponentDataImpl::FindOpenPropSheetOnNodeAndDescendants( IN LPPROPERTYSHEETPROVIDER piPropertySheetProvider, IN MMC_COOKIE cookie ) /*++ Routine Description: Starting with the current node, check to see if an open property sheet exists. Keep looking through descendants also. As soon as an open property sheet is found, return TRUE. Arguments: MMC_COOKIE cookie : Cookie for currently selected item Return Value: TRUE if an open property sheet is found, FALSE if not. Notes: Function is called recursively --*/ { HSCOPEITEM hScopeItem = NULL; CIISObject * pObject = (CIISObject *)cookie; if (pObject != NULL) { hScopeItem = pObject->GetScopeHandle(); } // // Check the current node // HRESULT hr = piPropertySheetProvider->FindPropertySheet( (MMC_COOKIE)hScopeItem, NULL, NULL ); if (hr == S_OK) { // // Found a sheet // return TRUE; } // // Now check the descendants for open sheets // ASSERT(m_pScope != NULL); // // Look for machine object off the root // HSCOPEITEM hChildItem; hr = m_pScope->GetChildItem(hScopeItem, &hChildItem, &cookie); while (hr == S_OK && hChildItem != NULL) { CIISObject * pObject = (CIISObject *)cookie; if (FindOpenPropSheetOnNodeAndDescendants( piPropertySheetProvider, cookie )) { // // Found a sheet // return TRUE; } // // Advance to next child of the same parent. // hr = m_pScope->GetNextItem(hChildItem, &hChildItem, &cookie); } // // Not found // return FALSE; } /* INTRINSA suppress=null_pointers, uninitialized */ STDMETHODIMP CComponentDataImpl::Notify( IN LPDATAOBJECT lpDataObject, IN MMC_NOTIFY_TYPE event, IN LPARAM arg, IN LPARAM param ) /*++ Routine Description: Notification message handler Arguments: LPDATAOBJECT lpDataObject : Selected item MMC_NOTIFY_TYPE event : Notification message LPARAM arg : Notification Argument LPARAM param : Notification Parameter Return Value: HRESULT --*/ { if (event == MMCN_PROPERTY_CHANGE) { return OnProperties(param); } ASSERT(m_pScope != NULL); HRESULT hr = S_OK; INTERNAL * pInternal = ExtractInternalFormat(lpDataObject); MMC_COOKIE cookie; if (pInternal == NULL) { TRACEEOLID("Extension (CComponentDataImpl) Snapin"); CIISObject::m_fIsExtension = m_fIsExtension = TRUE; cookie = NULL; } else { cookie = pInternal->m_cookie; FREE_DATA(pInternal); } switch(event) { case MMCN_REFRESH: RefreshIISObject((CIISObject *)cookie, TRUE, arg); break; case MMCN_DELETE: DeleteObject((CIISObject *)cookie); break; case MMCN_RENAME: hr = OnRename(cookie, arg, param); break; case MMCN_REMOVE_CHILDREN: hr = OnRemoveChildren(arg); break; case MMCN_EXPAND: hr = OnExpand(lpDataObject, arg, param); break; case MMCN_SELECT: hr = OnSelect(cookie, arg, param); break; case MMCN_CONTEXTMENU: hr = OnContextMenu(cookie, arg, param); break; default: break; } return hr; } STDMETHODIMP CComponentDataImpl::Destroy() /*++ Routine Description: Destroy component data Arguments: None Return Value: HRESULT --*/ { SAFE_RELEASE_SETTONULL(m_pScope); SAFE_RELEASE_SETTONULL(m_pConsole); m_hIISRoot = NULL; // // Scope items will clean themselves up // return S_OK; } STDMETHODIMP CComponentDataImpl::QueryDataObject( IN MMC_COOKIE cookie, IN DATA_OBJECT_TYPES type, OUT LPDATAOBJECT * ppDataObject ) /*++ Routine Description: Query data object Arguments: MMC_COOKIE cookie : Private data (i.e. a CIISObject *) DATA_OBJECT_TYPES type : Data object type LPDATAOBJECT * ppDataObject : Returns LPDATAOBJECT Return Value: HRESULT --*/ { ASSERT(ppDataObject != NULL); CComObject* pObject; CComObject::CreateInstance(&pObject); ASSERT(pObject != NULL); if (pObject == NULL) return E_UNEXPECTED; // // Save cookie and type for delayed rendering // pObject->SetType(type); pObject->SetCookie(cookie); #ifdef _DEBUG pObject->SetComponentData(this); #endif // // Store the coclass with the data object // pObject->SetClsid(GetCoClassID()); return pObject->QueryInterface(IID_IDataObject, (void **)ppDataObject); } // // IExtendPropertySheet Implementation // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< STDMETHODIMP CComponentDataImpl::CreatePropertyPages( IN LPPROPERTYSHEETCALLBACK lpProvider, IN LONG_PTR handle, IN LPDATAOBJECT lpDataObject ) /*++ Routine Description: Create the property pages for the given object Arguments: LPPROPERTYSHEETCALLBACK lpProvider : Provider LONG_PTR handle : Handle. LPDATAOBJECT lpDataObject : Data object Return Value: HRESULT --*/ { #ifdef MMC_PAGES CIISObject * pObject = NULL; if (lpDataObject != NULL) { INTERNAL * pInternal = ExtractInternalFormat(lpDataObject); if (pInternal) { pObject = (CIISObject *)pInternal->m_cookie; FREE_DATA(pInternal); } } ASSERT(pObject && pObject->IsMMCConfigurable()); CError err(pObject->ConfigureMMC(lpProvider, (LPARAM)pObject, handle)); // // ISSUE: MMC silently fails on this error. Perhaps it should // write the error message? // if (err.Failed()) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); err.MessageBox(); } return err; #else return E_NOTIMPL; #endif // MMC_PAGES } STDMETHODIMP CComponentDataImpl::QueryPagesFor( IN LPDATAOBJECT lpDataObject ) /*++ Routine Description: Check to see if a property sheet should be brought up for this data object Arguments: LPDATAOBJECT lpDataObject : Data object Return Value: S_OK, if properties may be brought up for this item, S_FALSE otherwise --*/ { #ifdef MMC_PAGES CIISObject * pObject = NULL; if (lpDataObject != NULL) { INTERNAL * pInternal = ExtractInternalFormat(lpDataObject); if (pInternal) { pObject = (CIISObject *)pInternal->m_cookie; FREE_DATA(pInternal); } } return pObject && pObject->IsMMCConfigurable() ? S_OK : S_FALSE; #else return S_FALSE; #endif // MMC_PAGES } // // IPersistStream interface members // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< STDMETHODIMP CComponentDataImpl::GetClassID( OUT CLSID * pClassID ) /*++ Routine Description: Return the objects class ID Arguments: CLSID pClassID : Returns the class ID Return Value: HRESULT --*/ { ASSERT(pClassID != NULL); // // Copy the CLSID for this snapin // *pClassID = CLSID_Snapin; return S_OK; } STDMETHODIMP CComponentDataImpl::IsDirty() /*++ Routine Description: Determine if we need to save the cache Arguments: None Return Value: S_OK if the stream needs to be updated, S_FALSE otherwise --*/ { if (m_fIsExtension) { // // Extension to computer management -- no private cache // return S_FALSE; } // // Primary snapin // return IsCacheDirty() ? S_OK : S_FALSE; } STDMETHODIMP CComponentDataImpl::Load( IN IStream * pStm ) /*++ Routine Description: Load the persisted information Arguments: IStream * pStm : Persistence stream Return Value: HRESULT --*/ { ASSERT(pStm); DWORD cch; DWORD cBytesRead; LPTSTR lpstr; if (m_fIsExtension) { // // Extension to computer management -- no private cache // return S_OK; } // // First read the size of the string array // HRESULT hr = pStm->Read(&cch, sizeof(cch), &cBytesRead); if (FAILED(hr)) { return E_FAIL; } // // Virgin cache // if (cBytesRead != sizeof(cch) || cch == 0) { return S_OK; } lpstr = AllocTString(cch); if (lpstr == NULL) { return E_FAIL; } hr = pStm->Read(lpstr, cch * sizeof(TCHAR), &cBytesRead); ASSERT(SUCCEEDED(hr) && cBytesRead == cch * sizeof(TCHAR)); if (FAILED(hr)) { return E_FAIL; } CStringList strl; ConvertDoubleNullListToStringList(lpstr, strl); for (POSITION pos = strl.GetHeadPosition(); pos!= NULL; ) { CString & str = strl.GetNext(pos); AddServerToCache(str, FALSE); } FreeMem(lpstr); return hr; } STDMETHODIMP CComponentDataImpl::Save( IN IStream * pStm, IN BOOL fClearDirty ) /*++ Routine Description: Save persistence information Arguments: IStream * pStm : Persistence stream BOOL fClearDirty : TRUE to clear the dirty flag Return Value: HRESULT --*/ { ASSERT(pStm); if (m_fIsExtension) { // // Extension to computer management -- no private cache // return S_OK; } // // Flatten the cache // DWORD cch; LPTSTR lpstr; DWORD cBytesWritten; HRESULT hr = S_OK; DWORD err = ConvertStringListToDoubleNullList( GetCachedServers(), cch, lpstr ); if (err == ERROR_SUCCESS) { // // First write the total size // hr = pStm->Write(&cch, sizeof(cch), &cBytesWritten); ASSERT(SUCCEEDED(hr) && cBytesWritten == sizeof(cch)); if (FAILED(hr)) { return STG_E_CANTSAVE; } // // Now write the body. // hr = pStm->Write(lpstr, cch * sizeof(TCHAR), &cBytesWritten); ASSERT(SUCCEEDED(hr) && cBytesWritten == cch * sizeof(TCHAR)); if (FAILED(hr)) { return STG_E_CANTSAVE; } if (fClearDirty) { ClearCacheDirty(); } FreeMem(lpstr); } return S_OK; } STDMETHODIMP CComponentDataImpl::GetSizeMax( OUT ULARGE_INTEGER * pcbSize ) /*++ Routine Description: Get max size of persistence information Arguments: ULARGE_INTEGER * pcbSize : Returns the size Return Value: HRESULT --*/ { ASSERT(pcbSize); // // Set the size of the string to be saved // pcbSize->QuadPart = (ULONGLONG)0; return S_OK; } // // IExtendContextMenu implementation // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< STDMETHODIMP CComponentDataImpl::AddMenuItems( IN LPDATAOBJECT lpDataObject, IN LPCONTEXTMENUCALLBACK lpContextMenuCallback, IN long * pInsertionAllowed ) /*++ Routine Description: Add menu items to the right-click context menu Arguments: LPDATAOBJECT pDataObject : Select LPCONTEXTMENUCALLBACK pContextMenuCallback : Context menu callback function long * pInsertionAllowed : ??? Return Value: HRESULT --*/ { if (!(*pInsertionAllowed & CCM_INSERTIONALLOWED_TOP)) { // // Nothing to add to the action menu. // return S_OK; } INTERNAL * pInternal = ExtractInternalFormat(lpDataObject); if (pInternal == NULL) { // // Extensions not supported yet. // ASSERT(FALSE); return S_OK; } CIISObject * pObject = (CIISObject *)pInternal->m_cookie; FREE_DATA(pInternal); if (pObject == NULL) { // // Must be the static root, add connect menu item only. // CIISObject::AddMenuItemByCommand(lpContextMenuCallback, IDM_CONNECT); return S_OK; } return pObject->AddMenuItems(lpContextMenuCallback); } BOOL CComponentDataImpl::DoChangeState( IN CIISObject * pObject, IN int nNewState ) /*++ Routine Description: Change the state of the selected object Arguments: CIISObject * pObject : Selected object int nNewState : Desired new state Return Value: TRUE for success, FALSE for failure. Notes: In case of failure, this method will already have displayed an error message with the cause --*/ { ASSERT(pObject != NULL); ASSERT(pObject->IsControllable()); int nNumRunningChange = pObject->IsRunning() ? -1 : 0; CError err; // // Temporarily override some messages // TEMP_ERROR_OVERRIDE(ERROR_BAD_DEV_TYPE, IDS_CLUSTER_ENABLED); TEMP_ERROR_OVERRIDE(ERROR_INVALID_PARAMETER, IDS_CANT_START_SERVICE); { // // Needed for CWaitCursor // AFX_MANAGE_STATE(::AfxGetStaticModuleState()); CWaitCursor wait; err = pObject->ChangeState(nNewState); } if (err.Failed()) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); err.MessageBox(); } // // Refresh regardless // pObject->RefreshDisplayInfo(); nNumRunningChange += pObject->IsRunning() ? +1 : 0; AddToNumRunning(nNumRunningChange); //UpdateStatusBarNumbers(); return err.Succeeded(); } void CComponentDataImpl::OnMetaBackRest( IN CIISObject * pObject ) /*++ Routine Description: Perform metabase backup/restore Arguments: CIISObject * pObject : Selected object (should be machine node) Return Value: None --*/ { AFX_MANAGE_STATE(AfxGetStaticModuleState()); ASSERT(pObject->QueryGUID() == cMachineNode); CBackupDlg dlg(pObject->GetMachineName()); dlg.DoModal(); if (dlg.HasChangedMetabase()) { RefreshIISObject(pObject, TRUE); } } void CComponentDataImpl::OnIISShutDown( IN CIISObject * pObject ) /*++ Routine Description: Bring up IIS shutdown/restart dialogs Arguments: CIISObject * pObject : Selected object (should be machine node) Return Value: None --*/ { AFX_MANAGE_STATE(AfxGetStaticModuleState()); ASSERT(pObject->QueryGUID() == cMachineNode); LPCTSTR lpszMachineName = pObject->GetMachineName(); // // If a property sheet is open for this item, don't // allow deletion. // LPPROPERTYSHEETPROVIDER piPropertySheetProvider = NULL; HRESULT hr = m_pConsole->QueryInterface( IID_IPropertySheetProvider, (void **)&piPropertySheetProvider ); if (FindOpenPropSheetOnNodeAndDescendants( piPropertySheetProvider, (ULONG_PTR)pObject )) { // // Already had properties open // ::AfxMessageBox(IDS_PROP_OPEN_SHUTDOWN); return; } CIISShutdownDlg dlg(lpszMachineName); dlg.DoModal(); if (dlg.ServicesWereRestarted()) { // // Rebind all metabase handles on this server // CServerInfo * pServerInfo; CObListIter obli(m_oblServers); while (pServerInfo = (CServerInfo *)obli.Next()) { if (!::lstrcmpi(pServerInfo->QueryServerName(), lpszMachineName)) { TRACEEOLID( "Rebinding against " << pServerInfo->GetShortName() << " on " << lpszMachineName ); pServerInfo->ISMRebind(); } } // // Now do a refresh on the computer node. Since we've forced // the rebinding here, we should not get the disconnect warning. // RefreshIISObject(pObject, TRUE); } } void CComponentDataImpl::OnConnectOne() /*++ Routine Description: Connect to a single server Arguments: None Return Value: None --*/ { AFX_MANAGE_STATE(AfxGetStaticModuleState()); TEMP_ERROR_OVERRIDE(EPT_S_NOT_REGISTERED, IDS_ERR_RPC_NA); TEMP_ERROR_OVERRIDE(RPC_S_SERVER_UNAVAILABLE, IDS_ERR_RPC_NA); TEMP_ERROR_OVERRIDE(RPC_S_UNKNOWN_IF, IDS_ERR_INTERFACE); TEMP_ERROR_OVERRIDE(RPC_S_PROCNUM_OUT_OF_RANGE, IDS_ERR_INTERFACE); ConnectServerDlg dlg; if (dlg.DoModal() == IDOK) { // // Clean up name. // CString strServerName(dlg.QueryServerName()); int cServices = 0; // // The function will report the errors // CError err; { CWaitCursor wait; err = AddServerToList( TRUE, // Cache FALSE, // Handle errors strServerName, cServices ); } if (err.Failed()) { err.MessageBoxFormat( IDS_ERROR_CONNECTING, MB_OK, NO_HELP_CONTEXT, (LPCTSTR)strServerName ); } else if (cServices == 0) { // // No errors, but no services found // ::AfxMessageBox(IDS_NO_SERVICE); } } } void CComponentDataImpl::DoConfigure( IN CIISObject * pObject ) /*++ Routine Description: Configure the given iis object Arguments: CIISObject * pObject : Object to be configured Return Value: None --*/ { CError err; // // We need a window handle for this, but MMC won't // give us one. Fortunately, we know MMC to be // an MFC app, so we can sleazily grab it anyway. // CWnd * pMainWnd = NULL; { HWND hwnd; err = m_pConsole->GetMainWindow(&hwnd); if (err.Succeeded()) { pMainWnd = CWnd::FromHandle(hwnd); } } ASSERT(pMainWnd != NULL); CWnd wnd; if (pMainWnd == NULL) { // // No main window in MMC? Use NULL handle // pMainWnd = &wnd; } ASSERT(m_pConsole); ASSERT(pObject->IsConfigurable()); err = pObject->Configure(pMainWnd); AFX_MANAGE_STATE(::AfxGetStaticModuleState()); if (!err.MessageBoxOnFailure()) { // // Refresh and display the entry // OnProperties((LPARAM)pObject); } } BOOL CComponentDataImpl::DeleteObject( IN CIISObject * pObject ) /*++ Routine Description: Delete the given iisobject. Ask for confirmation first. Arguments: CIISObject * pObject : Object to be deleted Return Value: TRUE if the item was deleted successfully, FALSE otherwise. --*/ { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); BOOL fReturn = FALSE; // // If a property sheet is open for this item, don't // allow deletion. // LPPROPERTYSHEETPROVIDER piPropertySheetProvider = NULL; HRESULT hr = m_pConsole->QueryInterface( IID_IPropertySheetProvider, (void **)&piPropertySheetProvider ); if (FindOpenPropSheetOnNodeAndDescendants( piPropertySheetProvider, (ULONG_PTR)pObject )) { // // Already had properties open // ::AfxMessageBox(IDS_PROP_OPEN); } else { CError err; // Before we go and delete this baby, // let's check if it's got a Cluster property set. if (pObject->IsClusterEnabled()) { ::AfxMessageBox(IDS_CLUSTER_ENABLED_2); } else { if (!pObject->HandleUI() || NoYesMessageBox(IDS_CONFIRM_DELETE)) { CWaitCursor wait; err = pObject->Delete(); if (pObject->HandleUI()) { // // Only complain if we're to handle the error messages. // In e.g. the file system, explorer handles all error // messages // err.MessageBoxOnFailure(); } if (err.Succeeded()) { ASSERT(m_pScope); // // Delete the item from the view, but be careful that // result item nodes store a scope handle, but which // actually refers to the _parent_'s scope handle. // if (!pObject->ScopeHandleIsParent()) { m_pScope->DeleteItem(pObject->GetScopeHandle(), TRUE); delete pObject; } fReturn = TRUE; } } } } piPropertySheetProvider->Release(); return fReturn; } void CComponentDataImpl::DisconnectItem( IN CIISObject * pObject ) /*++ Routine Description: Delete the given iisobject. Ask for confirmation first. Arguments: CIISObject * pObject : Object to be deleted Return Value: None --*/ { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); // // If a property sheet is open for this item, don't // allow deletion. // LPPROPERTYSHEETPROVIDER piPropertySheetProvider = NULL; HRESULT hr = m_pConsole->QueryInterface( IID_IPropertySheetProvider, (void **)&piPropertySheetProvider ); if (FindOpenPropSheetOnNodeAndDescendants( piPropertySheetProvider, (ULONG_PTR)pObject )) { // // Already had properties open // ::AfxMessageBox(IDS_PROP_OPEN); } else { CString strMachine(pObject->GetMachineName()); CString str, str2; VERIFY(str.LoadString(IDS_CONFIRM_DISCONNECT)); str2.Format(str, strMachine); if (NoYesMessageBox(str2)) { // // Remove from Cache and oblist // CError err(RemoveServerFromList(TRUE, strMachine)); if (!err.MessageBoxOnFailure()) { ASSERT(m_pScope); m_pScope->DeleteItem(pObject->GetScopeHandle(), TRUE); delete pObject; } } } piPropertySheetProvider->Release(); } STDMETHODIMP CComponentDataImpl::Command( IN long nCommandID, IN LPDATAOBJECT lpDataObject ) /*++ Routine Description: Command handler Arguments: long nCommandID : Command ID LPDATAOBJECT lpDataObject : Selected Data object Return Value: HRESULT --*/ { if (nCommandID == IDM_CONNECT) { // // This is the only case that doesn't require a selected // data object. // OnConnectOne(); return S_OK; } /* if (IsMMCMultiSelectDataObject(lpDataObject)) { // // Do something for multi-select? // TRACEEOLID("Multiple selection"); } */ INTERNAL * pInternal = lpDataObject ? ExtractInternalFormat(lpDataObject) : NULL; if (pInternal == NULL) { return S_OK; } CIISObject * pObject = (CIISObject *)pInternal->m_cookie; FREE_DATA(pInternal); CError err; switch (nCommandID) { case IDM_METABACKREST: OnMetaBackRest(pObject); break; case IDM_SHUTDOWN: OnIISShutDown(pObject); break; case IDM_DISCONNECT: DisconnectItem(pObject); break; case IDM_CONFIGURE: DoConfigure(pObject); break; case IDM_STOP: DoChangeState(pObject, INetServiceStopped); m_pConsole->UpdateAllViews(lpDataObject, 0L, (LONG_PTR)pObject); break; case IDM_START: DoChangeState(pObject, INetServiceRunning); m_pConsole->UpdateAllViews(lpDataObject, 0L, (LONG_PTR)pObject); break; case IDM_PAUSE: DoChangeState( pObject, pObject->IsPaused() ? INetServiceRunning : INetServicePaused ); m_pConsole->UpdateAllViews(lpDataObject, 0L, (LONG_PTR)pObject); break; case IDM_EXPLORE: pObject->Explore(); break; case IDM_OPEN: pObject->Open(); break; case IDM_BROWSE: pObject->Browse(); break; case IDM_TASK_SECURITY_WIZARD: { // // Launch the security wizard // err = pObject->SecurityWizard(); err.MessageBoxOnFailure(); } break; case IDM_NEW_INSTANCE: { // // Executed from another instance of the same type // ISMINSTANCEINFO ii; CServerInfo * pServerInfo = pObject->GetServerInfo(); ASSERT(pServerInfo); err = pServerInfo->AddInstance(&ii, sizeof(ii)); if (err.Succeeded()) { CIISInstance * pInstance = new CIISInstance(&ii, pServerInfo); // // Add the new instance grouped with the service type, // and select it // BOOL fNext; HSCOPEITEM hParent = FindServerInfoParent( GetRootHandle(), pServerInfo ); ASSERT(hParent != NULL); HSCOPEITEM hSibling = FindNextInstanceSibling( hParent, pInstance, &fNext ); HSCOPEITEM hItem = AddIISObject( hParent, pInstance, hSibling, fNext ); // // hItem could return NULL if the hParent // was not yet expanded. // if (hItem) { m_pConsole->SelectScopeItem(hItem); } } } break; case IDM_NEW_VROOT: CIISChildNode * pChild; err = pObject->AddChildNode(pChild); if (err.Succeeded()) { // // Insert prior to file/dir nodes, and select the item // HSCOPEITEM hItem = AddIISObject( pObject->GetScopeHandle(), pChild, FindNextVDirSibling(pObject->GetScopeHandle(), pChild) ); // // hItem could return NULL if the parent object // was not yet expanded. // if (hItem) { m_pConsole->SelectScopeItem(hItem); } } break; default: // // Now try to get to the service that we're // supposed to create a new instance for. // if (nCommandID >= IDM_NEW_EX_INSTANCE) { int nID = nCommandID - IDM_NEW_EX_INSTANCE; CNewInstanceCmd * pcmd = (CNewInstanceCmd *)m_oblNewInstanceCmds.Index(nID); ASSERT(pcmd != NULL); if (pcmd) { ISMINSTANCEINFO ii; CServerInfo * pServerInfo = FindServerInfo( pObject->GetMachineName(), pcmd->GetServiceInfo() ); if (pServerInfo == NULL) { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); ::AfxMessageBox(IDS_ERR_SERVICE_NOT_INSTALLED); break; } ASSERT(pServerInfo); err = pServerInfo->AddInstance(&ii, sizeof(ii)); if (err.Succeeded()) { // // Add and select the item // CIISInstance * pInstance = new CIISInstance( &ii, pServerInfo ); BOOL fNext; HSCOPEITEM hSibling = FindNextInstanceSibling( pObject->GetScopeHandle(), pInstance, &fNext ); HSCOPEITEM hItem = AddIISObject( pObject->GetScopeHandle(), pInstance, hSibling, fNext ); m_pConsole->SelectScopeItem(hItem); } } } else { // // Unknown command! // ASSERT(FALSE); } break; } return S_OK; } // // Notification handlers for IComponentData // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< HRESULT CComponentDataImpl::OnAdd( IN MMC_COOKIE cookie, IN LPARAM arg, IN LPARAM param ) /*++ Routine Description: Add handler Arguments: MMC_COOKIE cookie : Scope item cookie (CIISObject *) LPARAM arg : Argument LPARAM param : Parameter Return Value: HRESULT --*/ { return E_UNEXPECTED; } HRESULT CComponentDataImpl::OnDelete( IN MMC_COOKIE cookie ) /*++ Routine Description: Handle deletion of the given scope item (CIISObject *) Arguments: MMC_COOKIE cookie : Casts to a CIISObject * Return Value: HRESULT --*/ { CIISObject * pObject = (CIISObject *)cookie; if (pObject == NULL) { return S_FALSE; } delete pObject; return S_OK; } HRESULT CComponentDataImpl::OnRename( IN MMC_COOKIE cookie, IN LPARAM arg, IN LPARAM param ) /*++ Routine Description: Rename notification handler Arguments: MMC_COOKIE cookie : Currently selected cookie (CIISObject *) LPARAM arg : Notification argument LPARAM param : Notification parameter Return Value: HRESULT --*/ { CIISObject * pObject = (CIISObject *)cookie; if (pObject == NULL) { // // Can't rename this one // return S_FALSE; } if (!arg) { // // Check to see if we're renamable // return pObject->IsRenamable() ? S_OK : S_FALSE; } TEMP_ERROR_OVERRIDE(ERROR_ALREADY_EXISTS, IDS_ERR_DUP_VROOT); CError err; // // Do an actual rename // LPCTSTR lpstrNewName = (LPCTSTR)param; err = pObject->Rename(lpstrNewName); if (err.Succeeded()) { // // Force a refresh on the children // pObject->DirtyChildren(); } else { if (pObject->HandleUI()) { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); err.MessageBox(); } } return err.Succeeded() ? S_OK : S_FALSE; } HRESULT CComponentDataImpl::OnRemoveChildren( IN LPARAM arg ) /*++ Routine Description: 'Remove Children' notification handler Arguments: LPARAM arg : Notification argument Return Value: HRESULT --*/ { m_strlCachedServers.RemoveAll(); return S_OK; } HRESULT CComponentDataImpl::OnExpand( LPDATAOBJECT lpDataObject, LPARAM arg, LPARAM param ) /*++ Routine Description: Expand notification handler Arguments: LPDATAOBJECT lpDataObject : Currently selected cookie (CIISObject *) LPARAM arg : Notification argument LPARAM param : Notification parameter Return Value: HRESULT --*/ { if (arg) { // // Did Initialize get called? // ASSERT(m_pScope != NULL); EnumerateScopePane(lpDataObject, param); } return S_OK; } HRESULT CComponentDataImpl::OnSelect( IN MMC_COOKIE cookie, IN LPARAM arg, IN LPARAM param ) /*++ Routine Description: Selection notification handler Arguments: MMC_COOKIE cookie : Currently selected cookie (CIISObject *) LPARAM arg : Notification argument LPARAM param : Notification parameter Return Value: HRESULT --*/ { return E_UNEXPECTED; } HRESULT CComponentDataImpl::OnContextMenu( IN MMC_COOKIE cookie, IN LPARAM arg, IN LPARAM param ) /*++ Routine Description: Context Menu notification handler Arguments: MMC_COOKIE cookie : Currently selected cookie (CIISObject *) LPARAM arg : Notification argument LPARAM param : Notification parameter Return Value: HRESULT --*/ { return S_OK; } HRESULT CComponentDataImpl::OnProperties( IN LPARAM param ) /*++ Routine Description: Properties change notification handler Arguments: LPARAM arg : Notification argument (CIISObject *) Return Value: HRESULT --*/ { if (param == NULL) { return S_OK; } CIISObject * pObject = (CIISObject *)param; ASSERT(pObject != NULL); if (pObject != NULL) { CString strOldPath(pObject->QueryPhysicalPath()); CString strOldRedirect(pObject->QueryRedirPath()); BOOL fOldChildRedirOnly = pObject->IsChildOnlyRedir(); // // Refresh the data to see if either the physical or redirection // path has changed. If it has, refresh the child objects // CError err(pObject->RefreshData()); // // Determine if the file system needs to be refreshed // BOOL fRefreshFileSystem = err.Succeeded() && pObject->SupportsChildren() && pObject->ChildrenExpanded() && pObject->SupportsFileSystem() && strOldPath.CompareNoCase(pObject->QueryPhysicalPath()) != 0; // // Determine if everything needs to be refreshed // BOOL fFullRefresh = err.Succeeded() && pObject->SupportsChildren() && pObject->ChildrenExpanded() && (strOldRedirect.CompareNoCase(pObject->QueryRedirPath()) != 0 || fOldChildRedirOnly != pObject->IsChildOnlyRedir()); TRACEEOLID("Refresh files: " << fRefreshFileSystem << " Full Refresh: " << fFullRefresh ); RefreshIISObject(pObject, fFullRefresh); if (!fFullRefresh && fRefreshFileSystem) { // // Not a full refresh -- the file system only. // CString strPhysicalPath, strMetaRoot; pObject->BuildPhysicalPath(strPhysicalPath); pObject->BuildFullPath(strMetaRoot, FALSE); // // Note: we can't use ExpandIISObject, because we're only // replacing specific nodes, not the entire subtree // AddFileSystem( pObject->GetScopeHandle(), strPhysicalPath, strMetaRoot, pObject->FindOwnerInstance(), GET_DIRECTORIES, DELETE_CURRENT_DIR_TREE ); // // Mark this node to indicate that the child nodes // have been added. // pObject->CleanChildren(); } // // Re-enumerate the result side if the current item is // selected. // if ((fRefreshFileSystem || fFullRefresh) && pObject->IsScopeSelected()) { ASSERT(m_pConsole); m_pConsole->SelectScopeItem(pObject->GetScopeHandle()); } } return S_OK; } CServerInfo * CComponentDataImpl::FindServerInfo( IN LPCTSTR lpstrMachine, IN CServiceInfo * pServiceInfo ) /*++ Routine Description: Find specific server info (i.e. machine name / service info combination type) Arguments: LPCTSTR lpstrMachine : Machine name CServiceInfo * pServiceInfo : Service info Return Value: Server Info object pointer, or NULL --*/ { CServerInfo * pServerInfo; CObListIter obli(m_oblServers); // // Search is sequential, because we don't foresee more then a few // of these objects in the cache // while (pServerInfo = (CServerInfo *)obli.Next()) { if (!::lstrcmpi(pServerInfo->QueryServerName(), lpstrMachine) && pServerInfo->GetServiceInfo() == pServiceInfo) { // // Found it // return pServerInfo; } } return NULL; } void CComponentDataImpl::RefreshIISObject( IN CIISObject * pObject, IN BOOL fExpandTree, IN HSCOPEITEM pParent ) /*++ Routine Description: Refresh object, and optionally re-enumerate its display Arguments: CIISObject * pObject : Object to be refreshed BOOL fExpandTree : TRUE to expand its tree Return Value: None --*/ { ASSERT(pObject != NULL); CError err; CServerInfo * pServer = pObject->GetServerInfo(); CMetaInterface * pInterface = NULL; if (pServer) { pInterface = GetMetaKeyFromHandle(pServer->GetHandle()); } if (pInterface) { BEGIN_ASSURE_BINDING_SECTION err = pObject->RefreshData(); END_ASSURE_BINDING_SECTION(err, pInterface, RPC_S_SERVER_UNAVAILABLE); } else { err = pObject->RefreshData(); } if (err.Failed()) { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); err.MessageBox(); return; } pObject->RefreshDisplayInfo(); // // Reenumerate its children if requested to do so and its necessary // if (fExpandTree) { if (pObject->ChildrenExpanded()) { HSCOPEITEM hNode = pObject->GetScopeHandle(); if (KillChildren( hNode, IDS_PROP_OPEN_REFRESH, DELETE_EVERYTHING, DONT_CONTINUE_ON_OPEN_SHEET )) { pObject->DirtyChildren(); ExpandIISObject(hNode, pObject); } } // // Re-enumerate the result side, if the current item is // selected. // if (pObject->IsScopeSelected()) { ASSERT(m_pConsole); m_pConsole->SelectScopeItem(pObject->GetScopeHandle()); } } } void CComponentDataImpl::LoadDynamicExtensions( IN HSCOPEITEM hParent, IN CIISObject * pObject ) /*++ Routine Description: Load dynamic snap-in extensions for the given node type. Arguments: None Return Value: None (if it fails, it always fails silently) --*/ { // // We'll look here (on the server) to see if we need to load // extension snap-ins. // const TCHAR SERVICES_KEY[] = SZ_REMOTEIISEXT; if (pObject == NULL) { // // This should never happen, right? // ASSERT(FALSE); return; } CString str, strKey; // // Get base path for the given node type // strKey.Format( _T("%s\\%s"), SERVICES_KEY, GUIDToCString(pObject->QueryGUID(), str) ); TRACEEOLID( pObject->GetMachineName() << " Attempting to dynamically load extensions for " << strKey); CError err; CRMCRegKey rkExtensions(REG_KEY, strKey, KEY_READ, pObject->GetMachineName()); CComQIPtr pIConsoleNameSpace2 = m_pConsole; if (pIConsoleNameSpace2 && rkExtensions.Ok()) { DWORD dwType; GUID guidExtension; CRMCRegValueIter rkValues(rkExtensions); // // Loop through, and attempt to add each extension found in the // registry // while (rkValues.Next(&str, &dwType) == ERROR_SUCCESS) { TRACEEOLID("Found dynamic extension: " << str); err = ::CLSIDFromString((LPTSTR)(LPCTSTR)str, &guidExtension); if (err.Succeeded()) { err = pIConsoleNameSpace2->AddExtension(hParent, &guidExtension); } TRACEEOLID("DynaLoad returned " << err); } } } void CComponentDataImpl::ExpandIISObject( IN HSCOPEITEM hParent, IN CIISObject * pObject, IN LPTSTR lpszMachineName ) /*++ Routine Description: Expand the given IIS object tree Arguments: HSCOPEITEM hParent : Handle to parent item CIISObject * pObject : IISObject to expand Return Value: None --*/ { CError err; if (pObject != NULL) { ASSERT(hParent == pObject->GetScopeHandle()); } // // make sure we QI'ed for the interface // ASSERT(m_pScope != NULL); AFX_MANAGE_STATE(::AfxGetStaticModuleState()); { CWaitCursor wait; if (m_hIISRoot == NULL) { // // Store the IIS root node for future use // m_hIISRoot = hParent; } if (pObject == NULL) { // // Static root node -- populate with computers. // // This is done only once per session // ASSERT(GetRootHandle() == hParent); TRACEEOLID("Inserting static root node"); AddCachedServersToView(); CServerInfo * pServerInfo = NULL; CObListIter obli(m_oblServers); if (m_fIsExtension) { // // Since we're extending the computer management // snap-in, only add the one server info parent // object (only one computer, after all). // This is also necessary, because for some reason // I can't get the child item nodes of my parent, // even if I've added them myself, and so we'd get // duplicates. // if (lpszMachineName != NULL && *lpszMachineName != 0) { CServerInfo * p; while (p = (CServerInfo *)obli.Next()) { if (!::lstrcmpi(p->QueryServerName(), lpszMachineName)) { pServerInfo = p; break; } } } else pServerInfo = (CServerInfo *)obli.Next(); ASSERT(pServerInfo != NULL); if (pServerInfo) { AddServerInfoParent(hParent, pServerInfo); } } else { // // We're the primary snap-in, add all the server info // parent objects (computers) to the view // while (pServerInfo = (CServerInfo *)obli.Next()) { if (pServerInfo->IsServiceSelected()) { // // Add each item in the tree // AddServerInfoParent(hParent, pServerInfo); } } } } else { if (!pObject->ChildrenExpanded()) { // // Delete whatever children there may be // if (pObject->QueryGUID() == cMachineNode) { CIISMachine * pMachine = (CIISMachine *)pObject; CServerInfo * pServerInfo; CObListIter obli(m_oblServers); while (pServerInfo = (CServerInfo *)obli.Next()) { if (pServerInfo->MatchServerName(pMachine->GetMachineName()) && pServerInfo->IsServiceSelected()) { // // Add each item in the tree // AddServerInfo(hParent, pServerInfo, FALSE); } } } CString strMetaPath; if (pObject->SupportsChildren()) { pObject->BuildFullPath(strMetaPath, FALSE); // // Expand the children off the root // AddVirtualRoots( hParent, strMetaPath, pObject->FindOwnerInstance() ); } if (pObject->SupportsFileSystem()) { if (strMetaPath.IsEmpty()) { pObject->BuildFullPath(strMetaPath, FALSE); } // // Expand file system objects // CString strPhysicalPath; pObject->BuildPhysicalPath(strPhysicalPath); AddFileSystem( hParent, strPhysicalPath, strMetaPath, pObject->FindOwnerInstance(), GET_DIRECTORIES, DONT_DELETE_CURRENT_DIR_TREE ); } // // Now load the dynamic extensions // LoadDynamicExtensions(hParent, pObject); // // Mark this node to indicate that the child nodes // have been added. // pObject->CleanChildren(); } } } err.MessageBoxOnFailure(); } void CComponentDataImpl::EnumerateScopePane( IN LPDATAOBJECT lpDataObject, IN HSCOPEITEM hParent ) /*++ Routine Description: Handle expansion of hParent scope item. Arguments: LPDATAOBJECT lpDataObject : Selected data object HSCOPEITEM hParent : Scope handle of parent item or NULL Return Value: None --*/ { ASSERT(lpDataObject != NULL); INTERNAL * pInternal = ExtractInternalFormat(lpDataObject); MMC_COOKIE cookie = 0L; LPTSTR lpszMachine = NULL; if (pInternal == NULL) { // // Not mine -- must be an extension; Get the machine name // ASSERT(m_fIsExtension); lpszMachine = ExtractMachineName(lpDataObject); TRACEEOLID(lpszMachine); CString strServerName = PURE_COMPUTER_NAME(lpszMachine); if (strServerName.IsEmpty()) { // // MyComputer reports "" for the computer name. // This means the local machine is indicated // DWORD dwSize = MAX_SERVERNAME_LEN; if (::GetComputerName(strServerName.GetBuffer(dwSize + 1), &dwSize)) { strServerName.ReleaseBuffer(); } } // // Since we're an extension, the cache will not be // loaded from the persistence stream, and we can // therefore guarantee that this will be the only // item in the cache. // AddServerToCache(strServerName, FALSE); } else { cookie = pInternal->m_cookie; FREE_DATA(pInternal); } #ifdef _DEBUG CIISObject * pObject = (CIISObject *)cookie; if (pObject) { ASSERT(hParent == pObject->GetScopeHandle()); } #endif // _DEBUG ExpandIISObject(hParent, (CIISObject *)cookie, lpszMachine); } STDMETHODIMP CComponentDataImpl::GetDisplayInfo( IN LPSCOPEDATAITEM lpScopeDataItem ) /*++ Routine Description: Get display info (text, bitmap) for the selected scope item Arguments: LPSCOPEDATAITEM lpScopeDataItem : Selected item Return Value: HRESULT --*/ { ASSERT(lpScopeDataItem != NULL); if (lpScopeDataItem == NULL) { return E_POINTER; } static CString strText; CIISObject * pObject = (CIISObject *)lpScopeDataItem->lParam; ASSERT(lpScopeDataItem->mask & SDI_STR); pObject->GetDisplayText(strText); lpScopeDataItem->displayname = (LPTSTR)(LPCTSTR)strText; ASSERT(lpScopeDataItem->displayname != NULL); return S_OK; } STDMETHODIMP CComponentDataImpl::CompareObjects( IN LPDATAOBJECT lpDataObjectA, IN LPDATAOBJECT lpDataObjectB ) /*++ Routine Description: Compare two data objects. This is used by MMC to determine whether a given node has a property sheet already open for it. Arguments: LPDATAOBJECT lpDataObjectA : Data object 1 LPDATAOBJECT lpDataObjectB : Data object 2 Return Value: S_OK if the objects are identical, S_FALSE otherwise --*/ { if (lpDataObjectA == NULL || lpDataObjectB == NULL) { return E_POINTER; } // // Make sure both data object are mine // INTERNAL * pA; INTERNAL * pB; HRESULT hr = S_FALSE; pA = ExtractInternalFormat(lpDataObjectA); pB = ExtractInternalFormat(lpDataObjectA); if (pA != NULL && pB != NULL) { //hr = (*pA == *pB) ? S_OK : S_FALSE; return S_OK; } FREE_DATA(pA); FREE_DATA(pB); return hr; } void CComponentDataImpl::GetISMMachinePages() /*++ Routine Description: Load the names of the DLL providing ISM machine property page extentions Arguments: Return Value: --*/ { /* OBSOLETE OBSOLETE OBSOLETE OBSOLETE OBSOLETE CString strValueName; DWORD dwValueType; CRMCRegKey rkMachine(REG_KEY, SZ_ADDONMACHINEPAGES, KEY_READ); if (rkMachine.Ok()) { return; } CRMCRegValueIter rvi(rkMachine); CIISMachine::AttachPages(&m_oblISMMachinePages); try { while (rvi.Next(&strValueName, &dwValueType) == ERROR_SUCCESS) { CString strValue; rkMachine.QueryValue(strValueName, strValue); TRACEEOLID("Registering machine pages in " << strValue); m_oblISMMachinePages.AddTail(new CISMMachinePageExt(strValue)); } } catch(CException * e) { TRACEEOLID("!!!exception building ISM machine page list"); e->ReportError(); e->Delete(); } */ } void CComponentDataImpl::ConvertBitmapFormats( IN CBitmap & bmpSource, OUT CBitmap & bmp16x16, OUT CBitmap & bmp32x32 ) /*++ Routine Description: Convert a ISM service config bitmap to 16x16 and 32x32 exactly. Downlevel services only included a single sized bitmap (usually 16x16) for use in ISM. We need a small one and a large one, so we expand as needed. Arguments: IN CBitmap & bmpSource : Source bitmap OUT CBitmap & bmp16x16 : 16x16 output bitmap OUT CBitmap & bmp32x32 : 32x32 output bitmap Return Value: None --*/ { CDC dcImage; CDC dc16x16; CDC dc32x32; VERIFY(dcImage.CreateCompatibleDC(NULL)); VERIFY(dc16x16.CreateCompatibleDC(NULL)); VERIFY(dc32x32.CreateCompatibleDC(NULL)); CBitmap * pOld = dcImage.SelectObject(&bmpSource); BITMAP bm; VERIFY(bmpSource.GetObject(sizeof(bm), &bm)); VERIFY(bmp16x16.CreateBitmap(16, 16, bm.bmPlanes, bm.bmBitsPixel, NULL)); VERIFY(bmp32x32.CreateBitmap(32, 32, bm.bmPlanes, bm.bmBitsPixel, NULL)); CBitmap * pOld16x16 = dc16x16.SelectObject(&bmp16x16); CBitmap * pOld32x32 = dc32x32.SelectObject(&bmp32x32); dc16x16.StretchBlt(0, 0, 16, 16, &dcImage, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY); dc32x32.StretchBlt(0, 0, 32, 32, &dcImage, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY); dc16x16.SelectObject(pOld16x16); dc32x32.SelectObject(pOld32x32); dcImage.SelectObject(pOld); } BOOL CComponentDataImpl::VerifyBitmapSize( IN HBITMAP hBitmap, IN LONG nHeight, IN LONG nWidth ) /*++ Routine Description: Verify the given bitmap is of the right size Arguments: HBITMAP hBitmap : Bitmap handle LONG nHeight : Height the bitmap should be LONG nWidth : Width the bitmap should be Return Value: TRUE if the bitmap is of the right size, FALSE otherwise --*/ { BITMAP bm; int cb = GetObject(hBitmap, sizeof(BITMAP), &bm); return (cb == sizeof(BITMAP) && bm.bmWidth == nWidth && bm.bmHeight == nHeight ); } BOOL CComponentDataImpl::GetBitmapParms( IN CServiceInfo * pServiceInfo, IN BMP_TYPES bmpt, OUT CBitmap *& pbmp16x16, OUT CBitmap *& pbmp32x32, OUT COLORREF & rgbMask ) /*++ Routine Description: Get bitmap information from service info object Arguments: CServiceInfo * pServiceInfo : Service info BMP_TYPES bmpt : Type of info requested COLORREF & rgbMask : Returns background mask Return Value: None. --*/ { ASSERT(pServiceInfo != NULL); UINT nID16x16 = 0; UINT nID32x32 = 0; HINSTANCE hMod = NULL; if (pServiceInfo->InitializedOK()) { switch(bmpt) { case BMT_BUTTON: nID16x16 = pServiceInfo->QueryButtonBitmapID(); nID32x32 = 0; rgbMask = pServiceInfo->QueryButtonBkMask(); break; case BMT_SERVICE: nID16x16 = pServiceInfo->QueryServiceBitmapID(); nID32x32 = pServiceInfo->QueryLargeServiceBitmapID(); rgbMask = pServiceInfo->QueryServiceBkMask(); break; case BMT_VROOT: ASSERT(pServiceInfo->SupportsChildren()); nID16x16 = pServiceInfo->QueryChildBitmapID(); nID32x32 = pServiceInfo->QueryLargeChildBitmapID(); rgbMask = pServiceInfo->QueryChildBkMask(); break; } if (nID16x16 != 0) { hMod = pServiceInfo->QueryInstanceHandle(); } else { // // No bitmap provided by the service DLL, provide one from our // own resource segment. // nID16x16 = IDB_UNKNOWN; nID32x32 = 0; hMod = ::AfxGetResourceHandle(); rgbMask = TB_COLORMASK; } } else { // // Add a disabled dummy button for a service // that didn't load. // nID16x16 = IDB_NOTLOADED; nID32x32 = 0; hMod = ::AfxGetResourceHandle(); rgbMask = TB_COLORMASK; } if (nID16x16 == 0) { return FALSE; } pbmp16x16 = new CBitmap; pbmp32x32 = new CBitmap; if (pbmp16x16 == NULL || pbmp32x32 == NULL) { return FALSE; } HINSTANCE hOld = ::AfxGetResourceHandle(); ::AfxSetResourceHandle(hMod); if (nID32x32 != 0) { // // Have explicit large and small images // VERIFY(pbmp16x16->LoadBitmap(nID16x16)); VERIFY(pbmp32x32->LoadBitmap(nID32x32)); // // Check to make sure they're the right size // if (!VerifyBitmapSize((HBITMAP)*pbmp16x16, 16, 16) || !VerifyBitmapSize((HBITMAP)*pbmp32x32, 32, 32)) { ASSERT(FALSE); TRACEEOLID("Bogus bitmap size provided by service bitmap"); // // Synthesize based on small image. // delete pbmp32x32; CBitmap * pTmp = pbmp16x16; ConvertBitmapFormats(*pTmp, *pbmp16x16, *pbmp32x32); delete pTmp; } } else { // // Only have one image. Synthesize small and large from this // image. // CBitmap bmp; VERIFY(bmp.LoadBitmap(nID16x16)); // // Convert to 16x16 and 32x32 image // ConvertBitmapFormats(bmp, *pbmp16x16, *pbmp32x32); } ::AfxSetResourceHandle(hOld); return TRUE; } void CComponentDataImpl::GetToolMenu() /*++ Routine Description: Load the add-on tools Arguments: None. Return Value: None. --*/ { CString strValueName; DWORD dwValueType; int cTools = 0; CRMCRegKey rkMachine(REG_KEY, SZ_ADDONTOOLS, KEY_READ); if (!rkMachine.Ok()) { // // No registry entry // return; } CRMCRegValueIter rvi(rkMachine); static BOOL fInitialised = FALSE; if (fInitialised) { TRACEEOLID("Toolbar already initialised"); return; } try { int nButton = IDM_TOOLBAR; while (rvi.Next(&strValueName, &dwValueType) == ERROR_SUCCESS) { CString strValue; BOOL fExpanded; rkMachine.QueryValue( strValueName, strValue, EXPANSION_ON, &fExpanded ); TRACEEOLID("Adding tool: " << strValueName); TRACEEOLID("From Path: " << strValue); TRACEEOLID("Expansion: " << fExpanded); CISMShellExecutable * pNewAddOnTool = new CISMShellExecutable( strValue, nButton - 1, nButton ); if (!pNewAddOnTool->HasBitmap()) { TRACEEOLID("Tossing useless toolbar item"); delete pNewAddOnTool; continue; } g_oblAddOnTools.AddTail(pNewAddOnTool); ++nButton; } } catch(CException * e) { TRACEEOLID("!!!exception building tool menu"); e->ReportError(); e->Delete(); } fInitialised = TRUE; } void CComponentDataImpl::MatchupSuperDLLs() /*++ Routine Description: Match up all dlls with superceed dlls Arguments: None Return Value: None --*/ { POSITION pos = m_oblServices.GetHeadPosition(); while(pos) { CServiceInfo * pService = (CServiceInfo *)m_oblServices.GetNext(pos); ASSERT(pService != NULL); if (pService->RequiresSuperDll()) { // // Match up the super DLL // POSITION pos2 = m_oblServices.GetHeadPosition(); while (pos2) { CServiceInfo * pService2 = (CServiceInfo *)m_oblServices.GetNext(pos2); if (pService2->IsSuperDllFor(pService)) { pService->AssignSuperDll(pService2); break; } } ASSERT(pService->HasSuperDll()); } } } void CComponentDataImpl::GetServicesDLL() /*++ Routine Description: Load the add-on services. Arguments: None Return Value: None --*/ { int cServices = 0; HRESULT hr = S_OK; LPIMAGELIST lpScopeImage = NULL; LPIMAGELIST lpResultImage = NULL; ASSERT(m_pConsole); hr = m_pConsole->QueryScopeImageList(&lpScopeImage); ASSERT(SUCCEEDED(hr)); hr = m_pConsole->QueryResultImageList(&lpResultImage); ASSERT(SUCCEEDED(hr)); // // Run through the list of installed services, // load its associated cfg dll, and build up // a discovery mask for each service. // CString strValueName; DWORD dwValueType; CRMCRegKey rkMachine(REG_KEY, SZ_ADDONSERVICES, KEY_READ); if (rkMachine.Ok()) { CRMCRegValueIter rvi(rkMachine); CIISMachine::AttachNewInstanceCmds(&m_oblNewInstanceCmds); try { // // Now load the services // // AFX_MANAGE_STATE required to load service bitmaps // from the dlls // AFX_MANAGE_STATE(::AfxGetStaticModuleState()); int nBmpIndex = BMP_SERVICE; while (rvi.Next(&strValueName, &dwValueType) == ERROR_SUCCESS) { // // Expand environment variables in path if present. // CString strValue; BOOL fExpanded; rkMachine.QueryValue( strValueName, strValue, EXPANSION_ON, &fExpanded ); CServiceInfo * pServiceInfo = NULL; { CWaitCursor wait; TRACEEOLID("Adding service DLL: " << strValue); pServiceInfo = new CServiceInfo(cServices, strValue); } CError err(pServiceInfo->QueryReturnCode()); if (err.Failed()) { if (err.Win32Error() == ERROR_INVALID_PARAMETER) { // // The ERROR_INVALID_PARAMETER error return code // gets sent when the info buffer provided is too // small for the configuration DLL // ::AfxMessageBox( IDS_VERSION_INCOMPATIBLE, MB_OK | MB_ICONEXCLAMATION ); } else { err.MessageBoxFormat( IDS_ERR_NO_LOAD, MB_OK | MB_ICONEXCLAMATION, NO_HELP_CONTEXT, (LPCTSTR)pServiceInfo->QueryDllName() ); } // // Don't add it to the list // delete pServiceInfo; continue; } AddServiceToList(pServiceInfo); // // If this service use inetsloc discovery, // add it to the mask. // if (pServiceInfo->UseInetSlocDiscover()) { m_ullDiscoveryMask |= pServiceInfo->QueryDiscoveryMask(); } // // Add a bitmap representing the service // to the image list // CBitmap * pbmp16x16 = NULL; CBitmap * pbmp32x32 = NULL; COLORREF rgbMask; if (GetBitmapParms( pServiceInfo, BMT_SERVICE, pbmp16x16, pbmp32x32, rgbMask )) { g_obl16x16.AddTail(new CImage(pbmp16x16, rgbMask)); g_obl32x32.AddTail(new CImage(pbmp32x32, rgbMask)); pServiceInfo->SetBitmapIndex(nBmpIndex++); } // // Add to the 'new instance' menu commands // if (pServiceInfo->SupportsInstances()) { // // Add to new instances menu object // m_oblNewInstanceCmds.AddTail(new CNewInstanceCmd(pServiceInfo)); } if (pServiceInfo->SupportsChildren()) { if (GetBitmapParms( pServiceInfo, BMT_VROOT, pbmp16x16, pbmp32x32, rgbMask )) { g_obl16x16.AddTail(new CImage(pbmp16x16, rgbMask)); g_obl32x32.AddTail(new CImage(pbmp32x32, rgbMask)); pServiceInfo->SetChildBitmapIndex(nBmpIndex++); } } ++cServices; } MatchupSuperDLLs(); } catch(CException * e) { TRACEEOLID("Exception loading library"); e->ReportError(); e->Delete(); } } if (cServices == 0) { // // No services installed // CString str; VERIFY(str.LoadString(IDS_NO_SERVICES_INSTALLED)); AfxMessageBox(str); } } BOOL CComponentDataImpl::RemoveServerFromCache( IN LPCTSTR lpstrServer ) /*++ Routine Description: Remove machine from cache. Arguments: LPCTSTR lpstrServer : computer name to be removed from cache Return Value: None --*/ { CStringList & strList = GetCachedServers(); TRACEEOLID("Removing " << lpstrServer << " from cache"); POSITION posOld; POSITION pos = strList.GetHeadPosition(); int nResult; while(pos) { posOld = pos; CString & str = strList.GetNext(pos); nResult = str.CompareNoCase(lpstrServer); if (nResult == 0) { strList.RemoveAt(posOld); SetCacheDirty(TRUE); return TRUE; } if (nResult > 0) { // // We're not going to find it. // break; } } // // Didn't exist // ASSERT(FALSE && "Attempting to remove non-existent server from cache"); return FALSE; } void CComponentDataImpl::AddServerToCache( IN LPCTSTR lpstrServer, IN BOOL fSetCacheDirty OPTIONAL ) /*++ Routine Description: Add machine name to the cache Arguments: LPCTSTR lpstrServer : computer name to be added to the cache. BOOL fSetCacheDirty : TRUE to dirty the cache Return Value: None --*/ { CStringList & strList = GetCachedServers(); TRACEEOLID("Adding " << lpstrServer << " to cache"); CString strServer(lpstrServer); POSITION posOld; POSITION pos = strList.GetHeadPosition(); int nResult; while(pos) { posOld = pos; CString & str = strList.GetNext(pos); nResult = str.CompareNoCase(strServer); if (nResult == 0) { // // Already existed in the case, ignore // return; } else if (nResult > 0) { // // Found the proper place // strList.InsertBefore(posOld, strServer); if (fSetCacheDirty) { SetCacheDirty(); } return; } } // // Didn't exist yet, so add to list here // strList.AddTail(strServer); if (fSetCacheDirty) { SetCacheDirty(); } } static BOOL GetCommandLineServer(LPTSTR * pStr) { BOOL bRes = FALSE; LPTSTR pCmdLine = GetCommandLine(); int n; LPTSTR * pArgv = CommandLineToArgvW(pCmdLine, &n); *pStr = NULL; if (pArgv != NULL) { TCHAR szCmd[] = _T("/SERVER:"); int len = sizeof(szCmd) / sizeof(TCHAR) - 1; for (int i = 0; i < n; i++) { if (CSTR_EQUAL == CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, pArgv[i], len, szCmd, len)) { LPTSTR p = pArgv[i] + len; int count = 0; while (*p != _T(' ') && *p != 0) { p++; count++; } *pStr = (LPTSTR)LocalAlloc(LPTR, sizeof(TCHAR) * (count + 1)); if (*pStr != NULL) { lstrcpyn(*pStr, pArgv[i] + len, count + 1); bRes = TRUE; } break; } } GlobalFree(pArgv); } return bRes; } void CComponentDataImpl::AddCachedServersToView() /*++ Routine Description: Move the cached servers to the scope view. Arguments: None Return Value: None --*/ { CStringList & strlList = GetCachedServers(); BOOL bCmdLine = FALSE; LPTSTR pCmdLine = NULL; if (GetCommandLineServer(&pCmdLine)) { bCmdLine = TRUE; EmptyServerList(); AddServerToCache(pCmdLine, FALSE); if (pCmdLine != NULL) LocalFree(pCmdLine); } else if (strlList.IsEmpty()) { // // Nothing pre-selected or cached. // Add the local machine. // CString str; DWORD dwSize = MAX_SERVERNAME_LEN; if (::GetComputerName(str.GetBuffer(dwSize + 1), &dwSize)) { // // Add local machine, though don't persist this later // str.ReleaseBuffer(); AddServerToCache(str, FALSE); } } // // Now add everything cached to the current // view // CError err; for(POSITION pos = strlList.GetHeadPosition(); pos != NULL; ) { CString & strMachine = strlList.GetNext(pos); int cServices; err = AddServerToList( FALSE, bCmdLine ? TRUE : FALSE, strMachine, cServices, m_oblServices ); TRACEEOLID("adding " << strMachine << " to the view returned error code " << err); if (err.Failed()) { // // Temporarily map RPC errors to friendlier error message // TEMP_ERROR_OVERRIDE(EPT_S_NOT_REGISTERED, IDS_ERR_RPC_NA); TEMP_ERROR_OVERRIDE(RPC_S_SERVER_UNAVAILABLE, IDS_ERR_RPC_NA); TEMP_ERROR_OVERRIDE(RPC_S_UNKNOWN_IF, IDS_ERR_INTERFACE); TEMP_ERROR_OVERRIDE(RPC_S_PROCNUM_OUT_OF_RANGE, IDS_ERR_INTERFACE); AFX_MANAGE_STATE(::AfxGetStaticModuleState()); // // Give the option of removing from cache // if (err.MessageBoxFormat( IDS_ERROR_CONNECTING_CACHE, MB_YESNO | MB_DEFBUTTON1, NO_HELP_CONTEXT, (LPCTSTR)strMachine) != IDYES) { VERIFY(RemoveServerFromCache(strMachine)); } } else if (cServices == 0) { // // No errors, but no services found // ::AfxMessageBox(IDS_NO_SERVICE); } } } CServiceInfo * CComponentDataImpl::GetServiceAt( IN int nIndex ) /*++ Routine Description: Get the service object at the given index. Arguments: int nIndex : Index where to look for the service info object Return Value: Service info pointer, or NULL if the index was not valid --*/ { if (nIndex < 0 || nIndex >= m_oblServices.GetCount()) { TRACEEOLID("Invalid service index requested"); return NULL; } return (CServiceInfo *)m_oblServices.GetAt(m_oblServices.FindIndex(nIndex)); } void CComponentDataImpl::EmptyServerList() /*++ Routine Description: Empty server list Arguments: None Return Value: None --*/ { m_oblServers.RemoveAll(); m_cServers = m_cServicesRunning = 0; } DWORD CComponentDataImpl::AddServerToList( IN BOOL fCache, IN LPINET_SERVER_INFO lpServerInfo, IN OUT CObListPlus & oblServices ) /*++ Routine Description: Add a service object for each service discovered to be belonging to this server. Arguments: BOOL fCache : TRUE to cache LPINET_SERVER_INFO lpServerInfo : Discovery information (from inetsloc) CObListPlus & oblServices : List of installed services Return Value: Error return code --*/ { TRACEEOLID("For Server " << lpServerInfo->ServerName); CServerInfo * pServerInfo; DWORD err = ERROR_SUCCESS; for (DWORD j = 0; j < lpServerInfo->Services.NumServices; ++j) { LPINET_SERVICE_INFO lpServiceInfo = lpServerInfo->Services.Services[j]; try { // // Attempt to create a server info block // pServerInfo = new CServerInfo( lpServerInfo->ServerName, lpServiceInfo, oblServices ); if (pServerInfo->IsConfigurable()) { TRACEEOLID("Adding " << (DWORD)lpServiceInfo->ServiceMask); if (!AddToList(fCache, pServerInfo)) { TRACEEOLID("It already existed in the list"); delete pServerInfo; } } else { // // Toss it // TRACEEOLID("Tossing " << (DWORD)lpServiceInfo->ServiceMask); delete pServerInfo; } } catch(CMemoryException * e) { TRACEEOLID("AddServerList: memory exception"); err = ERROR_NOT_ENOUGH_MEMORY; e->Delete(); } } return err; } DWORD CComponentDataImpl::AddServerToList( IN BOOL fCache, IN BOOL fHandleErrors, IN CString & strServerName, OUT int & cServices, IN OUT CObListPlus & oblServices ) /*++ Routine Description: Add a service object for each service running on the machine listed above. Arguments: BOOL fCache : TRUE to cache the server BOOL fHandleErrors : TRUE to display error messages, FALSE to abort on error CString & strServerName : Name of this server int & cServices : # Services added CObListPlus & oblServices : List of installed services Return Value: Error return code --*/ { TEMP_ERROR_OVERRIDE(EPT_S_NOT_REGISTERED, IDS_ERR_RPC_NA); TEMP_ERROR_OVERRIDE(RPC_S_SERVER_UNAVAILABLE, IDS_ERR_RPC_NA); TEMP_ERROR_OVERRIDE(RPC_S_UNKNOWN_IF, IDS_ERR_INTERFACE); TEMP_ERROR_OVERRIDE(RPC_S_PROCNUM_OUT_OF_RANGE, IDS_ERR_INTERFACE); // // Loop through the services, and find out which ones // are installed on the target machine, if any. // CObListIter obli(oblServices); CServiceInfo * psi; cServices = 0; CError err; ISMSERVERINFO ServerInfo; CServerInfo::CleanServerName(strServerName); // // See if we can make contact with the machine // if (!DoesServerExist(strServerName)) { // // No, quit right here // err = RPC_S_SERVER_UNAVAILABLE; if (fHandleErrors) { err.MessageBox(); } return err.Win32Error(); } while (psi = (CServiceInfo *)obli.Next()) { int cErrors = 0; if (psi->InitializedOK()) { TRACEEOLID("Trying: " << psi->GetShortName()); ServerInfo.dwSize = sizeof(ServerInfo); { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); CWaitCursor wait; err = psi->ISMQueryServerInfo( strServerName, &ServerInfo ); } if (err.Win32Error() == ERROR_SERVICE_DOES_NOT_EXIST) { TRACEEOLID("Service not installed -- acceptable response"); err.Reset(); } else if (err.Win32Error() == ERROR_SERVICE_START_HANG) { TRACEEOLID("Service is hanging -- ignore silently"); err.Reset(); } else if (err.Succeeded()) { // // Yes, this service is running on this // machine. // ++cServices; // // Add to list // try { CServerInfo * pNewServer = new CServerInfo( strServerName, &ServerInfo, psi ); if (!AddToList(fCache, pNewServer, TRUE)) { TRACEEOLID("It already existed in the list"); delete pNewServer; } } catch(CMemoryException * e) { TRACEEOLID("AddServerList: memory exception"); err = ERROR_NOT_ENOUGH_MEMORY; e->Delete(); } } if (err.Failed()) { if (!fHandleErrors) { // // Let the calling process handle the errors, // we're stopping. // break; } ++cErrors; // // Display error about the service // AFX_MANAGE_STATE(::AfxGetStaticModuleState()); err.MessageBoxFormat( IDS_ERR_ENUMERATE_SVC, MB_OK, NO_HELP_CONTEXT, (LPCTSTR)psi->GetShortName(), (LPCTSTR)strServerName ); // // Optionally cancel here on no response. // //break; } } } return err.Win32Error(); } DWORD CComponentDataImpl::RemoveServerFromList( IN BOOL fCache, IN CString & strServerName ) /*++ Routine Description: Remove each service in the list belonging to the given computer name. Arguments: CString & strServerName : Name of this server Return Value: Error return code --*/ { CServerInfo::CleanServerName(strServerName); CServerInfo * pEntry; POSITION pos1, pos2; pos1 = m_oblServers.GetHeadPosition(); while(pos1) { pos2 = pos1; pEntry = (CServerInfo *)m_oblServers.GetNext(pos1); if (pEntry->MatchServerName(strServerName)) { m_oblServers.RemoveAt(pos2); } } if (fCache) { RemoveServerFromCache(strServerName); } return ERROR_SUCCESS; } void CComponentDataImpl::Refresh() /*++ Routine Description: Refresh the server list Arguments: None Return Value: None --*/ { // // AFX_MANAGE_STATE required for wait cursor // AFX_MANAGE_STATE(::AfxGetStaticModuleState()); POSITION pos; CWaitCursor wait; CServerInfo * pEntry; for(pos = m_oblServers.GetHeadPosition(); pos != NULL; /**/ ) { pEntry = (CServerInfo *)m_oblServers.GetNext(pos); int oldState = pEntry->QueryServiceState(); if (pEntry->Refresh() == ERROR_SUCCESS) { if (oldState != pEntry->QueryServiceState()) { // // Take away this service from the total // running count if it was part of it // before, and (re-) add it if it's currently // running. // if (oldState == INetServiceRunning) { --m_cServicesRunning; } if (pEntry->IsServiceRunning()) { ++m_cServicesRunning; } } //UpdateAllViews(NULL, HINT_REFRESHITEM, pEntry); } } } BOOL CComponentDataImpl::AddToList( IN BOOL fCache, IN CServerInfo * pServerInfo, IN BOOL fSelect ) /*++ Routine Description: Add the service to the list if it didn't exist already, otherwise refresh the info if it did exist. Return TRUE if the service was added, FALSE, if already existed and was refreshed. Arguments: CServerInfo * pServerInfo : Server to add BOOL fSelect : If TRUE, select the newly added item Return Value: TRUE if added, FALSE otherwise --*/ { POSITION pos; BOOL fFoundService = FALSE; BOOL fFoundServer = FALSE; BOOL fServiceAdded = FALSE; //int nAddHint = fSelect ? HINT_ADDITEM_SELECT : HINT_ADDITEM; CServerInfo * pEntry; for( pos = m_oblServers.GetHeadPosition(); pos != NULL; m_oblServers.GetNext(pos) ) { pEntry = (CServerInfo *)m_oblServers.GetAt(pos); if (pEntry->CompareByServer(pServerInfo) == 0) { fFoundServer = TRUE; // // Found the server, also the service? // if (pEntry->CompareByService(pServerInfo) == 0) { // // Yes, the service was found also -- update the information // if the service state has changed. // TRACEEOLID("Entry Already Existed"); fFoundService = TRUE; if (pEntry->QueryServiceState() != pServerInfo->QueryServiceState()) { TRACEEOLID("Service State has changed -- refreshing"); // // Decrement the running counter if this service // was part of that counter. The counter will be // re-added if the service is still running // if (pEntry->IsServiceRunning()) { --m_cServicesRunning; } *pEntry = *pServerInfo; if (pEntry->IsServiceRunning()) { ++m_cServicesRunning; } //UpdateAllViews(NULL, HINT_REFRESHITEM, pEntry); } break; } } else { if (fFoundServer) { // // Server name no longer matches, but did match // the last time, so we didn't find the service. // Insert it at the end of the services belonging // to this guy. // TRACEEOLID("Found new service belonging to known server"); m_oblServers.InsertBefore(pos, pServerInfo); fServiceAdded = TRUE; // Don't add again. if (pServerInfo->IsServiceRunning()) { ++m_cServicesRunning; } //UpdateAllViews(NULL, nAddHint, pServerInfo); break; } } } if (!fFoundService && !fServiceAdded) { // // Came to the end of the list without // finding the service -- add a new one // at the end. // TRACEEOLID("Adding new entry to tail"); m_oblServers.AddTail(pServerInfo); if (pServerInfo->IsServiceRunning()) { ++m_cServicesRunning; } if (!fFoundServer) { ++m_cServers; if (fCache) { AddServerToCache(pServerInfo->QueryServerName(), TRUE); } } if (fCache && GetRootHandle() != NULL) { // // View has already been expanded -- add it here. // if (pServerInfo->IsServiceSelected()) { AddServerInfoParent(GetRootHandle(), pServerInfo); } } } return !fFoundService; } static HRESULT GetSnapinHelpFile(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; } STDMETHODIMP CComponentDataImpl::GetHelpTopic(LPOLESTR *lpCompiledHelpFile) { return GetSnapinHelpFile(lpCompiledHelpFile); } // // About implementation // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< DEBUG_DECLARE_INSTANCE_COUNTER(CSnapinAboutImpl); CSnapinAboutImpl::CSnapinAboutImpl() /*++ Routine Description: Constructor Arguments: None Return Value: N/A --*/ { } CSnapinAboutImpl::~CSnapinAboutImpl() /*++ Routine Description: Destructor Arguments: N/A Return Value: N/A --*/ { DEBUG_DECREMENT_INSTANCE_COUNTER(CSnapinAboutImpl); } HRESULT CSnapinAboutImpl::AboutHelper( IN UINT nID, OUT LPOLESTR * lpPtr ) /*++ Routine Description: "About" helper function Arguments: UINT nID : String resource ID LPOLESTR * lpPtr : Return buffer for the string Return Value: HRESULT --*/ { if (lpPtr == NULL) { return E_POINTER; } CString s; VERIFY(::LoadString( _Module.GetModuleInstance(), nID, s.GetBuffer(255), 255)); s.ReleaseBuffer(); *lpPtr = (LPOLESTR)CoTaskMemAlloc((s.GetLength() + 1) * sizeof(wchar_t)); if (*lpPtr == NULL) { return E_OUTOFMEMORY; } lstrcpy(*lpPtr, (LPCTSTR)s); return S_OK; } STDMETHODIMP CSnapinAboutImpl::GetSnapinDescription( OUT LPOLESTR * lpDescription ) /*++ Routine Description: Get the snapin description Arguments: LPOLESTR * lpDescription : String return buffer Return Value: HRESULT --*/ { return AboutHelper(IDS_SNAPIN_DESC, lpDescription); } STDMETHODIMP CSnapinAboutImpl::GetProvider( OUT LPOLESTR * lpName ) /*++ Routine Description: Get the snapin provider string Arguments: LPOLESTR * lpName : String return buffer Return Value: HRESULT --*/ { return AboutHelper(IDS_COMPANY, lpName); } STDMETHODIMP CSnapinAboutImpl::GetSnapinVersion( IN LPOLESTR * lpVersion ) /*++ Routine Description: Get the snapin version string: Arguments: LPOLESTR * lpVersion : Version string Return Value: HRESULT --*/ { return AboutHelper(IDS_VERSION, lpVersion); } STDMETHODIMP CSnapinAboutImpl::GetSnapinImage( IN 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; } *hAppIcon = LoadIcon( _Module.GetModuleInstance(), MAKEINTRESOURCE(IDI_ICON)); ASSERT(*hAppIcon != NULL); return (*hAppIcon != NULL) ? S_OK : E_FAIL; } STDMETHODIMP CSnapinAboutImpl::GetStaticFolderImage( OUT HBITMAP * phSmallImage, OUT HBITMAP * phSmallImageOpen, OUT HBITMAP * phLargeImage, OUT COLORREF * prgbMask ) /*++ Routine Description: Get the static folder image 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); } *phSmallImage = (HBITMAP)LoadImage( _Module.GetModuleInstance(), MAKEINTRESOURCE(IDB_INETMGR16), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR ); *phSmallImageOpen = (HBITMAP)LoadImage( _Module.GetModuleInstance(), MAKEINTRESOURCE(IDB_INETMGR16), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR ); *phLargeImage = (HBITMAP)LoadImage( _Module.GetModuleInstance(), MAKEINTRESOURCE(IDB_INETMGR32), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR ); *prgbMask = RGB_BK_IMAGES; return *phSmallImage && *phLargeImage ? S_OK : E_FAIL; }