//*************************************************************************** // // LOGVIEW.CPP // // Module: NLB Manager (client-side exe) // // Purpose: Implementation of the view of a log of events. // // Copyright (c)2001 Microsoft Corporation, All Rights Reserved // // History: // // 08/03/01 JosephJ Adapted from now defunct RightBottomView // //*************************************************************************** #include "precomp.h" #pragma hdrstop #include "private.h" IMPLEMENT_DYNCREATE( LogView, CListView ) BEGIN_MESSAGE_MAP( LogView, CListView ) ON_WM_KEYDOWN() // ON_NOTIFY(HDN_ITEMCLICK, 0, OnHeaderClock) ON_NOTIFY_REFLECT(NM_DBLCLK, OnDoubleClick) // ON_NOTIFY(NM_CLICK, 1, OnDoubleClick) // ON_NOTIFY(NM_KEYDOWN, 1, OnDoubleClick) END_MESSAGE_MAP() LogView::LogView() : m_fPrepareToDeinitialize(FALSE) { InitializeCriticalSection(&m_crit); } LogView::~LogView() { DeleteCriticalSection(&m_crit); } Document* LogView::GetDocument() { return ( Document *) m_pDocument; } void LogView::OnInitialUpdate() { CListCtrl& ctrl = GetListCtrl(); // // set images for this view. // ctrl.SetImageList( GetDocument()->m_images48x48, LVSIL_SMALL ); // // set the style, we only want report // view // // get present style. LONG presentStyle; presentStyle = GetWindowLong( m_hWnd, GWL_STYLE ); // Set the last error to zero to avoid confusion. // See sdk for SetWindowLong. SetLastError(0); // set new style. SetWindowLong( m_hWnd, GWL_STYLE, // presentStyle | LVS_REPORT | WS_TILED | WS_CAPTION // presentStyle | LVS_REPORT | WS_CAPTION // presentStyle | LVS_REPORT | WS_DLGFRAME presentStyle | LVS_REPORT| LVS_NOSORTHEADER ); // SetWindowText(L"Log view"); ctrl.InsertColumn(0, GETRESOURCEIDSTRING( IDS_HEADER_LOG_TYPE), LVCFMT_LEFT, Document::LV_COLUMN_TINY ); ctrl.InsertColumn(1, GETRESOURCEIDSTRING( IDS_HEADER_LOG_DATE), LVCFMT_LEFT, Document::LV_COLUMN_SMALL ); ctrl.InsertColumn(2, GETRESOURCEIDSTRING( IDS_HEADER_LOG_TIME), LVCFMT_LEFT, Document::LV_COLUMN_SMALLMEDIUM ); ctrl.InsertColumn(3, GETRESOURCEIDSTRING( IDS_HEADER_LOG_CLUSTER), LVCFMT_LEFT, Document::LV_COLUMN_MEDIUM); ctrl.InsertColumn(4, GETRESOURCEIDSTRING( IDS_HEADER_LOG_HOST), LVCFMT_LEFT, Document::LV_COLUMN_LARGE); ctrl.InsertColumn(5, GETRESOURCEIDSTRING( IDS_HEADER_LOG_TEXT), LVCFMT_LEFT, Document::LV_COLUMN_GIGANTIC); ctrl.SetExtendedStyle( ctrl.GetExtendedStyle() | LVS_EX_FULLROWSELECT ); IUICallbacks::LogEntryHeader Header; // we will register // with the document class, // as we are the status pane // and status is reported via us. GetDocument()->registerLogView( this ); // // Log a starting-nlbmgr message (needs to be after the registration, // because if file-logging is enabled and there is an error writing // the the file, that code tries to log an error message -- that message // would get dropped if we have not yet registered. // LogString( &Header, GETRESOURCEIDSTRING(IDS_LOG_NLBMANAGER_STARTED) ); // // Make this initial entry the selected one. We want some row highlighted // to provide a visual cue as we move between views using keystrokes. // GetListCtrl().SetItemState(0, LVIS_SELECTED, LVIS_SELECTED); } // // Log a message in human-readable form. // void LogView::LogString( IN const IUICallbacks::LogEntryHeader *pHeader, IN const wchar_t *szText ) { mfn_Lock(); IUICallbacks::LogEntryType Type = pHeader->type; const wchar_t *szCluster = pHeader->szCluster; const wchar_t *szHost = pHeader->szHost; const wchar_t *szDetails = pHeader->szDetails; static LONG sSequence=0; LONG Seq; LPCWSTR szType = L""; UINT Image = 0; CListCtrl& ctrl = GetListCtrl(); WCHAR szSequenceNo[64]; LPCWSTR szDate = NULL; LPCWSTR szTime = NULL; _bstr_t bstrTime; _bstr_t bstrDate; INT nItem = ctrl.GetItemCount(); _bstr_t bstrText = _bstr_t(szText); BOOL fLogTrimError = FALSE; if (m_fPrepareToDeinitialize) { goto end_unlock; } // // If total count exceeds our limit by 100 entries, // get rid of the first 100 entries and log a message saying we've // got rid of those entries. // #define MAX_LOG_ITEMS_IN_LIST 1000 #define LOG_ITEMS_TO_DELETE 100 if (nItem > MAX_LOG_ITEMS_IN_LIST) { for (int i=0;i < LOG_ITEMS_TO_DELETE;i++) { LPCWSTR szDetails = (LPCWSTR) ctrl.GetItemData(0); delete szDetails; // may be NULL ctrl.DeleteItem(0); } // // Get the updated count... // nItem = ctrl.GetItemCount(); fLogTrimError = TRUE; } if (szCluster == NULL) { szCluster = L""; } if (szHost == NULL) { szHost = L""; } if (szDetails != NULL) { // // There is detail-info. We make a copy of it and save it // as the lParam structure. TODO -- copy the // interface and other info as well. // UINT uLen = wcslen(szDetails)+1; // +1 for ending NULL; WCHAR *szTmp = new WCHAR[uLen]; if (szTmp!=NULL) { CopyMemory(szTmp, szDetails, uLen*sizeof(WCHAR)); } szDetails = szTmp; // could be NULL on mem failure. if (szDetails != NULL) { // // We'll add a hint to the text to double click for details... // bstrText += GETRESOURCEIDSTRING( IDS_LOG_DETAILS_HINT); LPCWSTR szTmp1 = bstrText; if (szTmp1 != NULL) { szText = szTmp1; } } } GetTimeAndDate(REF bstrTime, REF bstrDate); szTime = bstrTime; szDate = bstrDate; if (szTime == NULL) szTime = L""; if (szDate == NULL) szDate = L""; Seq = InterlockedIncrement(&sSequence); StringCbPrintf(szSequenceNo, sizeof(szSequenceNo), L"%04lu", Seq); switch(Type) { case IUICallbacks::LOG_ERROR: Image = Document::ICON_ERROR; szType = GETRESOURCEIDSTRING(IDS_PARM_ERROR); break; case IUICallbacks::LOG_WARNING: Image = Document::ICON_WARNING; szType = GETRESOURCEIDSTRING(IDS_PARM_WARN); break; case IUICallbacks::LOG_INFORMATIONAL: Image = Document::ICON_INFORMATIONAL; szType = GETRESOURCEIDSTRING(IDS_LOGTYPE_INFORMATION); break; } ctrl.InsertItem( LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM, // nMask nItem, szSequenceNo, // text 0, // nState 0, // nStateMask Image, (LPARAM) szDetails // lParam ); ctrl.SetItem( nItem, 1,// nSubItem LVIF_TEXT, // nMask szDate, // lpszItem 0, // nImage 0, // nState 0, // nStateMask 0 // lParam ); ctrl.SetItem( nItem, 2,// nSubItem LVIF_TEXT, // nMask szTime, // lpszItem 0, // nImage 0, // nState 0, // nStateMask 0 // lParam ); ctrl.SetItem( nItem, 3,// nSubItem LVIF_TEXT, // nMask szCluster, // lpszItem 0, // nImage 0, // nState 0, // nStateMask 0 // lParam ); ctrl.SetItem( nItem, 4,// nSubItem LVIF_TEXT, // nMask szHost, // lpszItem 0, // nImage 0, // nState 0, // nStateMask 0 // lParam ); ctrl.SetItem( nItem, 5,// nSubItem LVIF_TEXT, // nMask szText, // lpszItem 0, // nImage 0, // nState 0, // nStateMask 0 // lParam ); ctrl.EnsureVisible(nItem, FALSE); // FALSE == partial visibility not ok. WCHAR logBuf[2*MAXSTRINGLEN]; StringCbPrintf( logBuf, sizeof(logBuf), L"%ls\t%ls\t%ls\t%ls\t%ls\t%ls\t%ls\t\n", szSequenceNo, szType, szDate, szTime, szCluster, szHost, szText ); GetDocument()->logStatus(logBuf); if (szDetails != NULL) { GetDocument()->logStatus((LPWSTR) szDetails); } end_unlock: mfn_Unlock(); if (fLogTrimError) { static LONG ReentrancyCount; // // We're going to call ourselves recursively, better make sure that // we will NOT try to trim the log this time, or else we'll end // up in a recursive loop. // if (InterlockedIncrement(&ReentrancyCount)==1) { IUICallbacks::LogEntryHeader Header; Header.type = IUICallbacks::LOG_WARNING; this->LogString( &Header, GETRESOURCEIDSTRING(IDS_LOG_TRIMMING_LOG_ENTRIES) ); } InterlockedDecrement(&ReentrancyCount); } return; } void LogView::OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags ) { CListView::OnKeyDown(nChar, nRepCnt, nFlags); if (nChar == VK_TAB || nChar == VK_F6) { // if (::GetAsyncKeyState(VK_SHIFT) > 0) if (! (::GetAsyncKeyState(VK_SHIFT) & 0x8000)) { GetDocument()->SetFocusNextView(this, nChar); } else { GetDocument()->SetFocusPrevView(this, nChar); } // DummyAction(L"LogView TAB!"); } else if (nChar == VK_RETURN) { POSITION pos = NULL; CListCtrl& ctrl = GetListCtrl(); pos = ctrl.GetFirstSelectedItemPosition(); if(pos != NULL) { int index = ctrl.GetNextSelectedItem( pos ); mfn_DisplayDetails(index); } this->SetFocus(); } } void LogView::OnDoubleClick(NMHDR* pNotifyStruct, LRESULT* pResult) { LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW) pNotifyStruct; // to get index mfn_DisplayDetails(lpnmlv->iItem); } void LogView::mfn_DisplayDetails(int iItem) { LPCWSTR szCaption = NULL; WCHAR rgEvent[64]; WCHAR rgDate[256]; WCHAR rgTime[256]; WCHAR rgCluster[256]; WCHAR rgHost[256]; WCHAR rgSummary[256]; CListCtrl& ctrl = GetListCtrl(); LPCWSTR szDetails = (LPCWSTR) ctrl.GetItemData(iItem); CLocalLogger logCaption; if (szDetails == NULL) { goto end; } UINT uLen; uLen = ctrl.GetItemText(iItem, 0, rgEvent, ASIZE(rgEvent)-1); rgEvent[uLen]=0; logCaption.Log(IDS_LOG_ENTRY_DETAILS, rgEvent); szCaption = logCaption.GetStringSafe(); uLen = ctrl.GetItemText(iItem, 1, rgDate, ASIZE(rgDate)-1); rgDate[uLen]=0; uLen = ctrl.GetItemText(iItem, 2, rgTime, ASIZE(rgTime)-1); rgTime[uLen]=0; uLen = ctrl.GetItemText(iItem, 3, rgCluster, ASIZE(rgCluster)-1); rgTime[uLen]=0; uLen = ctrl.GetItemText(iItem, 4, rgHost, ASIZE(rgHost)-1); rgHost[uLen]=0; uLen = ctrl.GetItemText(iItem, 5, rgSummary, ASIZE(rgSummary)-1); rgTime[uLen]=0; if (szDetails != NULL) { // // We need to REMOVE the hint text we added to the summary // In the LogView list entry (see LogView::LogString, or search // for IDS_LOG_DETAILS_HINT). // _bstr_t bstrHint = GETRESOURCEIDSTRING( IDS_LOG_DETAILS_HINT); LPCWSTR szHint = bstrHint; if (szHint != NULL) { LPWSTR szLoc = wcsstr(rgSummary, szHint); if (szLoc != NULL) { // // Found the hint -- chop it off.. // *szLoc = 0; } } } { DetailsDialog Details( GetDocument(), szCaption, // Caption rgDate, rgTime, rgCluster, rgHost, NULL, // TODO: rgInterface rgSummary, szDetails, this // parent ); (void) Details.DoModal(); } end: return; } void LogView::mfn_Lock(void) { // // See notes.txt entry // 01/23/2002 JosephJ DEADLOCK in Leftview::mfn_Lock // for the reason for this convoluted implementation of mfn_Lock // while (!TryEnterCriticalSection(&m_crit)) { ProcessMsgQueue(); Sleep(100); } } void LogView::Deinitialize(void) { ASSERT(m_fPrepareToDeinitialize); // DummyAction(L"LogView::Deinitialize"); }