/* CompData.cpp : Implementation of the ComponentData (namespace items) for * the MSInfo Snapin. * * Copyright (c) 1998-1999 Microsoft Corporation * * History: a-jsari 8/27/97 Initial version */ #include "StdAfx.h" #include #include #include #include #ifndef IDB_16x16 #include "Resource.h" #endif // IDB_16x16 #include "resrc1.h" #include "DataObj.h" #include "CompData.h" #include "DataSrc.h" #include "SysInfo.h" #include "ViewObj.h" #include "Dialogs.h" #include "chooser.h" static LPCTSTR cszViewKey = _T("Software\\Microsoft\\MSInfo"); static LPCTSTR cszViewValue = _T("View"); static LPCTSTR cszBasicValue = _T("basic"); static LPCTSTR cszAdvancedValue = _T("advanced"); /* * CSystemInfoScope - Trivial constructor. Make sure all of our * essential pointers start NULL. * * History: a-jsari 8/27/97 Initial version */ CSystemInfoScope::CSystemInfoScope() :m_pScope(NULL), m_pConsole(NULL), m_pSource(NULL), m_bIsDirty(FALSE), m_bInitializedCD(FALSE), m_fViewUninitialized(FALSE), m_prdSave(NULL), m_prdOpen(NULL), m_prdReport(NULL), m_pthdFind(NULL), m_BasicFlags(0L), m_AdvancedFlags(MF_CHECKED), m_pwConsole(NULL), m_pmapCategories(NULL), m_pstrMachineName(new CString), m_pstrOverrideName(new CString), m_pstrCategory(NULL), m_RootCookie(0), m_fSelectCategory(FALSE), m_pSetSourceSource(NULL), m_fSetSourcePreLaunch(FALSE), m_pViewCABTool(NULL), m_pSaveUnknown(NULL), m_fInternalDelete(FALSE), m_pLastSystemInfo(NULL) { #ifdef _DEBUG m_bDestroyedCD = true; #endif // _DEBUG } /* * ~CSystemInfoScope - Never called. Don't use. * * History: a-jsari 8/27/97 Initial version */ CSystemInfoScope::~CSystemInfoScope() { #ifdef _DEBUG if (m_bInitializedCD) ASSERT(m_pScope == NULL); ASSERT(m_pConsole == NULL); ASSERT(m_bDestroyedCD); #endif } /* * InitializeDialogs - Pre-create our Dialog pointers to speed their loading when * they are needed. * * History: a-jsari 11/28/97 Moved from Initialize */ inline HRESULT CSystemInfoScope::InitializeDialogs() { HWND hWindow; ASSERT(m_pConsole != NULL); HRESULT hr = m_pConsole->GetMainWindow(&hWindow); ASSERT(hr == S_OK); if (FAILED(hr)) return hr; ASSERT(m_prdReport == NULL); m_prdReport = new CMSInfoReportDialog(hWindow); ASSERT(m_prdReport != NULL); if (m_prdReport == NULL) ::AfxThrowMemoryException(); m_prdSave = new CMSInfoSaveDialog(hWindow); ASSERT(m_prdSave != NULL); if (m_prdSave == NULL) ::AfxThrowMemoryException(); m_prdOpen = new CMSInfoOpenDialog(hWindow); ASSERT(m_prdOpen != NULL); if (m_prdOpen == NULL) ::AfxThrowMemoryException(); return hr; } /* * FindWindowProc - The window proc for the hidden window that will allow us to * call find from the Find Dialog Thread. * * History: a-jsari 2/6/98 Initial version */ static LRESULT CALLBACK FindWindowProc(HWND, UINT uMsg, WPARAM wParam, LPARAM lParam) { // ASSERT(uMsg == CFindDialog::WM_MSINFO_FIND); // Only process our specific message. if (uMsg == CFindDialog::WM_MSINFO_FIND) reinterpret_cast(wParam)->ExecuteFind((long)lParam); return 1; } /* * InitializeInternal - Because MMC never calls our destructor, we are leaking * memory in our member classes. To fix this, allocate them on the heap * and delete them in our Destroy method. * * History: a-jsari 12/30/97 Initial version */ inline HRESULT CSystemInfoScope::InitializeInternal() { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); m_pmapCategories = new CScopeItemMap; ASSERT(m_pmapCategories != NULL); if (m_pmapCategories == NULL) ::AfxThrowMemoryException(); CString strClassName; CString strWindowName; HWND hwndParent; CREATESTRUCT csFind; VERIFY(strClassName.LoadString(IDS_FINDCLASS)); VERIFY(strWindowName.LoadString(IDS_FINDWINDOWNAME)); WNDCLASSEX wceFind; ::memset(&wceFind, 0, sizeof(wceFind)); wceFind.cbSize = sizeof(wceFind); wceFind.style = CS_CLASSDC; wceFind.lpfnWndProc = FindWindowProc; wceFind.hInstance = ::AfxGetInstanceHandle(); wceFind.lpszClassName = (LPCTSTR)strClassName; ::RegisterClassEx(&wceFind); pConsole()->GetMainWindow(&hwndParent); m_hwndFind = ::CreateWindow(strClassName, strWindowName, WS_CHILD, 0, 0, 0, 0, hwndParent, NULL, wceFind.hInstance, &csFind); ASSERT(m_hwndFind != NULL); return S_OK; } /* * SkipSpaces - Advance the pointer passed in beyond all spaces. * * History: a-jsari 11/28/97 Initial version */ inline void SkipSpaces(LPTSTR &pszString) { while (_istspace(*pszString)) ++pszString; } /* * GetValue - Save the value (of the form '= Value' out of pszString into * szValue. * * History: a-jsari 11/28/97 Initial version */ static inline BOOL GetValue(LPTSTR &pszString, LPTSTR szValue) { SkipSpaces(pszString); //if (*pszString++ != (TCHAR)'=') // return FALSE; if (*pszString == (TCHAR)'=') pszString++; SkipSpaces(pszString); if (*pszString == (TCHAR)'"') { ++pszString; do { *szValue = *pszString; if (*szValue++ == 0) return FALSE; } while ((*pszString++ != '"')); *--szValue = (TCHAR)0; } else { do { *szValue++ = *pszString; if (*pszString == 0) return TRUE; } while (!::_istspace(*pszString++)); *--szValue = (TCHAR)0; } return TRUE; } /* * DisplayHelp - Show the help information for MSInfo * * History: a-jsari 11/28/97 Initial version */ static inline void DisplayHelp(LPTSTR /* szHelp */) { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); CString strHelpFile; VERIFY(strHelpFile.LoadString(IDS_HELPFILE)); ::HtmlHelp(/* HWND */ NULL, strHelpFile, HH_DISPLAY_TOPIC, 0); } /* * ProcessCommandLine - Grab all of the essential values out of our command * line. * * History: a-jsari 11/28/97 Initial version */ HRESULT CSystemInfoScope::ProcessCommandLine() { const int VALUE_SIZE = 256; LPTSTR szCommands = ::GetCommandLine(); LPTSTR pszCurrent = szCommands; TCHAR szValueBuffer[VALUE_SIZE]; int iFirst; if (pszCurrent == NULL) return E_FAIL; while (TRUE) { iFirst = _tcscspn(pszCurrent, _T(" \t")); // If our match is \0, we have reached the end of the string. if (pszCurrent[iFirst] == (TCHAR)'\0') break; pszCurrent += iFirst; ++pszCurrent; SkipSpaces(pszCurrent); // Not a command line switch, check the next parameter. if (!(*pszCurrent == (TCHAR)'/' || *pszCurrent == (TCHAR)'-')) continue; else ++pszCurrent; // We are processing the computer flag. if (_tcsnicmp(pszCurrent, _T("computer"), 8) == 0) { pszCurrent += 8; if (GetValue(pszCurrent, szValueBuffer) == TRUE) m_strDeferredMachine = szValueBuffer; } // After this point, all flags are msinfo specific. if (_tcsnicmp(pszCurrent, _T("msinfo"), 6) != 0) continue; pszCurrent += 6; // pszCurrent += strlen("msinfo"); if (*pszCurrent == (TCHAR)'?' || _tcsnicmp(pszCurrent, _T("_help"), 5) == 0) { LPTSTR pszValue; if (GetValue(pszCurrent, szValueBuffer) == TRUE) { pszValue = szValueBuffer; } else { // No value for help switch, back up to the previous space, unless we're // at the end of the string. if (*pszCurrent != 0) do --pszCurrent; while (!::_istspace(*pszCurrent)); pszValue = NULL; } DisplayHelp(pszValue); continue; } if (_tcsnicmp(pszCurrent, _T("_category"), 9) == 0) { pszCurrent += 9; if (GetValue(pszCurrent, szValueBuffer) == TRUE) { // -1 parameter means select no result pane item. m_pstrCategory = new CString(szValueBuffer); // SelectItem(szValueBuffer, -1); } continue; } if (_tcsnicmp(pszCurrent, _T("_file"), 5) == 0) { pszCurrent += 5; if (GetValue(pszCurrent, szValueBuffer) == TRUE) m_strDeferredLoad = szValueBuffer; continue; } if (_tcsnicmp(pszCurrent, _T("_showcategories"), 15) == 0) { pszCurrent += 15; if (GetValue(pszCurrent, szValueBuffer) == TRUE) m_strDeferredCategories = szValueBuffer; continue; } } return S_OK; } /* * Initialize - Called from MMC; we Initialize all of our relevant pointers * using QueryInterface and set the IConsole ImageList. * * History: a-jsari 8/27/97 Initial version */ STDMETHODIMP CSystemInfoScope::Initialize(LPUNKNOWN pUnknown) { ASSERT(pUnknown != NULL); TRACE(_T("CSystemInfoScope::Initialize\n")); HRESULT hr; do { AFX_MANAGE_STATE(AfxGetStaticModuleState()); // MMC should only call ::Initialize once! ASSERT(m_pScope == NULL); hr = pUnknown->QueryInterface(IID_IConsoleNameSpace, reinterpret_cast(&m_pScope)); ASSERT(hr == S_OK); if (FAILED(hr)) break; ASSERT(m_pConsole == NULL); hr = pUnknown->QueryInterface(IID_IConsole, reinterpret_cast(&m_pConsole)); ASSERT(hr == S_OK); if (FAILED(hr)) break; if (m_pSaveUnknown == NULL) // check this out, reversed { // We are reinitializing, so don't do the image list code again. ::CBitmap bmp16x16; ::CBitmap bmp32x32; LPIMAGELIST lpScopeImage; hr = m_pConsole->QueryScopeImageList(&lpScopeImage); ASSERT(hr == S_OK); if (FAILED(hr)) break; VERIFY(bmp16x16.LoadBitmap(IDB_16x16)); VERIFY(bmp32x32.LoadBitmap(IDB_32x32)); hr = lpScopeImage->ImageListSetStrip( reinterpret_cast(static_cast(bmp16x16)), reinterpret_cast(static_cast(bmp32x32)), 0, RGB(255,0,255)); (void)lpScopeImage->Release(); ASSERT(hr == S_OK); if (FAILED(hr)) break; // This is also a fine place to add the log entry for starting MSInfo, // so it won't be repeated when we reinitialize. if (msiLog.IsLogging()) msiLog.WriteLog(CMSInfoLog::BASIC, _T("START MSInfo\r\n")); } hr = InitializeDialogs(); if (FAILED(hr)) break; hr = InitializeInternal(); if (FAILED(hr)) break; hr = ProcessCommandLine(); } while (FALSE); if (FAILED(hr)) { SAFE_RELEASE(m_pScope); SAFE_RELEASE(m_pConsole); } else { m_bInitializedCD = true; } #ifdef _DEBUG m_bDestroyedCD = false; #endif // Note that MMC does not permit us to fail return from Initialize, // so we always return S_OK, whether or not our Initialization is // successful. return S_OK; } /* * InitializeView - Read the current user's view information from the registry * * History: a-jsari 12/3/97 Initial version. */ HRESULT CSystemInfoScope::InitializeView() { CRegKey crkView; long lResult; TCHAR szBuffer[1024]; DWORD dwSize; lResult = crkView.Open(HKEY_CURRENT_USER, cszViewKey); if (lResult == ERROR_SUCCESS) { dwSize = sizeof(szBuffer); lResult = crkView.QueryValue(szBuffer, cszViewValue, &dwSize); } if (lResult != ERROR_SUCCESS) // Default to basic view. SetView(BASIC, TRUE); else { if (::_tcscmp(szBuffer, cszBasicValue) == 0) SetView(BASIC, FALSE); else if (::_tcscmp(szBuffer, cszAdvancedValue) == 0) SetView(ADVANCED, FALSE); else { ASSERT(FALSE); return E_FAIL; } } return S_OK; } HRESULT CSystemInfoScope::MessageBox( CString strText) { CString strTitle; strTitle.LoadString( IDS_DESCRIPTION); int nRC; return pConsole()->MessageBox( strText, strTitle, MB_OK, &nRC); } /* * InitializeSource - Perform any initialization which may fail. * * History: a-jsari 12/3/97 Initial version. */ HRESULT CSystemInfoScope::InitializeSource() { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); CWaitCursor DoWaitCursor; try { // m_pSource will exist if we were loaded from a stream. if (m_pSource == NULL) { m_bInitializedCD = true; // Initialize WBEM with the machine name. // Deleted in either SetView or Destroy m_pSource = new CWBEMDataSource(MachineName()); } } catch (CUserException *) { CString strLocalConnect; VERIFY(strLocalConnect.LoadString(IDS_LOCAL_CONNECT)); MessageBox((LPCTSTR)strLocalConnect); try { (*m_pstrMachineName) = _T(""); m_pSource = new CWBEMDataSource(NULL); } catch (CUserException *) { m_bInitializedCD = false; return E_FAIL; } } m_fViewUninitialized = TRUE; InitializeView(); return S_OK; } /* * DestroyInternal - Delete all of our internal pointers. This method of dealing * with pointers is required because MMC never calls our object's destructor. * * History: a-jsari 12/30/97 Initial version */ void CSystemInfoScope::DestroyInternal() { // AFX_MANAGE_STATE(::AfxGetStaticModuleState()); if (pSource() && pSource()->GetType() == CDataSource::GATHERER) { CWBEMDataSource * pWBEMSource = reinterpret_cast(pSource()); if (pWBEMSource && pWBEMSource->m_pThreadRefresh) pWBEMSource->m_pThreadRefresh->CancelRefresh(FALSE); } // If there is a find thread, it will take care of getting rid of the window. // Otherwise, we must destroy it here. Fixes bug 395091. if (m_pthdFind) dynamic_cast(m_pthdFind)->RemoteQuit(); else ::DestroyWindow(m_hwndFind); if (m_pSource) { delete m_pSource; m_pSource = NULL; } if (m_prdSave) { delete m_prdSave; m_prdSave = NULL; } if (m_prdOpen) { delete m_prdOpen; m_prdOpen = NULL; } if (m_prdReport) { delete m_prdReport; m_prdReport = NULL; } if (m_pstrMachineName) { delete m_pstrMachineName; m_pstrMachineName = NULL; } if (m_pstrOverrideName) { delete m_pstrOverrideName; m_pstrOverrideName = NULL; } if (m_pmapCategories) { delete m_pmapCategories; m_pmapCategories = NULL; } if (m_pViewCABTool) { delete m_pViewCABTool; m_pViewCABTool = NULL; } } /* * CreateComponent - Create the IComponent interface object (the CSystemInfo * object in this framework) and attach myself (the IComponentData * interface object) to it. Return the QueryInterfaced IComponent * pointer in ppComponent. * * History: a-jsari 8/27/97 Initial version */ STDMETHODIMP CSystemInfoScope::CreateComponent(LPCOMPONENT *ppComponent) { ASSERT(ppComponent != NULL); TRACE(_T("CSystemInfoScope::CreateComponent\n")); #if 0 if (m_bInitializedCD == false) { *ppComponent = NULL; return S_OK; } #endif CComObject *pObject; CComObject::CreateInstance(&pObject); ASSERT(pObject != NULL); if(NULL == pObject) return E_OUTOFMEMORY; m_pLastSystemInfo = pObject; //Store IComponentData HRESULT hr; hr = pObject->SetIComponentData(this); ASSERT(hr == S_OK); if (FAILED(hr)) { return hr; } return pObject->QueryInterface(IID_IComponent, reinterpret_cast(ppComponent)); } /* * PreUIInit - Make any changes required just before the UI initializes. * * History: a-jsari 3/6/98 Initial version */ HRESULT CSystemInfoScope::PreUIInit() { HRESULT hr = S_OK; if (m_pSource == NULL) { hr = InitializeSource(); } else { hr = InitializeView(); } return hr; } /* * PostUIInit - Do all initialization which can only occur after the result * pane UI * * History: a-jsari 3/6/98 Initial version */ void CSystemInfoScope::PostUIInit() { if (!m_strDeferredLoad.IsEmpty() || !m_strDeferredMachine.IsEmpty()) { // Instead of doing a "SetSource(m_pSource, FALSE)", which deletes the // tree, all we need to do here is rename the root node to match the // opened file. A non-empty m_strDeferredLoad indicates this is necessary. // (Or a non empty deferred machine change.) m_strDeferredLoad.Empty(); m_strDeferredMachine.Empty(); SCOPEDATAITEM sdiRoot; CString strNodeName; HSCOPEITEM hsiRoot; HRESULT hr; if (m_pmapCategories->ScopeFromView(NULL, hsiRoot)) { ::memset(&sdiRoot, 0, sizeof(sdiRoot)); sdiRoot.ID = hsiRoot; sdiRoot.mask = SDI_STR; hr = pScope()->GetItem(&sdiRoot); ASSERT(SUCCEEDED(hr)); if (SUCCEEDED(hr)) { m_pSource->GetNodeName(strNodeName); sdiRoot.displayname = T2OLE(const_cast((LPCTSTR)strNodeName)); hr = pScope()->SetItem(&sdiRoot); ASSERT(SUCCEEDED(hr)); } } } // This is the first point at which we can set UI values. if (m_fViewUninitialized && m_fSelectCategory) { m_fViewUninitialized = FALSE; SetSource(m_pSource, FALSE); } if (m_pstrCategory != NULL && m_fSelectCategory) { // Before the SelectItem call to prevent recursion. m_fSelectCategory = FALSE; SelectItem(*m_pstrCategory); delete m_pstrCategory; m_pstrCategory = NULL; } } /* * Notify - Handle any MSInfo namespace node events posted by MMC. * * History: a-jsari 8/27/97 Initial version */ STDMETHODIMP CSystemInfoScope::Notify(LPDATAOBJECT pDataObject, MMC_NOTIFY_TYPE event, LPARAM arg, LPARAM param) { TRACE(_T("CSystemInfoScope::Notify (DataObject, %lx, %p, %p)\n"), event, arg, param); HRESULT hr; if (m_bInitializedCD == false) return S_OK; switch(event) { case MMCN_EXPAND: if (m_pSaveUnknown) { m_pstrMachineName = new CString; m_pstrOverrideName = new CString; Initialize(m_pSaveUnknown); m_pSaveUnknown = NULL; } hr = PreUIInit(); if (FAILED(hr)) break; hr = OnExpand(pDataObject, arg, param); PostUIInit(); break; case MMCN_REMOVE_CHILDREN: // Sometimes we make an internal call which causes this notification to be // sent, but we don't want to process it the same way. if (!m_fInternalDelete) { m_pSaveUnknown = m_pScope; DestroyInternal(); SAFE_RELEASE(m_pScope); SAFE_RELEASE(m_pConsole); m_pScope = NULL; m_pConsole = NULL; m_pSource = NULL; m_prdSave = NULL; m_prdOpen = NULL; m_prdReport = NULL; m_pstrMachineName = NULL; m_pstrOverrideName = NULL; m_pmapCategories = NULL; } hr = S_OK; break; case MMCN_PROPERTY_CHANGE: hr = OnProperties(param); break; case MMCN_EXPANDSYNC: break; default: ASSERT(FALSE); break; } return hr; } /* * Destroy - Release all of our Initialized pointers. * * History: a-jsari 8/27/97 Initial version */ STDMETHODIMP CSystemInfoScope::Destroy() { TRACE(L"CSystemInfoScope::Destroy\n"); if (m_pwConsole != NULL) { m_pwConsole->Detach(); delete m_pwConsole; } #if FALSE #ifdef _DEBUG m_bDestroyedCD = true; #endif #if 0 if (m_bInitializedCD == FALSE) return S_OK; #endif DestroyInternal(); SAFE_RELEASE(m_pScope); SAFE_RELEASE(m_pConsole); #endif return S_OK; } /* * QueryDataObject - Return the DataObject referred to by the cookie * and type. * * History: a-jsari 8/27/97 Initial version */ STDMETHODIMP CSystemInfoScope::QueryDataObject(MMC_COOKIE cookie, DATA_OBJECT_TYPES type, LPDATAOBJECT *ppDataObject) { TRACE(_T("CSystemInfoScope::QueryDataObject (%lx, %x, DataObject)\n"), cookie, type); return CDataObject::CreateDataObject(cookie, type, this, ppDataObject); } /* * CompareObjects - Compare two objects to see if they are equivalent. * * History: a-jsari 8/27/97 Initial version */ STDMETHODIMP CSystemInfoScope::CompareObjects(LPDATAOBJECT lpDataObjectA, LPDATAOBJECT lpDataObjectB) { TRACE(_T("CSystemInfoScope::CompareObjects\n")); return CDataObject::CompareObjects(lpDataObjectA, lpDataObjectB); } /* * GetDisplayInfo - Returns display information for this node in the scope pane. * * History: a-jsari 8/27/97 Initial version */ STDMETHODIMP CSystemInfoScope::GetDisplayInfo(SCOPEDATAITEM *pScopeDataItem) { USES_CONVERSION; #if 0 TRACE(_T("CSystemInfoScope::GetDisplayInfo\n")); #endif ASSERT(pScopeDataItem != NULL); if (pScopeDataItem == NULL) return E_POINTER; ASSERT(pScopeDataItem->mask & SDI_STR); if (pScopeDataItem->mask & SDI_STR) { CViewObject *pDataCategory = reinterpret_cast(pScopeDataItem->lParam); pScopeDataItem->displayname = T2OLE(const_cast(pDataCategory->GetTextItem())); } return S_OK; } /* * AddToMenu - Add an item to a menu. This method assumes that the calling * function has called AFX_MANAGE_STATE(AfxGetStaticModuleState()) prior * to calling this function. * * History: a-jsari 9/15/97 Initial version */ HRESULT CSystemInfoScope::AddToMenu(LPCONTEXTMENUCALLBACK lpCallback, long lNameResource, long lStatusResource, long lCommandID, long lInsertionPoint, long fFlags) { USES_CONVERSION; CONTEXTMENUITEM cmiMenuItem = { NULL, NULL, lCommandID, lInsertionPoint, fFlags, 0L}; CString szResourceName; CString szResourceStatus; // FIX: Make these resources load only once. VERIFY(szResourceName.LoadString(lNameResource)); VERIFY(szResourceStatus.LoadString(lStatusResource)); cmiMenuItem.strName = WSTR_FROM_CSTRING(szResourceName); cmiMenuItem.strStatusBarText = WSTR_FROM_CSTRING(szResourceStatus); HRESULT hr = lpCallback->AddItem(&cmiMenuItem); ASSERT(hr == S_OK); return hr; } /* * AddMenuItems - Add the "Save Report" and "Save System Information" items to * the context menu. * * History: a-jsari 9/15/97 Initial version */ extern BOOL fCABOpened; STDMETHODIMP CSystemInfoScope::AddMenuItems(LPDATAOBJECT lpDataObject, LPCONTEXTMENUCALLBACK lpCallback, long *pInsertionAllowed) { TRACE(_T("CSystemInfoScope::AddMenuItems\n")); ASSERT(lpDataObject != NULL); ASSERT(lpCallback != NULL); ASSERT(pInsertionAllowed != NULL); if (lpDataObject == NULL || lpCallback == NULL || pInsertionAllowed == NULL) return E_POINTER; HRESULT hr = S_OK; // Note - snap-ins need to look at the data object and determine in what // context menu items need to be added. They must also observe the // insertion allowed flags to see what items can be added. AFX_MANAGE_STATE(AfxGetStaticModuleState()); // CHECK: Will this ever work differently for multiselect? do { // Save Report and Save File are both task items and context items. if (*pInsertionAllowed & CCM_INSERTIONALLOWED_TOP) { hr = AddToTopMenu(lpCallback, IDS_SAVEREPORTMENUNAME, IDS_SAVEREPORTSTATUS, IDM_SAVEREPORT); if (FAILED(hr)) break; hr = AddToTopMenu(lpCallback, IDS_SAVEFILEMENUNAME, IDS_SAVEFILESTATUS, IDM_SAVEFILE); if (FAILED(hr)) break; hr = AddToTopMenu(lpCallback, IDS_FINDMENUNAME, IDS_FINDSTATUS, IDM_FIND); if (FAILED(hr)) break; } if (*pInsertionAllowed & CCM_INSERTIONALLOWED_TASK) { hr = AddToTaskMenu(lpCallback, IDS_FINDMENUNAME, IDS_FINDSTATUS, IDM_TASK_FIND); if (FAILED(hr)) break; // Don't add the Open File menu item for Extension snap-ins. if (IsPrimaryImpl()) { hr = AddToTaskMenu(lpCallback, IDS_OPENFILEMENUNAME, IDS_OPENFILESTATUS, IDM_TASK_OPENFILE); if (FAILED(hr)) break; CDataSource * pCurrentSource = pSource(); if (pCurrentSource && pCurrentSource->GetType() != CDataSource::GATHERER) hr = AddToTaskMenu(lpCallback, IDS_CLOSEFILEMENUNAME, IDS_CLOSEFILEMENUSTATUS, IDM_TASK_CLOSE); else hr = AddToMenu(lpCallback, IDS_CLOSEFILEMENUNAME, IDS_CLOSEFILEMENUSTATUS, IDM_TASK_CLOSE, CCM_INSERTIONPOINTID_PRIMARY_TASK, MF_GRAYED); if (FAILED(hr)) break; } hr = AddToTaskMenu(lpCallback, IDS_SAVEFILEMENUNAME, IDS_SAVEFILESTATUS, IDM_TASK_SAVEFILE); if (FAILED(hr)) break; hr = AddToTaskMenu(lpCallback, IDS_SAVEREPORTMENUNAME, IDS_SAVEREPORTSTATUS, IDM_TASK_SAVEREPORT); if (FAILED(hr)) break; // If a CAB file has been opened, add the view CAB contents menu item. Also // take this opportunity to create the tool to view the CAB contents, if it // has not already been created. if (fCABOpened) { if (m_pViewCABTool == NULL) m_pViewCABTool = new CCabTool(this); if (m_pViewCABTool != NULL) { hr = AddToTaskMenu(lpCallback, IDS_CAB_NAME, IDS_CAB_DESCRIPTION, IDM_TASK_VIEWCAB); if (FAILED(hr)) break; } } } if (*pInsertionAllowed & CCM_INSERTIONALLOWED_VIEW) { hr = AddToViewMenu(lpCallback, IDS_ADVANCEDVIEWNAME, IDS_ADVANCEDSTATUS, IDM_VIEW_ADVANCED, m_AdvancedFlags); if (FAILED(hr)) break; hr = AddToViewMenu(lpCallback, IDS_BASICVIEWNAME, IDS_BASICSTATUS, IDM_VIEW_BASIC, m_BasicFlags); if (FAILED(hr)) break; } } while (FALSE); return hr; } /* * DisplayFileError - Show a message box with an error message taken from * the exception thrown. * * History: a-jsari 2/13/98 Initial version */ static inline void DisplayError(HRESULT hr, const CString &strFileName) { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); USES_CONVERSION; CString strFileError, strTitle; switch (hr) { case STG_E_PATHNOTFOUND: strFileError.Format(IDS_BAD_PATH, (LPCTSTR)strFileName); break; case STG_E_TOOMANYOPENFILES: VERIFY(strFileError.LoadString(IDS_TOOMANYOPENFILES)); break; case STG_E_ACCESSDENIED: strFileError.Format(IDS_ACCESS_DENIED, (LPCTSTR)strFileName); break; case STG_E_SHAREVIOLATION: strFileError.Format(IDS_SHARING_VIOLATION, (LPCTSTR)strFileName); break; case STG_E_WRITEFAULT: VERIFY(strFileError.LoadString(IDS_HARDIO)); break; case STG_E_MEDIUMFULL: strFileError.Format(IDS_DISK_FULL, (LPCTSTR)strFileName); break; default: VERIFY(strFileError.LoadString(IDS_UNKNOWN_FILE)); break; } strTitle.LoadString(IDS_DESCRIPTION); ::MessageBox( ::AfxGetMainWnd()->GetSafeHwnd(), strFileError, strTitle, MB_OK); } /* * SaveReport - Create the save report save file dialog and save the selected file. * * History: a-jsari 12/8/97 Initial version */ void CSystemInfoScope::SaveReport() { if (m_prdReport->DoModal() == IDOK) { CWaitCursor DoWaitCursor; if (m_pLastSystemInfo) m_pLastSystemInfo->SetStatusText(IDS_REFRESHING_MSG); HRESULT hr = pSource()->ReportWrite(m_prdReport->GetPathName(), m_pfLast); if (m_pLastSystemInfo) m_pLastSystemInfo->SetStatusText(_T("")); if (FAILED(hr)) { ::DisplayError(hr, m_prdReport->GetPathName()); } } } /* * SaveFile - Create the save file dialog and save the selected file. * * History: a-jsari 12/8/97 Initial version */ void CSystemInfoScope::SaveFile() { if (m_prdSave->DoModal() == IDOK) { CWaitCursor DoWaitCursor; if (m_pLastSystemInfo) m_pLastSystemInfo->SetStatusText(IDS_REFRESHING_MSG); HRESULT hr = pSource()->SaveFile(m_prdSave->GetPathName()); if (m_pLastSystemInfo) m_pLastSystemInfo->SetStatusText(_T("")); if (FAILED(hr)) ::DisplayError(hr, m_prdSave->GetPathName()); } } /* * PrintReport - Create the print dialog and do the print. * * History: a-jsari 12/8/97 Initial version */ void CSystemInfoScope::PrintReport() { HWND hWindow; ASSERT(m_pConsole != NULL); HRESULT hr = m_pConsole->GetMainWindow(&hWindow); ASSERT(hr == S_OK); if (FAILED(hr)) return; CMSInfoPrintDialog * prdPrint = new CMSInfoPrintDialog(hWindow); ASSERT(prdPrint != NULL); if (prdPrint == NULL) ::AfxThrowMemoryException(); prdPrint->m_pd.nToPage = prdPrint->m_pd.nFromPage = prdPrint->m_pd.nMinPage = 1; prdPrint->m_pd.nMaxPage = 1000; if (prdPrint->DoModal() == IDOK) { CWaitCursor DoWaitCursor; pSource()->RefreshPrintData(prdPrint, m_pfLast); pSource()->PrintReport(prdPrint, m_pfLast); } delete prdPrint; } /* * DoFind - Display the Find dialog * * History: a-jsari 12/8/97 Initial version. */ void CSystemInfoScope::DoFind() { const UINT STACK_SIZE_PARENT = 0; // const DWORD FLAGS_IMMEDIATE_START = 0; const LPSECURITY_ATTRIBUTES NO_ATTRIBUTES = NULL; if (m_pthdFind == NULL) { m_pthdFind = dynamic_cast(::AfxBeginThread(RUNTIME_CLASS(CFindThread), THREAD_PRIORITY_NORMAL, STACK_SIZE_PARENT, CREATE_SUSPENDED, NO_ATTRIBUTES)); m_pthdFind->SetScope(this); HWND hwndMMC; if (pConsole() == NULL || FAILED(pConsole()->GetMainWindow(&hwndMMC))) hwndMMC = NULL; m_pthdFind->SetParent(m_hwndFind, hwndMMC); m_pthdFind->ResumeThread(); } else m_pthdFind->Activate(); } /* * ExecuteFind - This function is actually called via a hook into * MMC's main window's WindowProc, by a message posted by * CFindThread's Find button function.. * * The Refresh and Find functions may be interrupted by the Find * window's UI thread, and m_pthdFind may be deleted while this * function is executing. * * Instead of taking parameters, this function calls back to our * existing find thread to get its search string and last * search string. This simplifies the PostMessage call, but * would need to change if we ever decide to have multiple * find windows per IComponentData...unlikely. * * History: a-jsari 1/22/98 Initial version. */ void CSystemInfoScope::ExecuteFind(long lFindState) { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); CWaitCursor DoWaitCursor; // Display the hourglass CString strFindData; // Our current search string. // The restricted context within which our search occurs. static CFolder *pfContext; // Check m_pthdFind since there's a remote possibility of thread pointer // invalidation (by the user closing the Find dialog while the // find is running). if (m_pthdFind == NULL) return; strFindData = m_pthdFind->FindString(); CDataSource *pdsSearch = pSource(); do { if ((lFindState & CDataSource::FIND_OPTION_REPEAT_SEARCH) == 0) { // Only refresh the first time we search for a given string. // Also, only refresh if we are searching more than category names. BOOL fRefreshResult = TRUE; if ((lFindState & CDataSource::FIND_OPTION_CATEGORY_ONLY) == 0) { // If we are only searching in one category, only refresh that // category. if ((lFindState & CDataSource::FIND_OPTION_ONE_CATEGORY) != 0) { if (pdsSearch->GetType() == CDataSource::GATHERER) { CWBEMDataSource * pWBEMSource = reinterpret_cast(pSource()); if (pWBEMSource && pWBEMSource->m_pThreadRefresh) pWBEMSource->m_pThreadRefresh->RefreshFolder(m_pfLast, TRUE, FALSE); } } else fRefreshResult = pdsSearch->Refresh(TRUE); } if (!fRefreshResult) { if (m_pthdFind != NULL) m_pthdFind->ResetSearch(); break; } // Set our context the first time we start a restricted search. if ((lFindState & CDataSource::FIND_OPTION_ONE_CATEGORY) != 0) pfContext = m_pfLast; else pfContext = NULL; } else if ((lFindState & CDataSource::FIND_OPTION_ONE_CATEGORY) != 0) { // If we set a restricted search inside an already started search // restrict our context. // CHECK: iDepth??? if (pfContext == NULL) pfContext = m_pfLast; } if (pdsSearch->Find(strFindData, lFindState) == FALSE) { // Failed find means no match or halted execution. // If the user stoppped the find, no message necessary. if (!pdsSearch->FindStopped()) { CString strError; // Error display CString strTitle; // Error title. int nReturn; // Test the thread because the find window might vanish in the middle // of our find operation. if (m_pthdFind != NULL) m_pthdFind->FindComplete(); if (m_pthdFind != NULL) m_pthdFind->ResetSearch(); // If we're repeating a search, no more matches otherwise, not found. if ((lFindState & CDataSource::FIND_OPTION_REPEAT_SEARCH) == 0) { strError.Format(IDS_DATANOTFOUND, (LPCTSTR)strFindData); } else strError.Format(IDS_NOMOREMATCHES, (LPCTSTR)strFindData); VERIFY(strTitle.LoadString(IDS_FIND_TITLE)); pConsole()->MessageBox((LPCTSTR)strError, (LPCTSTR)strTitle, MB_TOPMOST|MB_SETFOREGROUND, &nReturn); // If we are restricting our search, reset the selected folder to the // beginning of our restricted search. if ((lFindState & CDataSource::FIND_OPTION_ONE_CATEGORY) != 0) SetSelectedFolder(pfContext); // We've already completed the find, don't drop out. return; } } else { SelectItem(pdsSearch->m_strPath, pdsSearch->m_iLine); } } while (FALSE); // The find window might vanish in the middle of our find operation. if (m_pthdFind != NULL) m_pthdFind->FindComplete(); } /* * MainThreadStopFind - Stops a find operation running in an alternate thread. * * History: a-jsari 1/22/98 Initial version. */ void CSystemInfoScope::StopFind() { // This will be called by an alternate UI thread. pSource()->StopSearch(); } /* * Refresh - Refresh the data, and redraw the current node if applicable. * * History: a-jsari 2/25/98 Initial version */ void CSystemInfoScope::Refresh(CFolder * pfSelected, CSystemInfo * pSystemInfo) { CWaitCursor DoWaitCursor; if (pfSelected && pSource() && pSource()->GetType() == CDataSource::GATHERER) { CWBEMDataSource * pWBEMSource = reinterpret_cast(pSource()); if (pWBEMSource && pWBEMSource->m_pThreadRefresh) { if (pSystemInfo) pWBEMSource->m_pThreadRefresh->RefreshFolderAsync(pfSelected, pSystemInfo, TRUE, FALSE); else pWBEMSource->m_pThreadRefresh->RefreshFolder(pfSelected, TRUE, FALSE); } } else pSource()->Refresh(); if (pfSelected != NULL) { CString strName; int nLine = 0; pfSelected->InternalName(strName); SelectItem(strName, nLine); } } /* * CloseFindWindow - Function to be called when the find window closes. * * History: a-jsari 1/22/97 Initial version. */ void CSystemInfoScope::CloseFindWindow() { // Don't delete m_pthdFind; it will delete itself. m_pthdFind = NULL; } /* * OpenFile - Create the open file file dialog and open the resultant file * * History: a-jsari 12/8/97 Initial version */ void CSystemInfoScope::OpenFile() { const long DONT_USE_LAST_FOLDER = 1; if (m_prdOpen->DoModal() == IDOK) { CWaitCursor DoWaitCursor; CDataSource *pSource = NULL; try { pSource = CBufferDataSource::CreateDataSourceFromFile(m_prdOpen->GetPathName()); } catch (...) { delete pSource; pSource = NULL; } if (pSource != NULL) { SetSource(pSource); InitializeView(); } pConsole()->UpdateAllViews(0, DONT_USE_LAST_FOLDER, 0L); // Reset the selected folder (it's no longer valid with the new tree). SetSelectedFolder(NULL); HSCOPEITEM hsiNode = NULL; if (m_pmapCategories && m_pmapCategories->ScopeFromView(NULL, hsiNode)) pConsole()->SelectScopeItem(hsiNode); } } //----------------------------------------------------------------------------- // Close the currently opened file. //----------------------------------------------------------------------------- void CSystemInfoScope::CloseFile() { CDataSource * pDataSource = pSource(); if (pDataSource && pDataSource->GetType() == CDataSource::GATHERER) return; try { (*m_pstrMachineName) = _T(""); pDataSource = new CWBEMDataSource(NULL); } catch (CUserException *) { m_bInitializedCD = false; return; } if (pDataSource != NULL) { SetSource(pDataSource); InitializeView(); } const long DONT_USE_LAST_FOLDER = 1; pConsole()->UpdateAllViews(0, DONT_USE_LAST_FOLDER, 0L); // Reset the selected folder (it's no longer valid with the new tree). SetSelectedFolder(NULL); } /* * Command - Call the function represented by nCommandID. * * History: a-jsari 9/15/97 Initial version * * Note: This function currently takes no notice of the context represented * by pdoContext. */ STDMETHODIMP CSystemInfoScope::Command(long nCommandID, LPDATAOBJECT) { HRESULT hr = S_OK; TRACE(_T("CSystemInfoScope::Command(%lx)\n"), nCommandID); AFX_MANAGE_STATE(::AfxGetStaticModuleState()); // For any of these commands, we want to cancel an async category refresh // is there is one in progress. if (pSource() && pSource()->GetType() == CDataSource::GATHERER) { CWBEMDataSource * pWBEMSource = reinterpret_cast(pSource()); if (pWBEMSource && pWBEMSource->m_pThreadRefresh) { CWaitCursor waitcursor; pWBEMSource->m_pThreadRefresh->WaitForRefresh(); } } try { switch (nCommandID) { case IDM_SAVEREPORT: case IDM_TASK_SAVEREPORT: if (msiLog.IsLogging()) msiLog.WriteLog(CMSInfoLog::MENU, _T("MENU \"Save As Text\"\r\n")); SaveReport(); break; case IDM_SAVEFILE: case IDM_TASK_SAVEFILE: if (msiLog.IsLogging()) msiLog.WriteLog(CMSInfoLog::MENU, _T("MENU \"Save NFO\"\r\n")); SaveFile(); break; case IDM_TASK_FIND: case IDM_FIND: if (msiLog.IsLogging()) msiLog.WriteLog(CMSInfoLog::MENU, _T("MENU \"Find\"\r\n")); DoFind(); break; case IDM_TASK_OPENFILE: if (msiLog.IsLogging()) msiLog.WriteLog(CMSInfoLog::MENU, _T("MENU \"Open NFO\"\r\n")); OpenFile(); break; case IDM_TASK_VIEWCAB: if (msiLog.IsLogging()) msiLog.WriteLog(CMSInfoLog::MENU, _T("MENU \"View CAB\"\r\n")); if (m_pViewCABTool) m_pViewCABTool->RunTool(); break; case IDM_VIEW_ADVANCED: if (msiLog.IsLogging()) msiLog.WriteLog(CMSInfoLog::MENU, _T("MENU \"Set View ADVANCED\"\r\n")); SetView(ADVANCED, TRUE); break; case IDM_VIEW_BASIC: if (msiLog.IsLogging()) msiLog.WriteLog(CMSInfoLog::MENU, _T("MENU \"Set View BASIC\"\r\n")); SetView(BASIC, TRUE); break; case IDM_TASK_CLOSE: if (msiLog.IsLogging()) msiLog.WriteLog(CMSInfoLog::MENU, _T("MENU \"Close NFO\"\r\n")); CloseFile(); break; case ~0: // We get this result when we back arrow on the taskpad. break; default: ASSERT(FALSE); break; } } catch (...) { ASSERT(FALSE); hr = HRESULT_FROM_WIN32(::GetLastError()); } return hr; } /* * CreatePropertyPages - Create an instance of our property pages and attach them * to MMC's property sheet. * * History: a-jsari 9/17/97 Initial version */ STDMETHODIMP CSystemInfoScope::CreatePropertyPages(LPPROPERTYSHEETCALLBACK pProvider, LONG_PTR handle, LPDATAOBJECT pDataObject) { ASSERT(pProvider != NULL); ASSERT(pDataObject != NULL); TRACE(_T("CSystemInfoScope::CreatePropertyPages\n")); if (pProvider == NULL || pDataObject == NULL) return E_INVALIDARG; // Special code goes here if we're used as an extension. HRESULT hResult; do { CChooseMachinePropPage *pPropChoose; HPROPSHEETPAGE hGeneralPage; BOOL fOverride; AFX_MANAGE_STATE(::AfxGetStaticModuleState()); // Deleted automatically. pPropChoose = new CChooseMachinePropPage(IDD_CHOOSER_CHOOSE_MACHINE); if (pPropChoose == NULL) { ::MMCFreeNotifyHandle(handle); ::AfxThrowMemoryException(); } // Save the current machine name, in case the one selected in the property // page is not any good. m_strLastMachineName = *m_pstrMachineName; pPropChoose->SetHandle(handle); pPropChoose->SetOutputBuffers(m_pstrMachineName, &fOverride, m_pstrOverrideName); hGeneralPage = ::CreatePropertySheetPage(&pPropChoose->m_psp); if (!hGeneralPage) { hResult = E_FAIL; break; } hResult = pProvider->AddPage(hGeneralPage); ASSERT(SUCCEEDED(hResult)); } while (FALSE); if (FAILED(hResult)) return hResult; return S_OK; } /* * QueryPagesFor - Return S_OK, informing MMC that we have property sheets available. * * History: a-jsari 9/17/97 Initial version */ STDMETHODIMP CSystemInfoScope::QueryPagesFor(LPDATAOBJECT pDataObject) { TRACE(_T("CSystemInfoScope::QueryPagesFor\n")); ASSERT(pDataObject != NULL); if (pDataObject == NULL) return E_POINTER; // If we are being loaded as an extension, don't display property pages. // We do this because the base snap-in is responsible for the connected // machine, not us. if (!IsPrimaryImpl()) return S_FALSE; #if 0 // Not sure why I did this. // If the machine name is already set, don't display the property page. if (m_pstrMachineName && m_pstrMachineName->GetLength() > 0) return S_FALSE; #endif return S_OK; } /* * Load - Load our state from the stream provided. * * History: a-jsari 11/5/97 Initial version */ STDMETHODIMP CSystemInfoScope::Load(IStream *pStm) { CDataSource *pSource; AFX_MANAGE_STATE(AfxGetStaticModuleState()); TRACE(_T("CSystemInfoScope::Load\n")); ASSERT(pStm != NULL); if (pStm == NULL) return E_POINTER; ClearDirty(); // If our command-line parameters have specified a source, don't replace it. if (!m_fViewUninitialized) { if (m_strDeferredLoad.IsEmpty()) pSource = CDataSource::CreateFromIStream(pStm); else { pSource = CBufferDataSource::CreateDataSourceFromFile(m_strDeferredLoad); if (pSource) { const long DONT_USE_LAST_FOLDER = 1; SetSource(pSource, TRUE); pConsole()->UpdateAllViews(0, DONT_USE_LAST_FOLDER, 0L); SetSelectedFolder(NULL); return S_OK; } else m_strDeferredLoad.Empty(); } if (pSource == NULL) InitializeSource(); else { if (!m_strDeferredCategories.IsEmpty()) pSource->SetCategories(m_strDeferredCategories); SetSource(pSource, TRUE); if (!m_strDeferredMachine.IsEmpty()) SetMachine(m_strDeferredMachine); } } return S_OK; } /* * Save - Save our state to the stream provided. * * History: a-jsari 11/5/97 Initial version */ STDMETHODIMP CSystemInfoScope::Save(IStream *pStm, BOOL fClearDirty) { TRACE(_T("CSystemInfoScope::Save\n")); ASSERT(pStm != NULL); if (pStm == NULL) return E_POINTER; if (fClearDirty) ClearDirty(); HRESULT hResult = S_OK; // If there is a source, let it save its state to the stream. Otherwise, // save the state for the default source (GATHERER with no machine name). if (m_pSource) hResult = m_pSource->Save(pStm); else { unsigned wValue; ULONG cWrite; wValue = CDataSource::GATHERER; hResult = pStm->Write(&wValue, sizeof(wValue), &cWrite); if (SUCCEEDED(hResult)) { wValue = 0; hResult = pStm->Write(&wValue, sizeof(wValue), &cWrite); } } return hResult; } /* * IsDirty - Return our Dirty status, to see if a save would be beneficial. * * History: a-jsari 11/5/97 Initial version */ STDMETHODIMP CSystemInfoScope::IsDirty() { TRACE(_T("CSystemInfoScope::IsDirty\n")); return ObjectIsDirty() ? S_OK : S_FALSE; } /* * GetClassID - Return our Component Object Class ID. * * History: a-jsari 11/5/97 Initial version */ STDMETHODIMP CSystemInfoScope::GetClassID(CLSID *pClassID) { TRACE(_T("CSystemInfoScope::GetClassID\n")); ASSERT(pClassID != NULL); if (pClassID == NULL) return E_POINTER; *pClassID = GetCoClassID(); return S_OK; } /* * GetSizeMax - Return the maximum size consumed in a stream by our * persistance. * * History: a-jsari 11/5/97 Initial version */ STDMETHODIMP CSystemInfoScope::GetSizeMax(ULARGE_INTEGER *pcbSize) { TRACE(_T("CSystemInfoScope::GetSizeMax\n")); ASSERT(pcbSize != NULL); if (pcbSize == NULL) return E_POINTER; pcbSize->LowPart = 2 * sizeof(unsigned) + MAX_PATH; pcbSize->HighPart = 0; return E_NOTIMPL; } //----------------------------------------------------------------------------- // Implementation of GetHelpTopic, which supplies the location of our help // file to MMC for merging into the combined help. Adapted from example in // the MMC help file. //----------------------------------------------------------------------------- STDMETHODIMP CSystemInfoScope::GetHelpTopic(LPOLESTR* lpCompiledHelpFile) { if (lpCompiledHelpFile == NULL) return E_POINTER; // Get the name of our help file, and prepend the help directory. // Actually, although the MMC documentation said that a full path is // required, we can just put the file name, since it is being stored in // the system help directory (HTMLHelp will find it there). AFX_MANAGE_STATE(AfxGetStaticModuleState()); CString strHelp; strHelp.LoadString(IDS_MSINFO_HELP_FILE); // NOTE: It looks like HTMLHelp has been changed, and will not locate // the help file in the standard help directory. So we'll need to get // the full path to the help file. (JCM, 7/1/98) TCHAR szFilePath[MAX_PATH]; DWORD dwCnt = ExpandEnvironmentStrings(CString(_T("%WINDIR%\\help\\")) + strHelp, szFilePath, MAX_PATH); ASSERT(dwCnt != 0); if (dwCnt != 0) strHelp = szFilePath; // Allocate the string to hold the help file path. MMC will be responsible // for deallocating this buffer later. *lpCompiledHelpFile = reinterpret_cast(CoTaskMemAlloc((strHelp.GetLength() + 1)* sizeof(wchar_t))); if (*lpCompiledHelpFile == NULL) return E_OUTOFMEMORY; // Copy from the path string to the buffer and return success. USES_CONVERSION; wcscpy(*lpCompiledHelpFile, T2OLE((LPTSTR)(LPCTSTR)strHelp)); return S_OK; } /* * SetMachine - Set our internal machine name. * * History: a-jsari 1/16/98 Initial version. */ BOOL CSystemInfoScope::SetMachine(const CString &strMachine) { BOOL fReturn = FALSE; *m_pstrMachineName = strMachine; ASSERT(pSource() != NULL); if (pSource() && pSource()->GetType() == CDataSource::GATHERER) { // Changing the machine name is only meaningful if we are connected to a WBEM // data source. CWBEMDataSource * pWBEMSource = reinterpret_cast(pSource()); fReturn = pWBEMSource->SetMachineName(*m_pstrMachineName); if (fReturn) { // If we succeeded, reset the root node text. Also, there's a hack in place // to keep SetSource from running twice during initialization. Which requires // another hack here to make it run - change the flag it uses to keep track // of the last call so that this call is different. I hate this. m_fSetSourcePreLaunch = TRUE; SetSource(pSource(), FALSE); } else { } } return fReturn; } /* * SetView - Set the view on the data. * * History: a-jsari 12/3/97 Initial version. */ void CSystemInfoScope::SetView(enum DataComplexity Complexity, BOOL fViewInitialized) { CDataSource *pDataSource = pSource(); switch (Complexity) { case BASIC: m_BasicFlags = MF_CHECKED; m_AdvancedFlags = 0L; break; case ADVANCED: m_BasicFlags = 0L; m_AdvancedFlags = MF_CHECKED; break; } if (pDataSource != NULL) VERIFY(pDataSource->SetDataComplexity(Complexity)); if (fViewInitialized) { CRegKey crkView; long lResult; pConsole()->UpdateAllViews(NULL, 0, 0); lResult = crkView.Create(HKEY_CURRENT_USER, cszViewKey); if (lResult != ERROR_SUCCESS) return; switch (Complexity) { case ADVANCED: lResult = crkView.SetValue(cszAdvancedValue, cszViewValue); break; case BASIC: lResult = crkView.SetValue(cszBasicValue, cszViewValue); break; } } } /* * SetSource - Remove the old data source, and replace it with a new one. * * History: a-jsari 9/25/97 Initial version. */ void CSystemInfoScope::SetSource(CDataSource *pNewSource, BOOL fPreLaunch) { HSCOPEITEM hsiRoot; HRESULT hr; BOOL fUIOK = FALSE; // We have some variables in this class to make sure that SetSource // isn't called twice during initialization with the same parameters. if (m_pSetSourceSource == pNewSource && m_fSetSourcePreLaunch == fPreLaunch) return; m_pSetSourceSource = pNewSource; m_fSetSourcePreLaunch = fPreLaunch; // If fPreLaunch is TRUE, we have not yet created our UI items. if (fPreLaunch == FALSE) { if (m_pmapCategories == NULL) m_pmapCategories = new CScopeItemMap; // Select the root node so we can remove all its children. if (m_pmapCategories && !m_pmapCategories->ScopeFromView(NULL, hsiRoot)) return; hr = pConsole()->SelectScopeItem(hsiRoot); ASSERT(SUCCEEDED(hr)); if (FAILED(hr)) return; // Before we delete the item, we want to make sure that it's actually // been added. Try a call to GetItem to make sure it's there. SCOPEDATAITEM item; item.mask = SDI_CHILDREN; item.ID = hsiRoot; fUIOK = SUCCEEDED(pScope()->GetItem(&item)); if (fUIOK) { m_fInternalDelete = TRUE; hr = pScope()->DeleteItem(hsiRoot, FALSE); m_fInternalDelete = FALSE; if (pSource() && pSource()->GetType() == CDataSource::GATHERER) ((CWBEMDataSource *) pSource())->ResetCategoryRefresh(); ASSERT(SUCCEEDED(hr)); if (FAILED(hr)) return; if (m_pSaveUnknown) { m_pstrMachineName = new CString; m_pstrOverrideName = new CString; Initialize(m_pSaveUnknown); m_pSaveUnknown = NULL; } } // Remove our memory of data items. m_pmapCategories->Clear(); } // Don't delete our pointer if we are resetting the same pointer. if (m_pSource != pNewSource) { if (!m_pSource) m_pSource = pNewSource; else { DataComplexity Complexity; Complexity = m_pSource->m_Complexity; delete m_pSource; m_pSource = pNewSource; m_pSource->SetDataComplexity(Complexity); } } if (fPreLaunch == FALSE) { CFolder *pFolder; SCOPEDATAITEM sdiRoot; CString strNodeName; USES_CONVERSION; ::memset(&sdiRoot, 0, sizeof(sdiRoot)); // Identify the node. sdiRoot.ID = hsiRoot; sdiRoot.mask = SDI_STR; m_pSource->GetNodeName(strNodeName); sdiRoot.displayname = T2OLE(const_cast((LPCTSTR)strNodeName)); hr = pScope()->SetItem(&sdiRoot); ASSERT(SUCCEEDED(hr)); hr = AddRoot(hsiRoot, &pFolder); ASSERT(SUCCEEDED(hr)); if (FAILED(hr)) return; if (fUIOK) { hr = ScopeEnumerate(hsiRoot, pFolder); ASSERT(SUCCEEDED(hr)); if (FAILED(hr)) return; } } } /* * AddRoot - Insert a CCategoryObject of the root folder into our Category map. * * History: a-jsari 11/20/97 Initial version */ HRESULT CSystemInfoScope::AddRoot(HSCOPEITEM hsiRoot, CFolder **ppFolder) { ASSERT(ppFolder != NULL); (*ppFolder) = pRootCategory(); ASSERT(*ppFolder != NULL); if (*ppFolder == NULL) return E_FAIL; // This object gets deleted in the mapCategories destructor CViewObject *pvoData = new CCategoryObject(*ppFolder); if (pvoData == NULL) ::AfxThrowMemoryException(); m_pmapCategories->InsertRoot(pvoData, hsiRoot); return S_OK; } /* * GetNamedChildFolder - Update strCategory to add the next backslash- * delimited path element from strPath. * * History: a-jsari 12/17/97 Initial version */ static inline void GetNamedChildFolder( CString &strCategory, const CString &strPath) { int iString; CString strSubCategory; CString strName; // Remove the Category prefix from the path iString = strPath.Find(strCategory); ASSERT(iString == 0); strSubCategory = strPath.Mid(iString + strCategory.GetLength() + 1); // Remove the trailing categories // (We'll deal with your rebel friends soon enough). iString = strSubCategory.Find((TCHAR) '\\'); if (iString != -1) strSubCategory = strSubCategory.Left(iString); // Update the category to add the current category strCategory += _T("\\"); strCategory += strSubCategory; #if 0 // Turned out not to need this code. // Find the Sub-folder with the name of the Sub-category. pfNext = pfNext->GetChildNode(); do { pfNext->GetName(szName); if (szName == szSubCategory) break; pfNext = pfNext->GetNextNode(); } while (pfNext != NULL); ASSERT(pfNext != NULL); #endif } //----------------------------------------------------------------------------- // If were are currently refreshing, hang here until it's done. //----------------------------------------------------------------------------- void CSystemInfoScope::WaitForRefresh() { if (pSource() && pSource()->GetType() == CDataSource::GATHERER) { CWBEMDataSource * pWBEMSource = reinterpret_cast(pSource()); if (pWBEMSource && pWBEMSource->m_pThreadRefresh) pWBEMSource->m_pThreadRefresh->WaitForRefresh(); } } //----------------------------------------------------------------------------- // Is an asynchronous refresh currently in progress? //----------------------------------------------------------------------------- BOOL CSystemInfoScope::InRefresh() { if (pSource() && pSource()->GetType() == CDataSource::GATHERER) { CWBEMDataSource * pWBEMSource = reinterpret_cast(pSource()); if (pWBEMSource && pWBEMSource->m_pThreadRefresh) return pWBEMSource->m_pThreadRefresh->IsRefreshing(); } return FALSE; } /* * SelectItem - Select the item pointed to by szPath * * History: a-jsari 12/11/97 Initial version */ BOOL CSystemInfoScope::SelectItem(const CString &strPath, int iLine) { HSCOPEITEM hsiNode; HRESULT hr; // Get the named node from our internal list. if (m_pmapCategories->ScopeFromName(strPath, hsiNode)) { CFolder *pFolder = m_pmapCategories->CategoryFromScope(hsiNode); pFolder->SetSelectedItem(iLine); hr = pConsole()->SelectScopeItem(hsiNode); ASSERT(hr == S_OK); if (m_pLastSystemInfo) m_pLastSystemInfo->SelectLine(iLine); if (FAILED(hr)) return FALSE; } else { CString strCategory = strPath; int iEnumerations = 0; int iString; // If the category isn't in our list, we need to enumerate all nodes // leading up to it. while (!m_pmapCategories->ScopeFromName(strCategory, hsiNode)) { ++iEnumerations; // Strip the last Category off the Path. iString = strCategory.ReverseFind((TCHAR) '\\'); if (iString == -1) { // If this fails, no nodes have been yet enumerated. if (!m_pmapCategories->ScopeFromName(_T(""), hsiNode)) { // --iEnumerations; break; } } else { strCategory = strCategory.Left(iString); } } HRESULT hr; // Enumerate all unenumerated nodes. #if 0 // Commented out because InsertItem in ScopeEnumerate fails inexplicably. CFolder *pfCurrent = pmapCategories->CategoryFromScope(hsiNode); #endif while (iEnumerations--) { #if 0 hr = ScopeEnumerate(hsiNode, pfCurrent); #else hr = pConsole()->SelectScopeItem(hsiNode); #endif ASSERT(hr == S_OK); if (FAILED(hr)) return FALSE; GetNamedChildFolder(strCategory, strPath); if (!m_pmapCategories->ScopeFromName(strCategory, hsiNode)) // The scope item we are searching for cannot be enumerated (The // attempt to do so in SelectScopeItem has failed.) This can happen // when we are connected to a remote computer which can't be accessed. return FALSE; } CFolder *pFolder = m_pmapCategories->CategoryFromScope(hsiNode); // Using this method of selecting a line because MMC won't // just allow me to call GetItem. pFolder->SetSelectedItem(iLine); hr = pConsole()->SelectScopeItem(hsiNode); if (m_pLastSystemInfo) m_pLastSystemInfo->SelectLine(iLine); ASSERT(hr == S_OK); if (FAILED(hr)) return FALSE; } return TRUE; } /* * ScopeEnumerate - Insert all categories of the given pFolder as namespace children * of hsiNode. * * History: a-jsari 11/20/97 Initial version */ HRESULT CSystemInfoScope::ScopeEnumerate(HSCOPEITEM hsiNode, CFolder *pFolder) { HRESULT hr = S_OK; ASSERT(pFolder); if (pFolder == NULL) return hr; SCOPEDATAITEM sdiNode; sdiNode.mask = SDI_STR | SDI_PARAM | SDI_PARENT; sdiNode.displayname = MMC_CALLBACK; sdiNode.relativeID = hsiNode; // If GetChildNode returned a NULL pointer, and this is the root node, // then some sort of error must have occurred. CFolder * pfolIterator = pFolder->GetChildNode(); if (pfolIterator == NULL && pFolder->GetParentNode() == NULL) { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); if (m_pSource && m_pSource->GetType() == CDataSource::GATHERER) if (((CWBEMDataSource *)m_pSource)->m_pGatherer) { DWORD dwError = ((CWBEMDataSource *)m_pSource)->m_pGatherer->GetLastError(); if (dwError) DisplayGatherError(dwError, (LPCTSTR)((CWBEMDataSource *)m_pSource)->m_strMachineName); } } while (pfolIterator) { // This object gets deleted inthe mapCategories destructor. CViewObject * pCategory = new CCategoryObject(pfolIterator); ASSERT(pCategory != NULL); if (pCategory == NULL) return E_OUTOFMEMORY; // If there are no children, modify the node we're inserting so // we don't get a '+' sign next to the folder. if (pfolIterator->GetChildNode() == NULL) { sdiNode.cChildren = 0; sdiNode.mask |= SDI_CHILDREN; } else { sdiNode.cChildren = 1; sdiNode.mask &= ~SDI_CHILDREN; } sdiNode.lParam = reinterpret_cast(pCategory); hr = pScope()->InsertItem(&sdiNode); ASSERT(hr == S_OK); if (FAILED(hr)) { delete pCategory; break; } pfolIterator->m_hsi = sdiNode.ID; m_pmapCategories->Insert(pCategory, sdiNode.ID); pfolIterator = pfolIterator->GetNextNode(); } return hr; } /* * AddExtensionRoot - If the snapin is loaded as an extension, create the root node. * * History: a-jsari 1/6/97 Initial version */ HRESULT CSystemInfoScope::AddExtensionRoot(HSCOPEITEM &hsiNode, CFolder **pFolder) { SCOPEDATAITEM sdiNode; HRESULT hrReturn; CViewObject *pView; ::memset(&sdiNode, 0, sizeof(sdiNode)); sdiNode.mask = SDI_STR | SDI_PARAM | SDI_PARENT | SDI_IMAGE | SDI_OPENIMAGE; sdiNode.nImage = 0; sdiNode.nOpenImage = 0; sdiNode.displayname = MMC_CALLBACK; sdiNode.relativeID = hsiNode; pView = new CExtensionRootObject(pRootCategory()); ASSERT(pView != NULL); if (pView == NULL) ::AfxThrowMemoryException(); m_RootCookie = sdiNode.lParam = reinterpret_cast(pView); hrReturn = pScope()->InsertItem(&sdiNode); hsiNode = sdiNode.ID; m_pmapCategories->InsertRoot(pView, hsiNode); *pFolder = pRootCategory(); return hrReturn; } /* * OnExpand - If fExpand is TRUE, expand the item pointed to by pDataObject, * otherwise contract it. If expanding, enumerate children. * * History: a-jsari 9/25/97 Initial version */ HRESULT CSystemInfoScope::OnExpand(LPDATAOBJECT pDataObject, LPARAM fExpand, HSCOPEITEM hsiNode) { CFolder *pfolSelection; HRESULT hr; // Log the expand event, so we know that the user clicked on a node. if (msiLog.IsLogging(CMSInfoLog::CATEGORY)) { CFolder * pFolder = m_pmapCategories->CategoryFromScope(hsiNode); if (pFolder) { CString strName; if (pFolder->GetName(strName)) msiLog.WriteLog(CMSInfoLog::CATEGORY, _T("CATEGORY \"%s\"\r\n"), strName); } } // We have nothing to do if we are contracting a node. // This never happens. if (fExpand == 0) return S_OK; // If our initialization failed, exit. if (!m_bInitializedCD) return S_OK; // Look up the CViewObject in our internal table based on our hsiNode value, // and // CHECK: Consider the memory leak potential in this map. if ((pfolSelection = m_pmapCategories->CategoryFromScope(hsiNode)) == NULL) { // If the expanded node isn't in our internal hash table, we should // be looking at the root node, so get it. if (IsPrimaryImpl() == FALSE) { // If we are an extension . . . FORMATETC fmtMachine = { (CLIPFORMAT) CDataObject::m_cfMachineName, NULL, DVASPECT_CONTENT, TYMED_HGLOBAL }; STGMEDIUM stgMachine; stgMachine.tymed = TYMED_HGLOBAL; stgMachine.hGlobal = ::GlobalAlloc(GMEM_MOVEABLE, (MAX_PATH + 1)* sizeof(WCHAR)); stgMachine.pUnkForRelease = NULL; // Only look externally for the machine when we are an extension. hr = pDataObject->GetDataHere(&fmtMachine, &stgMachine); if (hr == S_OK) { USES_CONVERSION; CString strMachine = W2T((LPWSTR)::GlobalLock(stgMachine.hGlobal)); ::GlobalUnlock(stgMachine.hGlobal); HGLOBAL hGlobal = ::GlobalFree(stgMachine.hGlobal); ASSERT(hGlobal == NULL); SetMachine(strMachine); } else ASSERT(hr == DV_E_FORMATETC); hr = AddExtensionRoot(hsiNode, &pfolSelection); ASSERT(SUCCEEDED(hr)); return hr; } else { hr = AddRoot(hsiNode, &pfolSelection); ASSERT(SUCCEEDED(hr)); if (FAILED(hr)) return hr; } } return ScopeEnumerate(hsiNode, pfolSelection); } /* * OnProperties - Called when a property value changes. * * History: a-jsari 9/25/97 Initial version. */ HRESULT CSystemInfoScope::OnProperties(LPARAM) { DWORD dwError = 0; AFX_MANAGE_STATE(::AfxGetStaticModuleState()); if (m_pSource && m_pSource->GetType() == CDataSource::GATHERER) { CWBEMDataSource * pWBEMSource = reinterpret_cast(pSource()); if (pWBEMSource && pWBEMSource->m_pThreadRefresh) pWBEMSource->m_pThreadRefresh->CancelRefresh(); if (m_strLastMachineName.CompareNoCase((LPCTSTR) *m_pstrMachineName) != 0) { // Try to connect to the new machine. If the connection fails, display // an appropriate error message and restore the machine name to the // original string. if (((CWBEMDataSource *)m_pSource)->m_pGatherer->SetConnect(*m_pstrMachineName)) { if (NULL == ((CWBEMDataSource *)m_pSource)->m_pGatherer->GetProvider()) dwError = ((CWBEMDataSource *)m_pSource)->m_pGatherer->GetLastError(); } else dwError = ((CWBEMDataSource *)m_pSource)->m_pGatherer->GetLastError(); if (dwError) { // Display the error, and reset the machine name and the data source // to the previous known good name. DisplayGatherError(dwError, (LPCTSTR) *m_pstrMachineName); *m_pstrMachineName = m_strLastMachineName; ((CWBEMDataSource *)m_pSource)->m_pGatherer->SetConnect(*m_pstrMachineName); ((CWBEMDataSource *)m_pSource)->m_pGatherer->GetProvider(); RefreshAsync(m_pfLast, m_pLastSystemInfo, FALSE); } else { if (pSource() != NULL) SetMachine(*m_pstrMachineName); } } // This doesn't really seem necessary... // if (m_pfLast && pConsole()) // pConsole()->SelectScopeItem(m_pfLast->m_hsi); } return S_OK; } //----------------------------------------------------------------------------- // DisplayGatherError // // There are multiple places we need to display a connection error to the user, // so the functionality is gathered here. //----------------------------------------------------------------------------- void CSystemInfoScope::DisplayGatherError(DWORD dwError, LPCTSTR szMachineName) { CString strMachine, strErrorMessage; if (!dwError) return; if (szMachineName) strMachine = CString(szMachineName); AFX_MANAGE_STATE(::AfxGetStaticModuleState()); if (strMachine.IsEmpty()) strMachine.LoadString(IDS_LOCALCOMPLABEL); switch (dwError) { case GATH_ERR_ALLOCATIONFAILED: case GATH_ERR_NOWBEMOUTOFMEM: strErrorMessage.LoadString(IDS_OUTOFMEMERROR); break; case GATH_ERR_NOWBEMLOCATOR: strErrorMessage.LoadString(IDS_NOLOCATOR); break; case GATH_ERR_NOWBEMCONNECT: strErrorMessage.Format(IDS_NOGATHERER, strMachine); break; case GATH_ERR_NOWBEMACCESSDENIED: strErrorMessage.Format(IDS_GATHERACCESS, strMachine); break; case GATH_ERR_NOWBEMBADSERVER: strErrorMessage.Format(IDS_BADSERVER, strMachine); break; case GATH_ERR_NOWBEMNETWORKFAILURE: strErrorMessage.Format(IDS_NETWORKERROR, strMachine); break; case GATH_ERR_BADCATEGORYID: strErrorMessage.LoadString(IDS_UNEXPECTED); break; default: ASSERT(FALSE); strErrorMessage.LoadString(IDS_UNEXPECTED); break; } MessageBox(strErrorMessage); } /* * MachineName - Return the current connected machine as a LPCSTR. * * History: a-jsari 11/12/97 Initial version. */ LPCTSTR CSystemInfoScope::MachineName() const { if (m_pstrMachineName == NULL || m_pstrMachineName->GetLength() == 0) return NULL; return (LPCTSTR)(*m_pstrMachineName)+2; // +2 to skip over the initial "\\" } /* * SetSelectedFolder - Remember the last selected folder for context- * sensitive operations (i.e. Print, Report, Find) * * History: a-jsari 2/12/98 Initial version */ void CSystemInfoScope::SetSelectedFolder(CFolder *pFolder) { m_pfLast = pFolder; if (pSource() != NULL) pSource()->SetLastFolder(pFolder); } //----------------------------------------------------------------------------- // Start an async refresh of the specified folder. //----------------------------------------------------------------------------- void CSystemInfoScope::RefreshAsync(CFolder * pFolder, CSystemInfo * pSystemInfo, BOOL fSoftRefresh) { if (pSource() && pSource()->GetType() == CDataSource::GATHERER) { CWBEMDataSource * pWBEMSource = reinterpret_cast(pSource()); if (pWBEMSource && pWBEMSource->m_pThreadRefresh) pWBEMSource->m_pThreadRefresh->RefreshFolderAsync(pFolder, pSystemInfo, FALSE, fSoftRefresh); } }