// MSInfo.h : Declaration of the CMSInfo #ifndef __MSINFO_H_ #define __MSINFO_H_ #include #include "resource.h" // main symbols #include #include "pseudomenu.h" #include "datasource.h" #include "category.h" #include "msinfotool.h" #include "msinfo4category.h" #include "htmlhelp.h" #include #include "dataset.h" // // From HelpServiceTypeLib.idl // #include extern void StringReplace(CString & str, LPCTSTR szLookFor, LPCTSTR szReplaceWith); extern BOOL gfEndingSession; //v-stlowe History progress dialog //member variable of CMSInfo so it can be updated by CMSInfo::UpdateDCOProgress // CHistoryRefreshDlg dialog //========================================================================= // //========================================================================= #include "HistoryParser.h" // Added by ClassView #include #include class CHistoryRefreshDlg : public CDialogImpl { public: enum { IDD = IDD_HISTORYREFRESHPROGRESS }; CWindow m_wndProgressBar; BEGIN_MSG_MAP(CWaitForRefreshDialog) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) END_MSG_MAP() LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); }; ///////////////////////////////////////////////////////////////////////////// // CMSInfo class ATL_NO_VTABLE CMSInfo : public CComObjectRootEx, public CStockPropImpl, public CComCompositeControl, public IPersistStreamInitImpl, public IOleControlImpl, public IOleObjectImpl, public IOleInPlaceActiveObjectImpl, public IViewObjectExImpl, public IOleInPlaceObjectWindowlessImpl, public IPersistStorageImpl, public ISpecifyPropertyPagesImpl, public IQuickActivateImpl, public IDataObjectImpl, public IProvideClassInfo2Impl<&CLSID_MSInfo, NULL, &LIBID_MSINFO32Lib>, public CComCoClass { public: CMSInfo() :m_fHistoryAvailable(FALSE),m_pCurrData(NULL),m_fHistorySaveAvailable(FALSE) { m_bWindowOnly = TRUE; CalcExtent(m_sizeExtent); //v-stlowe 2/23/01 synchronization for put_DCO_IUnknown m_evtControlInit = CreateEvent(NULL,TRUE,FALSE,CString(_T("MSInfoControlInitialized"))); //create history event, to signal when DCO has finished. m_hEvtHistoryComplete = CreateEvent(NULL,TRUE,FALSE,CString(_T("MSInfoHistoryDone"))); } DECLARE_REGISTRY_RESOURCEID(IDR_MSINFO) DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_COM_MAP(CMSInfo) COM_INTERFACE_ENTRY(IMSInfo) COM_INTERFACE_ENTRY(IDispatch) COM_INTERFACE_ENTRY(IViewObjectEx) COM_INTERFACE_ENTRY(IViewObject2) COM_INTERFACE_ENTRY(IViewObject) COM_INTERFACE_ENTRY(IOleInPlaceObjectWindowless) COM_INTERFACE_ENTRY(IOleInPlaceObject) COM_INTERFACE_ENTRY2(IOleWindow, IOleInPlaceObjectWindowless) COM_INTERFACE_ENTRY(IOleInPlaceActiveObject) COM_INTERFACE_ENTRY(IOleControl) COM_INTERFACE_ENTRY(IOleObject) COM_INTERFACE_ENTRY(IPersistStreamInit) COM_INTERFACE_ENTRY2(IPersist, IPersistStreamInit) COM_INTERFACE_ENTRY(ISpecifyPropertyPages) COM_INTERFACE_ENTRY(IQuickActivate) COM_INTERFACE_ENTRY(IPersistStorage) COM_INTERFACE_ENTRY(IDataObject) COM_INTERFACE_ENTRY(IProvideClassInfo) COM_INTERFACE_ENTRY(IProvideClassInfo2) END_COM_MAP() BEGIN_PROP_MAP(CMSInfo) PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4) PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4) PROP_ENTRY("Appearance", DISPID_APPEARANCE, CLSID_NULL) PROP_ENTRY("AutoSize", DISPID_AUTOSIZE, CLSID_NULL) PROP_ENTRY("BackColor", DISPID_BACKCOLOR, CLSID_StockColorPage) PROP_ENTRY("BackStyle", DISPID_BACKSTYLE, CLSID_NULL) PROP_ENTRY("BorderColor", DISPID_BORDERCOLOR, CLSID_StockColorPage) PROP_ENTRY("BorderStyle", DISPID_BORDERSTYLE, CLSID_NULL) PROP_ENTRY("BorderVisible", DISPID_BORDERVISIBLE, CLSID_NULL) PROP_ENTRY("BorderWidth", DISPID_BORDERWIDTH, CLSID_NULL) PROP_ENTRY("Font", DISPID_FONT, CLSID_StockFontPage) PROP_ENTRY("ForeColor", DISPID_FORECOLOR, CLSID_StockColorPage) PROP_ENTRY("HWND", DISPID_HWND, CLSID_NULL) // Example entries // PROP_ENTRY("Property Description", dispid, clsid) // PROP_PAGE(CLSID_StockColorPage) END_PROP_MAP() BEGIN_MSG_MAP(CMSInfo) MESSAGE_HANDLER(WM_CTLCOLORDLG, OnDialogColor) MESSAGE_HANDLER(WM_CTLCOLORSTATIC, OnDialogColor) CHAIN_MSG_MAP(CComCompositeControl) MESSAGE_HANDLER(WM_DESTROY, OnDestroy) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown) MESSAGE_HANDLER(WM_PAINT, OnPaint) MESSAGE_HANDLER(WM_SIZE, OnSize) MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove) MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUP) MESSAGE_HANDLER(WM_MSINFODATAREADY, OnMSInfoDataReady) NOTIFY_HANDLER(IDC_TREE, TVN_SELCHANGED, OnSelChangedTree) NOTIFY_HANDLER(IDC_LIST, LVN_COLUMNCLICK, OnColumnClick) NOTIFY_HANDLER(IDC_LIST, LVN_ITEMCHANGED, OnListItemChanged) NOTIFY_HANDLER(IDC_TREE, TVN_ITEMEXPANDING, OnItemExpandingTree) COMMAND_HANDLER(IDSTOPFIND, BN_CLICKED, OnStopFind) COMMAND_HANDLER(IDCANCELFIND, BN_CLICKED, OnStopFind) COMMAND_HANDLER(IDC_EDITFINDWHAT, EN_CHANGE, OnChangeFindWhat) COMMAND_HANDLER(IDSTARTFIND, BN_CLICKED, OnFind) COMMAND_HANDLER(IDFINDNEXT, BN_CLICKED, OnFind) COMMAND_HANDLER(IDC_HISTORYCOMBO, CBN_SELCHANGE, OnHistorySelection) COMMAND_HANDLER(IDC_CHECKSEARCHSELECTED, BN_CLICKED, OnClickedSearchSelected) COMMAND_HANDLER(IDC_CHECKSEARCHCATSONLY, BN_CLICKED, OnClickedSearchCatsOnly) MESSAGE_HANDLER(WM_SYSCOLORCHANGE, OnSysColorChange) NOTIFY_HANDLER(IDC_LIST, NM_SETFOCUS, OnSetFocusList) NOTIFY_HANDLER(IDC_LIST, LVN_GETINFOTIP, OnInfoTipList) NOTIFY_HANDLER(IDC_TREE, NM_RCLICK, OnRClickTree) COMMAND_HANDLER(IDC_EDITFINDWHAT, EN_SETFOCUS, OnSetFocusEditFindWhat) COMMAND_HANDLER(IDC_EDITFINDWHAT, EN_KILLFOCUS, OnKillFocusEditFindWhat) END_MSG_MAP() // Handler prototypes: // LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); // LRESULT CommandHandler(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled); // LRESULT NotifyHandler(int idCtrl, LPNMHDR pnmh, BOOL& bHandled); BEGIN_SINK_MAP(CMSInfo) //Make sure the Event Handlers have __stdcall calling convention END_SINK_MAP() STDMETHOD(OnAmbientPropertyChange)(DISPID dispid) { if (dispid == DISPID_AMBIENT_BACKCOLOR) { // Don't respond to ambient color changes (yet). // // SetBackgroundColorFromAmbient(); // FireViewChange(); } return IOleControlImpl::OnAmbientPropertyChange(dispid); } // IViewObjectEx DECLARE_VIEW_STATUS(0) // IMSInfo public: STDMETHOD(SaveFile)(BSTR filename, BSTR computer, BSTR category); STDMETHOD(get_DCO_IUnknown)(/*[out, retval]*/ IUnknown* *pVal); STDMETHOD(put_DCO_IUnknown)(/*[in]*/ IUnknown* newVal); STDMETHOD(SetHistoryStream)(IStream * pStream); STDMETHOD(UpdateDCOProgress)(VARIANT nPctDone); //========================================================================= // Member variables generated by the wizard. //========================================================================= //v-stlowe 2/23/2001 CHistoryRefreshDlg m_HistoryProgressDlg; //v-stlowe 2/27/2001 - filename needed when loading XML files, //and switching between snapshot and history view CString m_strFileName; short m_nAppearance; OLE_COLOR m_clrBackColor; LONG m_nBackStyle; OLE_COLOR m_clrBorderColor; LONG m_nBorderStyle; BOOL m_bBorderVisible; LONG m_nBorderWidth; CComPtr m_pFont; OLE_COLOR m_clrForeColor; //v-stlowe 2/23/01 synchronization for put_DCO_IUnknown HANDLE m_evtControlInit; //v-stlowe 2/24/01 synchronization for History DCO progress dlg, so it can be destroyed //even if DCO never returns from collecting history HANDLE m_hEvtHistoryComplete; UINT HistoryRefreshDlgDlgThreadProc(LPVOID pParamNotUsed ); enum { IDD = IDD_MSINFO }; //========================================================================= // MSInfo member variables (initialized in OnInitDialog()). //========================================================================= BOOL m_fDoNotRun; // if this is TRUE, don't allow the control to do anything CString m_strDoNotRun; // message to display if we don't run CString m_strMachine; // if empty, local machine, otherwise network name CWindow m_tree; // set to refer to the tree view CWindow m_list; // set to refer to the list view int m_iTreeRatio; // the tree is x% the size of the list view BOOL m_fFirstPaint; // flag so we can initialize some paint things, once CDataSource * m_pLiveData; // data source for current data from WMI CDataSource * m_pFileData; // data source for an open NFO, XML snapshot CDataSource * m_pCurrData; // copy of one of the previous two (don't need to delete this) CMSInfoCategory * m_pCategory; // currently selected category BOOL m_fMouseCapturedForSplitter; // mouse currently being looked at by splitter BOOL m_fTrackingSplitter; // does the user have the LBUTTON down, resizing panes RECT m_rectSplitter; // current splitter rect (for hit tests) RECT m_rectLastSplitter; // last rect in DrawFocusRect int m_cxOffset; // offset from mouse position to left side of splitter BOOL m_fAdvanced; // currently showing advanced data? CString m_strMessTitle; // title for message to display in results CString m_strMessText; // text for message to display in results BOOL m_fNoUI; // true if doing a silent save int m_aiCategoryColNumber[64]; // contains category column for each list view column int m_iCategoryColNumberLen; CMapWordToPtr m_mapIDToTool; // a map from menu ID to a CMSInfoTool pointer TCHAR m_szCurrentDir[MAX_PATH]; // current directory when control starts (should restore) // Find member variables. BOOL m_fFindVisible; // are the find controls visible? CWindow m_wndFindWhatLabel; // windows for the various find controls CWindow m_wndFindWhat; CWindow m_wndSearchSelected; CWindow m_wndSearchCategories; CWindow m_wndStartFind; CWindow m_wndStopFind; CWindow m_wndFindNext; CWindow m_wndCancelFind; BOOL m_fInFind; BOOL m_fCancelFind; BOOL m_fFindNext; BOOL m_fSearchCatNamesOnly; BOOL m_fSearchSelectedCatOnly; CString m_strFind; CMSInfoCategory * m_pcatFind; int m_iFindLine; // History member variables. CWindow m_history; CWindow m_historylabel; BOOL m_fHistoryAvailable; BOOL m_fHistorySaveAvailable; CMSInfoCategory * m_pLastCurrentCategory; CComPtr m_pDCO; CComPtr m_pHistoryStream; // Member variables set from the command line parameters. CString m_strOpenFile; CString m_strPrintFile; CString m_strCategory; CString m_strCategories; CString m_strComputer; BOOL m_fShowPCH; BOOL m_fShowCategories; //========================================================================= // A member variable and a function to hook into the windows procedure // of the parent window of the control (so we can put a menu on it). //========================================================================= HMENU m_hmenu; HWND m_hwndParent; static CMSInfo * m_pControl; static WNDPROC m_wndprocParent; static LRESULT CALLBACK MenuWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_COMMAND && m_pControl && m_pControl->DispatchCommand(LOWORD(wParam))) return 0; // We want to know if the session is ending (so we can set the timeout for // waiting for WMI to finish to something smaller). if (uMsg == WM_ENDSESSION || uMsg == WM_QUERYENDSESSION) gfEndingSession = TRUE; return CallWindowProc(m_wndprocParent, hwnd, uMsg, wParam, lParam); } //========================================================================= // Functions for initializing and destroying the dialog. A good place to // initialize variables and free memory. //========================================================================= LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { m_list.Attach(GetDlgItem(IDC_LIST)); m_tree.Attach(GetDlgItem(IDC_TREE)); m_history.Attach(GetDlgItem(IDC_HISTORYCOMBO)); m_historylabel.Attach(GetDlgItem(IDC_HISTORYLABEL)); // Determine if we are being loaded from within Help Center. Do this // by getting IOleContainer, which gives us IHTMLDocument2, which will // give us the URL loading us. m_fDoNotRun = TRUE; m_strDoNotRun.Empty(); CString strParams; CComPtr spContainer; m_spClientSite->GetContainer(&spContainer); CComQIPtr spHTMLDocument(spContainer); if (spHTMLDocument) { CComBSTR bstrURL; if (SUCCEEDED(spHTMLDocument->get_URL(&bstrURL))) { CComBSTR bstrEscapedUrl(bstrURL); USES_CONVERSION; if(SUCCEEDED(UrlUnescape(OLE2T(bstrEscapedUrl), NULL, NULL, URL_UNESCAPE_INPLACE))) bstrURL = bstrEscapedUrl; bstrEscapedUrl.Empty(); CString strURL(bstrURL); int iQuestionMark = strURL.Find(_T("?")); if (iQuestionMark != -1) strParams = strURL.Mid(iQuestionMark + 1); strURL.MakeLower(); if (strURL.Left(4) == CString(_T("hcp:"))) m_fDoNotRun = FALSE; // Include the following when we want to test the control // using a local URL. #ifdef MSINFO_TEST_WORKFROMLOCALURLS if (strURL.Left(5) == CString(_T("file:"))) m_fDoNotRun = FALSE; #endif } } if (m_fDoNotRun) { m_list.ShowWindow(SW_HIDE); m_tree.ShowWindow(SW_HIDE); return 0; } ::GetCurrentDirectory(MAX_PATH, m_szCurrentDir); m_strMachine.Empty(); m_fNoUI = FALSE; // Find the parent window for MSInfo. We want to add a menu bar to it. // We find the window by walking up the chain of parents until we get // to one with a caption. That window must also be top level (no parent), // and must not already have a menu. TCHAR szBuff[MAX_PATH]; HWND hwnd = this->m_hWnd; m_hmenu = NULL; m_hwndParent = NULL; m_wndprocParent = NULL; while (hwnd != NULL) { if (::GetWindowText(hwnd, szBuff, MAX_PATH) && NULL == ::GetParent(hwnd) && NULL == ::GetMenu(hwnd)) { // Update the window title. [This is done by the msinfo.xml file now.] // // CString strNewCaption; // ::AfxSetResourceHandle(_Module.GetResourceInstance()); // strNewCaption.LoadString(IDS_SYSTEMINFO); // ::SetWindowText(hwnd, strNewCaption); // We've found the window. Load the menu bar for it. m_hmenu = ::LoadMenu(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_MENUBAR)); if (m_hmenu) { ::SetMenu(hwnd, m_hmenu); // To catch the commands from the menu, we need to replace that // window's WndProc with our own. Ours will catch and menu command // that we implement, and pass the rest of the messages along. m_wndprocParent = (WNDPROC)::GetWindowLongPtr(hwnd, GWLP_WNDPROC); ::SetWindowLongPtr(hwnd, GWLP_WNDPROC, (ULONG_PTR)(WNDPROC)&MenuWndProc); m_pControl = this; // set a static member variable so the MenuWndProc can access it m_hwndParent = hwnd; } break; } hwnd = ::GetParent(hwnd); } m_fFirstPaint = TRUE; ListView_SetExtendedListViewStyle(m_list.m_hWnd, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP); m_strMessTitle.Empty(); m_strMessText.Empty(); m_fMouseCapturedForSplitter = FALSE; m_fTrackingSplitter = FALSE; ::SetRect(&m_rectSplitter, 0, 0, 0, 0); m_iTreeRatio = 33; // Set the history state variables (including whether or not history is available, // based on the presence of the DCO). #ifdef MSINFO_TEST_HISTORYFROMFILE m_fHistoryAvailable = TRUE; #else // m_fHistoryAvailable = (m_pDCO != NULL); #endif m_pLastCurrentCategory = NULL; // Removing the Advanced/Basic view menu items (Whistler 207638). Should // always default to Advanced now. m_fAdvanced = TRUE; // was FALSE; m_fFindVisible = TRUE; // used to be FALSE, before we decided to show find on startup m_wndFindWhatLabel.Attach(GetDlgItem(IDC_FINDWHATLABEL)); m_wndFindWhat.Attach(GetDlgItem(IDC_EDITFINDWHAT)); m_wndSearchSelected.Attach(GetDlgItem(IDC_CHECKSEARCHSELECTED)); m_wndSearchCategories.Attach(GetDlgItem(IDC_CHECKSEARCHCATSONLY)); // We need to set the event mask for the rich edit control // so we get EN_CHANGE notifications. m_wndFindWhat.SendMessage(EM_SETEVENTMASK, 0, (LPARAM)ENM_CHANGE); m_wndFindWhat.SendMessage(EM_SETTEXTMODE, TM_PLAINTEXT, 0); m_wndStartFind.Attach(GetDlgItem(IDSTARTFIND)); m_wndStopFind.Attach(GetDlgItem(IDSTOPFIND)); m_wndFindNext.Attach(GetDlgItem(IDFINDNEXT)); m_wndCancelFind.Attach(GetDlgItem(IDCANCELFIND)); // The pairs (start & find next, stop & cancel find) need to be matched // in size. The heights should already be the same. CRect rectStart, rectNext, rectStop, rectCancel; m_wndStartFind.GetWindowRect(&rectStart); m_wndFindNext.GetWindowRect(&rectNext); m_wndStopFind.GetWindowRect(&rectStop); m_wndCancelFind.GetWindowRect(&rectCancel); if (rectStart.Width() > rectNext.Width()) rectNext = rectStart; else rectStart = rectNext; if (rectStop.Width() > rectCancel.Width()) rectCancel = rectStop; else rectStop = rectCancel; m_wndStartFind.MoveWindow(&rectStart); m_wndFindNext.MoveWindow(&rectNext); m_wndStopFind.MoveWindow(&rectStop); m_wndCancelFind.MoveWindow(&rectCancel); // Initialize the find function member variables. m_fInFind = FALSE; m_fCancelFind = FALSE; m_fFindNext = FALSE; m_strFind = CString(_T("")); m_pcatFind = NULL; m_fSearchCatNamesOnly = FALSE; m_fSearchSelectedCatOnly = FALSE; // Show the appropriate find controls. ShowFindControls(); UpdateFindControls(); m_iCategoryColNumberLen = 0; // Parse the parameters out of the URL. m_strOpenFile = _T(""); m_strPrintFile = _T(""); m_strCategory = _T(""); m_strComputer = _T(""); m_strCategories = _T(""); m_fShowCategories = FALSE; m_fShowPCH = FALSE; CString strTemp; while (!strParams.IsEmpty()) { while (strParams[0] == _T(',')) strParams = strParams.Mid(1); strTemp = strParams.SpanExcluding(_T(",=")); if (strTemp.CompareNoCase(CString(_T("pch"))) == 0) { m_fShowPCH = TRUE; strParams = strParams.Mid(strTemp.GetLength()); } else if (strTemp.CompareNoCase(CString(_T("showcategories"))) == 0) { m_fShowCategories = TRUE; strParams = strParams.Mid(strTemp.GetLength()); } else if (strTemp.CompareNoCase(CString(_T("open"))) == 0) { strParams = strParams.Mid(strTemp.GetLength()); if (strParams[0] == _T('=')) strParams = strParams.Mid(1); m_strOpenFile = strParams.SpanExcluding(_T(",")); strParams = strParams.Mid(m_strOpenFile.GetLength()); } else if (strTemp.CompareNoCase(CString(_T("print"))) == 0) { strParams = strParams.Mid(strTemp.GetLength()); if (strParams[0] == _T('=')) strParams = strParams.Mid(1); m_strPrintFile = strParams.SpanExcluding(_T(",")); strParams = strParams.Mid(m_strPrintFile.GetLength()); } else if (strTemp.CompareNoCase(CString(_T("computer"))) == 0) { strParams = strParams.Mid(strTemp.GetLength()); if (strParams[0] == _T('=')) strParams = strParams.Mid(1); m_strComputer = strParams.SpanExcluding(_T(",")); strParams = strParams.Mid(m_strComputer.GetLength()); } else if (strTemp.CompareNoCase(CString(_T("category"))) == 0) { strParams = strParams.Mid(strTemp.GetLength()); if (strParams[0] == _T('=')) strParams = strParams.Mid(1); m_strCategory = strParams.SpanExcluding(_T(",")); strParams = strParams.Mid(m_strCategory.GetLength()); } else if (strTemp.CompareNoCase(CString(_T("categories"))) == 0) { strParams = strParams.Mid(strTemp.GetLength()); if (strParams[0] == _T('=')) strParams = strParams.Mid(1); m_strCategories = strParams.SpanExcluding(_T(",")); strParams = strParams.Mid(m_strCategories.GetLength()); } else strParams = strParams.Mid(strTemp.GetLength()); } // Initialize the data sources. m_pLiveData = NULL; CLiveDataSource * pLiveData = new CLiveDataSource; if (pLiveData) { HRESULT hr = pLiveData->Create(_T(""), m_hWnd, m_strCategories); // create a data source for this machine if (FAILED(hr)) { // bad news, report an error delete pLiveData; } else m_pLiveData = pLiveData; } else { // bad news - no memory } m_pFileData = NULL; m_pCategory = NULL; // Load the initial tool set. LoadGlobalToolset(m_mapIDToTool); UpdateToolsMenu(); //a-sanka 03/29/01 Moved here before any model MessageBox. //v-stlowe 2/23/01 synchronization for put_DCO_IUnknown SetEvent(m_evtControlInit); // Handle the command line parameters. if (!m_strPrintFile.IsEmpty()) m_strOpenFile = m_strPrintFile; HRESULT hrOpen = E_FAIL; if (!m_strOpenFile.IsEmpty()) { LPCTSTR szBuffer = m_strOpenFile; int nFileExtension = _tcsclen(szBuffer) - 1; while (nFileExtension >= 0 && szBuffer[nFileExtension] != _T('.')) nFileExtension -= 1; if (nFileExtension >= 0) hrOpen = OpenMSInfoFile(szBuffer, nFileExtension + 1); } // Check to see if we should initially display a file as we open. Also look up whether // the user was showing advanced data last time. HKEY hkey; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Shared Tools\\MSInfo"), 0, KEY_ALL_ACCESS, &hkey)) { TCHAR szBuffer[MAX_PATH]; DWORD dwType, dwSize = MAX_PATH * sizeof(TCHAR); if (ERROR_SUCCESS == RegQueryValueEx(hkey, _T("openfile"), NULL, &dwType, (LPBYTE)szBuffer, &dwSize)) if (dwType == REG_SZ) { RegDeleteValue(hkey, _T("openfile")); int nFileExtension = _tcsclen(szBuffer) - 1; while (nFileExtension >= 0 && szBuffer[nFileExtension] != _T('.')) nFileExtension -= 1; if (nFileExtension >= 0) hrOpen = OpenMSInfoFile(szBuffer, nFileExtension + 1); } // Removing the Advanced/Basic view menu items (Whistler 207638) // // dwSize = MAX_PATH * sizeof(TCHAR); // if (ERROR_SUCCESS == RegQueryValueEx(hkey, _T("advanced"), NULL, &dwType, (LPBYTE)szBuffer, &dwSize)) // if (dwType == REG_SZ && szBuffer[0] == _T('1')) // m_fAdvanced = TRUE; RegCloseKey(hkey); } if (FAILED(hrOpen) && m_pLiveData) SelectDataSource(m_pLiveData); if (FAILED(hrOpen) && m_pLiveData) SelectDataSource(m_pLiveData); if (!m_strPrintFile.IsEmpty()) DoPrint(TRUE); if (!m_strComputer.IsEmpty()) DoRemote(m_strComputer); if (!m_strCategory.IsEmpty() && m_pCurrData) { HTREEITEM hti = m_pCurrData->GetNodeByName(m_strCategory); if (hti != NULL) { TreeView_EnsureVisible(m_tree.m_hWnd, hti); TreeView_SelectItem(m_tree.m_hWnd, hti); } } if (m_fShowPCH && m_fHistoryAvailable && m_strMachine.IsEmpty()) { DispatchCommand(ID_VIEW_HISTORY); SetMenuItems(); } // Load the table of accelerator keys (used in our override of TranslateAccelerator). m_hAccTable = ::LoadAccelerators(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_ACCELERATOR1)); // Set the focus to the control, so we can process keystrokes immediately. ::SetFocus(m_hWnd); return 0; } LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { if (m_fDoNotRun) return 0; // Restore the window's wndproc. if (m_wndprocParent && m_hwndParent) { ::SetWindowLongPtr(m_hwndParent, GWLP_WNDPROC, (ULONG_PTR)m_wndprocParent); m_wndprocParent = NULL; m_hwndParent = NULL; } // When the window is closing, make sure we don't send any more messages // from the refresh thread. /* THIS HASN'T BEEN TESTED ENOUGH FOR THIS CHECKIN if (m_pCurrData) { CMSInfoCategory * pCat = m_pCurrData->GetRootCategory(); if (pCat && pCat->GetDataSourceType() == LIVE_DATA) { CLiveDataSource * pLiveDataSource = (CLiveDataSource *) m_pCurrData; if (pLiveDataSource->m_pThread) pLiveDataSource->m_pThread->AbortMessaging(); } } */ ::SetCurrentDirectory(m_szCurrentDir); if (m_pLiveData) { if (m_pFileData == m_pLiveData) m_pFileData = NULL; delete m_pLiveData; m_pLiveData = NULL; } if (m_pFileData) { delete m_pFileData; m_pFileData = NULL; } RemoveToolset(m_mapIDToTool); // Save the complexity of the view to the registry (so we can default to // it next time). // // Removing the Advanced/Basic view menu items (Whistler 207638). Should // always default to Advanced now. // HKEY hkey; // if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Shared Tools\\MSInfo"), 0, KEY_ALL_ACCESS, &hkey)) // { // TCHAR szBuffer[] = _T("0"); // // if (m_fAdvanced) // szBuffer[0] = _T('1'); // // RegSetValueEx(hkey, _T("advanced"), 0, REG_SZ, (LPBYTE)szBuffer, 2 * sizeof(TCHAR)); // RegCloseKey(hkey); // } if (m_pDCO) m_pDCO->Abort(); m_fDoNotRun = TRUE; return 0; } //========================================================================= // These functions process mouse movements, which we may respond to in // different ways. For instance, if the mouse is over the menu bar, then // we may want to highlight a menu. If the mouse is over the splitter, // we'll want to change the cursor into a resizer. // // This is complicated by the fact that we need to capture the mouse, // to make sure we know when it leaves so we can update appropriately. //========================================================================= LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { if (m_fDoNotRun) return 0; int xPos = LOWORD(lParam); int yPos = HIWORD(lParam); if (m_fTrackingSplitter) UpdateSplitterPosition(xPos, yPos); else CheckHover(xPos, yPos); return 0; } void CheckHover(int xPos, int yPos) { // Check to see if the mouse is hover over the splitter. if (::PtInRect(&m_rectSplitter, CPoint(xPos, yPos))) { if (!m_fMouseCapturedForSplitter) { SetCapture(); m_fMouseCapturedForSplitter = TRUE; } ::SetCursor(::LoadCursor(NULL, IDC_SIZEWE)); return; } else if (m_fMouseCapturedForSplitter) { ReleaseCapture(); m_fMouseCapturedForSplitter = FALSE; ::SetCursor(::LoadCursor(NULL, IDC_ARROW)); CheckHover(xPos, yPos); // give the other areas a chance return; } } //========================================================================= // These functions process mouse clicks and releases. This might entail // showing a menu, or resizing the panes using the splitter. //========================================================================= LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { if (m_fDoNotRun) return 0; int xPos = LOWORD(lParam); int yPos = HIWORD(lParam); SetFocus(); CheckSplitterClick(xPos, yPos); return 0; } LRESULT OnLButtonUP(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { if (m_fDoNotRun) return 0; if (m_fTrackingSplitter) EndSplitterDrag(); return 0; } //------------------------------------------------------------------------- // If the user clicked on the splitter (the area between the list view and // the tree view), start tracking the movement of it. Use the DrawFocusRect // API to give feedback. //------------------------------------------------------------------------- void CheckSplitterClick(int xPos, int yPos) { if (::PtInRect(&m_rectSplitter, CPoint(xPos, yPos))) { ASSERT(!m_fTrackingSplitter); m_fTrackingSplitter = TRUE; ::CopyRect(&m_rectLastSplitter, &m_rectSplitter); DrawSplitter(); m_cxOffset = xPos - m_rectLastSplitter.left; ASSERT(m_cxOffset >= 0); } } //========================================================================= // Functions for handling the user interaction with the splitter control. // // TBD - bug if you drag splitter and release button with the cursor over // a menu. //========================================================================= void DrawSplitter() { InvalidateRect(&m_rectLastSplitter, FALSE); PAINTSTRUCT ps; HDC hdc = BeginPaint(&ps); ::DrawFocusRect(hdc, &m_rectLastSplitter); EndPaint(&ps); } void UpdateSplitterPosition(int xPos, int yPos) { // If the user attempts to drag the splitter outside of the window, don't // allow it. RECT rectClient; GetClientRect(&rectClient); if (!::PtInRect(&rectClient, CPoint(xPos, yPos))) return; DrawSplitter(); // remove the current focus rect int cxDelta = xPos - (m_rectLastSplitter.left + m_cxOffset); m_rectLastSplitter.left += cxDelta; m_rectLastSplitter.right += cxDelta; DrawSplitter(); } void EndSplitterDrag() { DrawSplitter(); // remove the focus rect // Compute the new tree and list rectangles based on the last splitter position. RECT rectTree, rectList; m_tree.GetWindowRect(&rectTree); m_list.GetWindowRect(&rectList); ScreenToClient(&rectTree); ScreenToClient(&rectList); // First, check to see if the coordinates are reversed (possibly on a bi-directional locale). The // first case is that standard, left to right case. if (rectTree.right > rectTree.left || (rectTree.right == rectTree.left && rectList.right > rectList.left)) { rectTree.right = m_rectLastSplitter.left; rectList.left = m_rectLastSplitter.right; // Move the tree and list controls based on the new rects. m_tree.MoveWindow(rectTree.left, rectTree.top, rectTree.right - rectTree.left, rectTree.bottom - rectTree.top, TRUE); m_list.MoveWindow(rectList.left, rectList.top, rectList.right - rectList.left, rectList.bottom - rectList.top, TRUE); } else { // Here's the case where the coordinates are right to left. rectList.right = m_rectLastSplitter.right; rectTree.left = m_rectLastSplitter.left; // Move the tree and list controls based on the new rects. m_tree.MoveWindow(rectTree.right, rectTree.top, rectTree.left - rectTree.right, rectTree.bottom - rectTree.top, TRUE); m_list.MoveWindow(rectList.right, rectList.top, rectList.left - rectList.right, rectList.bottom - rectList.top, TRUE); } // If we're currently showing a category from a 4.10 NFO file, resize the OCX. CMSInfoCategory * pCategory = GetCurrentCategory(); if (pCategory && pCategory->GetDataSourceType() == NFO_410) { CMSInfo4Category * p4Cat = (CMSInfo4Category*) pCategory; p4Cat->ResizeControl(this->GetOCXRect()); } // If nothing data is showing and there is a message string, invalidate the window so // that it redraws based on the new rect. if (!m_list.IsWindowVisible() && (!m_strMessTitle.IsEmpty() || !m_strMessText.IsEmpty())) { RECT rectNewList; m_list.GetWindowRect(&rectNewList); ScreenToClient(&rectNewList); InvalidateRect(&rectNewList, TRUE); } // Figure the size ratio between the list view and tree view, and save it. Take into account // that the left coordinate might be greater than the right coordinate on a rect (if the // coordinates are from a RTL locale). RECT rectClient; GetClientRect(&rectClient); if (rectTree.right > rectTree.left) m_iTreeRatio = ((rectTree.right - rectTree.left) * 100) / (rectClient.right - rectClient.left); else if (rectTree.right < rectTree.left) m_iTreeRatio = ((rectTree.left - rectTree.right) * 100) / (rectClient.right - rectClient.left); else m_iTreeRatio = 100; // Update the splitter tracking variables. m_fTrackingSplitter = FALSE; ::CopyRect(&m_rectSplitter, &m_rectLastSplitter); // Invalidate the splitter itself (so that it will repaint). InvalidateRect(&m_rectSplitter); // Release the mouse capture and restore the cursor. ReleaseCapture(); m_fMouseCapturedForSplitter = FALSE; ::SetCursor(::LoadCursor(NULL, IDC_ARROW)); } //========================================================================= // Rendering functions, including handling the WM_PAINT message. //========================================================================= LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { // We're having some redraw issues when brought back from a locked // system. It seems that even though the updated rect is the same // as the client rect, it doesn't include all the necessary regions // to repaint. RECT rectUpdate, rectClient; if (GetClientRect(&rectClient) && GetUpdateRect(&rectUpdate) && ::EqualRect(&rectClient, &rectUpdate)) Invalidate(FALSE); PAINTSTRUCT ps; HDC hdc = BeginPaint(&ps); GetClientRect(&rectClient); ::SetTextColor(hdc, ::GetSysColor(COLOR_BTNFACE)); if (m_fFirstPaint) { m_hbrBackground = CreateSolidBrush(::GetSysColor(COLOR_BTNFACE /* COLOR_MENU */)); if (!m_fDoNotRun) SetMenuItems(); m_fFirstPaint = FALSE; } FillRect(hdc, &rectClient, m_hbrBackground); if (m_fDoNotRun) { if (m_strDoNotRun.IsEmpty()) { ::AfxSetResourceHandle(_Module.GetResourceInstance()); m_strDoNotRun.LoadString(IDS_ONLYINHELPCTR); } CDC dc; dc.Attach(hdc); dc.SetBkMode(TRANSPARENT); dc.DrawText(m_strDoNotRun, &rectClient, DT_CENTER | DT_VCENTER | DT_SINGLELINE); dc.Detach(); EndPaint(&ps); return 0; } if (!m_list.IsWindowVisible()) { RECT rectList; m_list.GetWindowRect(&rectList); ScreenToClient(&rectList); // If the list window is hidden, we should probably be displaying a message. First, // draw the 3D rectangle. CDC dc; dc.Attach(hdc); dc.Draw3dRect(&rectList, ::GetSysColor(COLOR_3DSHADOW), ::GetSysColor(COLOR_3DHILIGHT)); // For some reason, DrawText wants left < right (even on RTL systems, like ARA). if (rectList.left > rectList.right) { int iSwap = rectList.left; rectList.left = rectList.right; rectList.right = iSwap; } // Next, if there is text, draw it. if (!m_strMessTitle.IsEmpty() || !m_strMessText.IsEmpty()) { ::InflateRect(&rectList, -10, -10); CGdiObject * pFontOld = dc.SelectStockObject(DEFAULT_GUI_FONT); COLORREF crTextOld = dc.SetTextColor(::GetSysColor(COLOR_MENUTEXT)); int nBkModeOld = dc.SetBkMode(TRANSPARENT); if (!m_strMessTitle.IsEmpty()) { // We want to make a bigger version of the same font. Select the font // again (to get a pointer to the original font). Get it's LOGFONT, // make it bigger, and create a new font for selection. CFont * pNormalFont = (CFont *) dc.SelectStockObject(DEFAULT_GUI_FONT); LOGFONT lf; pNormalFont->GetLogFont(&lf); lf.lfHeight = (lf.lfHeight * 3) / 2; lf.lfWeight = FW_BOLD; CFont fontDoubleSize; fontDoubleSize.CreateFontIndirect(&lf); dc.SelectObject(&fontDoubleSize); RECT rectTemp; ::CopyRect(&rectTemp, &rectList); int iHeight = dc.DrawText(m_strMessTitle, &rectTemp, DT_LEFT | DT_TOP | DT_WORDBREAK | DT_CALCRECT); dc.DrawText(m_strMessTitle, &rectList, DT_LEFT | DT_TOP | DT_WORDBREAK); rectList.top += iHeight + 5; dc.SelectObject(pNormalFont); } if (!m_strMessText.IsEmpty()) { dc.DrawText(m_strMessText, &rectList, DT_LEFT | DT_TOP | DT_WORDBREAK); } dc.SelectObject(pFontOld); dc.SetTextColor(crTextOld); dc.SetBkMode(nBkModeOld); } dc.Detach(); } EndPaint(&ps); return 0; } LRESULT OnSysColorChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { m_fFirstPaint = TRUE; Invalidate(); UpdateWindow(); return 0; } LRESULT OnDialogColor(UINT, WPARAM w, LPARAM, BOOL&) { HDC dc = (HDC) w; LOGBRUSH lb; ::GetObject(m_hbrBackground, sizeof(lb), (void*)&lb); ::SetBkColor(dc, lb.lbColor); ::SetTextColor(dc, ::GetSysColor(COLOR_BTNTEXT)); ::SetBkMode(dc, TRANSPARENT); return (LRESULT)m_hbrBackground; } //========================================================================= // Handle the WM_SIZE message. This involves a bit of cleverness to avoid // ugly updates: if the user has moved the splitter bar, preserve // the ratio of sizes to the two windows. //========================================================================= LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { if (m_fDoNotRun) { RECT rectClient; GetClientRect(&rectClient); InvalidateRect(&rectClient, FALSE); return 0; } LayoutControl(); return 0; } void LayoutControl() { const int cBorder = 3; const int cxSplitter = 3; RECT rectClient; GetClientRect(&rectClient); int cyMenuHeight = cBorder; int cyFindControlHeight = PositionFindControls(); if (m_history.IsWindowVisible()) { CRect rectHistory, rectHistorylabel; m_history.GetWindowRect(&rectHistory); m_historylabel.GetWindowRect(&rectHistorylabel); rectHistory.OffsetRect(rectClient.right - cBorder - rectHistory.right, rectClient.top + cBorder - rectHistory.top); rectHistorylabel.OffsetRect(rectHistory.left - cBorder / 2 - rectHistorylabel.right, rectClient.top + cBorder + ((rectHistory.Height() - rectHistorylabel.Height()) / 2) - rectHistorylabel.top); if (rectHistorylabel.left < 0) // TBD { rectHistory += CPoint(0 - rectHistorylabel.left, 0); rectHistorylabel += CPoint(0 - rectHistorylabel.left, 0); } m_history.MoveWindow(&rectHistory); m_historylabel.MoveWindow(&rectHistorylabel); if (rectHistory.Height() + cBorder * 2 > cyMenuHeight) { cyMenuHeight = rectHistory.Height() + cBorder * 2 - 1; CPoint ptMenu(cBorder, (cyMenuHeight - 0) / 2 + 2); } } else { CPoint ptMenu(5, 5); } RECT rectTree; ::CopyRect(&rectTree, &rectClient); rectTree.left += cBorder; rectTree.top += cyMenuHeight + cxSplitter / 2; rectTree.bottom -= ((cyFindControlHeight) ? cyFindControlHeight : cBorder); rectTree.right = ((rectClient.right - rectClient.left) * m_iTreeRatio) / 100 + rectClient.left; m_tree.MoveWindow(rectTree.left, rectTree.top, rectTree.right - rectTree.left, rectTree.bottom - rectTree.top, FALSE); RECT rectList; ::CopyRect(&rectList, &rectClient); rectList.left = rectTree.right + cxSplitter; rectList.top = rectTree.top; rectList.bottom = rectTree.bottom; rectList.right -= cBorder; m_list.MoveWindow(rectList.left, rectList.top, rectList.right - rectList.left, rectList.bottom - rectList.top, FALSE); // Get the rectangle for the splitter, and save it. m_tree.GetWindowRect(&rectTree); m_list.GetWindowRect(&rectList); ScreenToClient(&rectTree); ScreenToClient(&rectList); // Check whether the coordinate system is LTR (eg. English) or RTL. if (rectTree.left < rectTree.right || (rectTree.left == rectTree.right && rectList.left < rectList.right)) { // Nominal (LTR) case. int cxLeft = (rectTree.right < rectList.left) ? rectTree.right : rectList.left - cxSplitter; ::SetRect(&m_rectSplitter, cxLeft, rectTree.top, cxLeft + cxSplitter, rectTree.bottom); } else { // Special case for RTL locales. int cxLeft = (rectTree.left < rectList.right) ? rectList.right : rectTree.left - cxSplitter; ::SetRect(&m_rectSplitter, cxLeft - cxSplitter, rectTree.top, cxLeft, rectTree.bottom); } CMSInfoCategory * pCategory = GetCurrentCategory(); if (pCategory && pCategory->GetDataSourceType() == NFO_410) { CMSInfo4Category * p4Cat = (CMSInfo4Category*) pCategory; p4Cat->ResizeControl(this->GetOCXRect()); } InvalidateRect(&rectClient, FALSE); } //========================================================================= // Respond to the user clicking the tree or list. //========================================================================= LRESULT OnSelChangedTree(int idCtrl, LPNMHDR pnmh, BOOL& bHandled) { if (m_fDoNotRun) return 0; LPNMTREEVIEW lpnmtv = (LPNMTREEVIEW) pnmh; if (lpnmtv) { CMSInfoCategory * pCategory = (CMSInfoCategory *) lpnmtv->itemNew.lParam; ASSERT(pCategory); if (pCategory) { CancelFind(); SelectCategory(pCategory); } } return 0; } LRESULT OnColumnClick(int idCtrl, LPNMHDR pnmh, BOOL& bHandled) { if (m_fDoNotRun) return 0; LPNMLISTVIEW pnmv = (LPNMLISTVIEW) pnmh; CMSInfoCategory * pCategory = GetCurrentCategory(); if (pnmv && pCategory) { BOOL fSorts, fLexical; // Get the internal column number (in BASIC view, this might be different // than the column indicated. int iCol; if (m_fAdvanced) iCol = pnmv->iSubItem; else { ASSERT(pnmv->iSubItem < m_iCategoryColNumberLen); if (pnmv->iSubItem >= m_iCategoryColNumberLen) return 0; iCol = m_aiCategoryColNumber[pnmv->iSubItem]; } if (pCategory->GetColumnInfo(iCol, NULL, NULL, &fSorts, &fLexical)) { if (!fSorts) return 0; if (iCol == pCategory->m_iSortColumn) pCategory->m_fSortAscending = !pCategory->m_fSortAscending; else pCategory->m_fSortAscending = TRUE; pCategory->m_iSortColumn = iCol; pCategory->m_fSortLexical = fLexical; CLiveDataSource * pLiveDataSource = NULL; if (pCategory->GetDataSourceType() == LIVE_DATA) pLiveDataSource = (CLiveDataSource *) m_pCurrData; if (pLiveDataSource) pLiveDataSource->LockData(); ListView_SortItems(m_list.m_hWnd, (PFNLVCOMPARE) ListSortFunc, (LPARAM) pCategory); if (pLiveDataSource) pLiveDataSource->UnlockData(); } } return 0; } LRESULT OnListItemChanged(int idCtrl, LPNMHDR pnmh, BOOL& bHandled) { if (m_fDoNotRun) return 0; SetMenuItems(); return 0; } //------------------------------------------------------------------------- // Don't allow the user to collapse the root node of the tree. //------------------------------------------------------------------------- LRESULT OnItemExpandingTree(int idCtrl, LPNMHDR pnmh, BOOL& bHandled) { LPNMTREEVIEW lpnmtv = (LPNMTREEVIEW) pnmh; if (lpnmtv && (lpnmtv->action & TVE_COLLAPSE)) { CMSInfoCategory * pCategory = (CMSInfoCategory *) lpnmtv->itemNew.lParam; if (pCategory && pCategory->GetParent() == NULL) return 1; } return 0; } //========================================================================= // Called when we're being notified by the worker thread that a refresh // is complete and data should be displayed. // // TBD - check whether is category is still selected //========================================================================= LRESULT OnMSInfoDataReady(UINT /* uMsg */, WPARAM /* wParam */, LPARAM lParam, BOOL& /* bHandled */) { if (m_fDoNotRun) return 0; ASSERT(lParam); if (lParam == 0) return 0; if (m_fInFind && lParam == (LPARAM)m_pcatFind) { FindRefreshComplete(); return 0; } HTREEITEM hti = TreeView_GetSelection(m_tree.m_hWnd); if (hti) { TVITEM tvi; tvi.mask = TVIF_PARAM; tvi.hItem = hti; if (TreeView_GetItem(m_tree.m_hWnd, &tvi)) if (tvi.lParam == lParam) { CMSInfoCategory * pCategory = (CMSInfoCategory *) lParam; ASSERT(pCategory->GetDataSourceType() == LIVE_DATA); SelectCategory(pCategory, TRUE); } } return 0; } //========================================================================= // Functions for managing the list view and the tree view. Note - the // dwItem can only be set for the row (when iCol == 0). //========================================================================= void ListInsertItem(int iRow, int iCol, LPCTSTR szItem, DWORD dwItem) { LVITEM lvi; lvi.mask = LVIF_TEXT | ((iCol == 0) ? LVIF_PARAM : 0); lvi.iItem = iRow; lvi.iSubItem = iCol; lvi.pszText = (LPTSTR) szItem; lvi.lParam = (LPARAM) dwItem; if (iCol == 0) ListView_InsertItem(m_list.m_hWnd, &lvi); else ListView_SetItem(m_list.m_hWnd, &lvi); } void ListInsertColumn(int iCol, int cxWidth, LPCTSTR szCaption) { LVCOLUMN lvc; lvc.mask = LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH; lvc.cx = cxWidth; lvc.pszText = (LPTSTR) szCaption; lvc.iOrder = iCol; ListView_InsertColumn(m_list.m_hWnd, iCol, &lvc); } void ListClearItems() { ListView_DeleteAllItems(m_list.m_hWnd); } void ListClearColumns() { while (ListView_DeleteColumn(m_list.m_hWnd, 0)); } void TreeClearItems() { TreeView_DeleteAllItems(m_tree.m_hWnd); } HTREEITEM TreeInsertItem(HTREEITEM hParent, HTREEITEM hInsertAfter, LPTSTR szName, LPARAM lParam) { TVINSERTSTRUCT tvis; tvis.hParent = hParent; tvis.hInsertAfter = hInsertAfter; tvis.item.mask = TVIF_TEXT | TVIF_PARAM; tvis.item.pszText = szName; tvis.item.lParam = lParam; return TreeView_InsertItem(m_tree.m_hWnd, &tvis); } void BuildTree(HTREEITEM htiParent, HTREEITEM htiPrevious, CMSInfoCategory * pCategory) { ASSERT(pCategory); CString strCaption(_T("")); // If the user specified the /showcategories flag, we should show the name of // each category in the tree (which is unlocalized). Otherwise show the caption. if (!m_fShowCategories) pCategory->GetNames(&strCaption, NULL); else pCategory->GetNames(NULL, &strCaption); if (pCategory->GetDataSourceType() == LIVE_DATA && htiParent == TVI_ROOT && !m_strMachine.IsEmpty() && pCategory != &catHistorySystemSummary) { CString strMachine(m_strMachine); strMachine.MakeLower(); strMachine.TrimLeft(_T("\\")); // Changed this to use a Format() with a string resource, instead of // appending the machine name to the string. This should solve some // problems with oddities on RTL languages. strCaption.Format(IDS_SYSTEMSUMMARYMACHINENAME, strMachine); } HTREEITEM htiCategory = TreeInsertItem(htiParent, htiPrevious, (LPTSTR)(LPCTSTR)strCaption, (LPARAM)pCategory); pCategory->SetHTREEITEM(htiCategory); for (CMSInfoCategory * pChild = pCategory->GetFirstChild(); pChild; pChild = pChild->GetNextSibling()) BuildTree(htiCategory, TVI_LAST, pChild); } void RefillListView(BOOL fRefreshDataOnly = TRUE) { HTREEITEM hti = TreeView_GetSelection(m_tree.m_hWnd); if (hti) { TVITEM tvi; tvi.mask = TVIF_PARAM; tvi.hItem = hti; if (TreeView_GetItem(m_tree.m_hWnd, &tvi)) if (tvi.lParam) { CMSInfoCategory * pCategory = (CMSInfoCategory *) tvi.lParam; SelectCategory(pCategory, fRefreshDataOnly); } } } //========================================================================= // If the user presses a key, we should check to see if it's something // we need to deal with (accelerators, ALT-?, etc.). //========================================================================= HACCEL m_hAccTable; STDMETHODIMP TranslateAccelerator(MSG * pMsg) { if (m_hwndParent && m_hAccTable && (GetAsyncKeyState(VK_CONTROL) < 0 || (WORD)pMsg->wParam == VK_F1) && ::TranslateAccelerator(m_hwndParent, m_hAccTable, pMsg)) return S_OK; if (m_fFindVisible && (WORD)pMsg->wParam == VK_RETURN && pMsg->message != WM_KEYUP) { // If the focus is on the stop find button, do so. if (GetFocus() == m_wndStopFind.m_hWnd || GetFocus() == m_wndCancelFind.m_hWnd) { BOOL bHandled; OnStopFind(0, 0, NULL, bHandled); return S_OK; } // Otherwise, if the find or find next button is showing, execute // that command. if (m_wndStartFind.IsWindowEnabled() || m_wndFindNext.IsWindowEnabled()) { BOOL bHandled; OnFind(0, 0, NULL, bHandled); return S_OK; } } return IOleInPlaceActiveObjectImpl::TranslateAccelerator(pMsg); } void MSInfoMessageBox(UINT uiMessageID, UINT uiTitleID = IDS_SYSTEMINFO) { CString strCaption, strMessage; ::AfxSetResourceHandle(_Module.GetResourceInstance()); strCaption.LoadString(uiTitleID); strMessage.LoadString(uiMessageID); MessageBox(strMessage, strCaption); } void MSInfoMessageBox(const CString & strMessage, UINT uiTitleID = IDS_SYSTEMINFO) { CString strCaption; ::AfxSetResourceHandle(_Module.GetResourceInstance()); strCaption.LoadString(uiTitleID); MessageBox(strMessage, strCaption); } //========================================================================= // Functions implemented in the CPP file. //========================================================================= BOOL DispatchCommand(int iCommand); void SelectDataSource(CDataSource * pDataSource); void SelectCategory(CMSInfoCategory * pCategory, BOOL fRefreshDataOnly = FALSE); void MSInfoRefresh(); void OpenNFO(); void SaveNFO(); void Export(); void CloseFile(); void SaveMSInfoFile(LPCTSTR szFilename, DWORD dwFilterIndex = 1);//new format by default HRESULT OpenMSInfoFile(LPCTSTR szFilename, int nFileExtension); void ExportFile(LPCTSTR szFilename, int nFileExtension); CMSInfoCategory * GetCurrentCategory(); void SetMenuItems(); void SetMessage(const CString & strTitle, const CString & strMessage = CString(_T("")), BOOL fRedraw = FALSE); void SetMessage(UINT uiTitle, UINT uiMessage = 0, BOOL fRedraw = FALSE); void EditCopy(); void EditSelectAll(); void DoPrint(BOOL fNoUI = FALSE); void UpdateToolsMenu(); CRect GetOCXRect(); void CancelFind(); LRESULT OnStopFind(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled); void UpdateFindControls(); LRESULT OnChangeFindWhat(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled); LRESULT OnFind(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled); void FindRefreshComplete(); BOOL FindInCurrentCategory(); void ShowFindControls(); int PositionFindControls(); void ShowRemoteDialog(); void DoRemote(LPCTSTR szMachine); void SaveXML(const CString & strFileName); void GetMachineName(LPTSTR lpBuffer, LPDWORD lpnSize); void RefreshData(CLiveDataSource * pSource, CMSInfoLiveCategory * pLiveCategory); //------------------------------------------------------------------------- // Fill our combo box with the available history deltas. //------------------------------------------------------------------------- void FillHistoryCombo() { m_history.SendMessage(CB_RESETCONTENT, 0, 0); CMSInfoCategory * pCategory = GetCurrentCategory(); if (pCategory == NULL || (pCategory->GetDataSourceType() != LIVE_DATA && pCategory->GetDataSourceType() != XML_SNAPSHOT)) return; CLiveDataSource * pLiveSource = (CLiveDataSource *) m_pCurrData; CStringList strlistDeltas; CString strItem; CDC dc; CSize size; int cxMaxWidth = 0; HDC hdc = GetDC(); dc.Attach(hdc); dc.SelectObject(CFont::FromHandle((HFONT)m_history.SendMessage(WM_GETFONT, 0, 0))); if (pLiveSource->GetDeltaList(&strlistDeltas)) while (!strlistDeltas.IsEmpty()) { strItem = strlistDeltas.RemoveHead(); m_history.SendMessage(CB_INSERTSTRING, -1, (LPARAM)(LPCTSTR)strItem); size = dc.GetTextExtent(strItem); if (size.cx > cxMaxWidth) cxMaxWidth = size.cx; } //else //TD: what if no history is available? CRect rectClient, rectHistory; GetClientRect(&rectClient); m_history.GetWindowRect(&rectHistory); if (cxMaxWidth > rectHistory.Width() && cxMaxWidth < rectClient.Width()) { rectHistory.InflateRect((cxMaxWidth - (rectHistory.Width() - GetSystemMetrics(SM_CXHSCROLL))) / 2 + 5, 0); m_history.MoveWindow(&rectHistory); LayoutControl(); } dc.Detach(); ReleaseDC(hdc); } //------------------------------------------------------------------------- // If the user selects a history delta to view, we need to mark all of the // categories as unrefreshed (so the new data will be generated). //------------------------------------------------------------------------- LRESULT OnHistorySelection(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { int iSelection = (int)m_history.SendMessage(CB_GETCURSEL, 0, 0); #ifdef A_STEPHL /* CString strMSG; strMSG.Format("iSelection= %d",iSelection); ::MessageBox(NULL,strMSG,"",MB_OK);*/ #endif if (iSelection == CB_ERR) { return 0; } ChangeHistoryView(iSelection); return 0; } void ChangeHistoryView(int iIndex) { CMSInfoCategory * pCategory = GetCurrentCategory(); if (pCategory == NULL) { ASSERT(FALSE && "NULL pCategory"); return; } else if (pCategory->GetDataSourceType() == LIVE_DATA || pCategory->GetDataSourceType() == XML_SNAPSHOT) { pCategory->ResetCategory(); if (((CMSInfoHistoryCategory*) pCategory)->m_iDeltaIndex == iIndex) { // return; } CLiveDataSource * pLiveSource = (CLiveDataSource *) m_pCurrData; if (pLiveSource->ShowDeltas(iIndex)) { SetMessage(IDS_REFRESHHISTORYMESSAGE, 0, TRUE); MSInfoRefresh(); #ifdef A_STEPHL /* CString strMSG; strMSG.Format("niIndex= %d",iIndex); ::MessageBox(NULL,strMSG,"",MB_OK);*/ #endif } else { // Clear the existing categories in the tree. TreeClearItems(); // Load the contents of the tree from the data source. CMSInfoCategory * pRoot = m_pCurrData->GetRootCategory(); if (pRoot) { BuildTree(TVI_ROOT, TVI_LAST, pRoot); TreeView_Expand(m_tree.m_hWnd, TreeView_GetRoot(m_tree.m_hWnd), TVE_EXPAND); TreeView_SelectItem(m_tree.m_hWnd, TreeView_GetRoot(m_tree.m_hWnd)); } } } else { ASSERT(FALSE && "shouldn't be showing history dropdown with this data source"); return; } } //------------------------------------------------------------------------- // If the user sets the focus to the list, and there is no previously // selected item, then select the first item in the list (so the user can // see the focus). //------------------------------------------------------------------------- LRESULT OnSetFocusList(int idCtrl, LPNMHDR pnmh, BOOL& bHandled) { if (ListView_GetSelectedCount(m_list.m_hWnd) == 0) ListView_SetItemState(m_list.m_hWnd, 0, LVIS_SELECTED, LVIS_SELECTED); return 0; } //------------------------------------------------------------------------- // If the hovers on a cell in the list view, show the contents // of that cell as an infotip. This is helpful for extremely long items. //------------------------------------------------------------------------- LRESULT OnInfoTipList(int idCtrl, LPNMHDR pnmh, BOOL& bHandled) { TCHAR szBuf[MAX_PATH * 3] = _T(""); LPNMLVGETINFOTIP pGetInfoTip = (LPNMLVGETINFOTIP)pnmh; if(NULL != pGetInfoTip) { ListView_GetItemText(m_list.m_hWnd, pGetInfoTip->iItem, pGetInfoTip->iSubItem, szBuf, MAX_PATH * 3); pGetInfoTip->cchTextMax = _tcslen(szBuf); pGetInfoTip->pszText = szBuf; } return 0; } //------------------------------------------------------------------------- // Is the user right clicks on the tree, we should show a context menu // with the "What's This?" item in it. If the user selects the menu // item, we should launch help with the topic for the category. //------------------------------------------------------------------------- LRESULT OnRClickTree(int idCtrl, LPNMHDR pnmh, BOOL& bHandled) { // Determine if the right click was on a category in the tree view. CMSInfoCategory * pCategory = NULL; DWORD dwPoint = GetMessagePos(); TVHITTESTINFO tvhti; tvhti.pt.x = ((int)(short)LOWORD(dwPoint)); tvhti.pt.y = ((int)(short)HIWORD(dwPoint)); ::ScreenToClient(m_tree.m_hWnd, &tvhti.pt); // If it's on a tree view item, get the category pointer. HTREEITEM hti = TreeView_HitTest(m_tree.m_hWnd, &tvhti); if (hti != NULL) { TVITEM tvi; tvi.mask = TVIF_PARAM; tvi.hItem = hti; if (TreeView_GetItem(m_tree.m_hWnd, &tvi) && tvi.lParam) pCategory = (CMSInfoCategory *) tvi.lParam; } if (pCategory != NULL) { const UINT uFlags = TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD | TPM_RIGHTBUTTON; ::AfxSetResourceHandle(_Module.GetResourceInstance()); HMENU hmenu = ::LoadMenu(_Module.GetResourceInstance(), _T("IDR_WHAT_IS_THIS_MENU")); HMENU hmenuSub = ::GetSubMenu(hmenu, 0); if (hmenuSub) if (::TrackPopupMenu(hmenuSub, uFlags, ((int)(short)LOWORD(dwPoint)), ((int)(short)HIWORD(dwPoint)), 0, m_hWnd, NULL)) ShowCategoryHelp(pCategory); ::DestroyMenu(hmenu); } return 0; } LRESULT OnClickedSearchSelected(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { if (IsDlgButtonChecked(IDC_CHECKSEARCHSELECTED) == BST_CHECKED) CheckDlgButton(IDC_CHECKSEARCHCATSONLY, BST_UNCHECKED); return 0; } LRESULT OnClickedSearchCatsOnly(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { if (IsDlgButtonChecked(IDC_CHECKSEARCHCATSONLY) == BST_CHECKED) CheckDlgButton(IDC_CHECKSEARCHSELECTED, BST_UNCHECKED); return 0; } //------------------------------------------------------------------------- // Display the help file with the topic for the specified category shown. // If the category doesn't have a topic, check its parent categories. If // There are no topics all the way to the root, just show the help without // a specific topic. //------------------------------------------------------------------------- void ShowCategoryHelp(CMSInfoCategory * pCategory) { CString strTopic(_T("")); while (pCategory != NULL && strTopic.IsEmpty()) { strTopic = pCategory->GetHelpTopic(); pCategory = pCategory->GetParent(); } if (strTopic.IsEmpty()) ::HtmlHelp(m_hWnd, _T("msinfo32.chm"), HH_DISPLAY_TOPIC, NULL); else ::HtmlHelp(m_hWnd, _T("msinfo32.chm"), HH_DISPLAY_TOPIC, (DWORD_PTR)(LPCTSTR)strTopic); } //------------------------------------------------------------------------- // If the user sets the focus to the edit control, we should turn on the // copy command in the menu. //------------------------------------------------------------------------- LRESULT OnSetFocusEditFindWhat(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { if (m_fDoNotRun) return 0; SetMenuItems(); return 0; } LRESULT OnKillFocusEditFindWhat(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { if (m_fDoNotRun) return 0; SetMenuItems(); return 0; } }; #endif //__MSINFO_H_