// LogDlg.cpp : Implementation of CShutdownApp and DLL registration. #include "stdafx.h" #include "LogDlg.h" #include using std::replace; _COM_SMARTPTR_TYPEDEF(INightSecErrorFix, __uuidof(INightSecErrorFix)); #include #include #define IMAGE_OKAY 0 #define IMAGE_ERROR 1 ///////////////////////////////////////////////////////////////////////////// // LRESULT CLogDlg::OnHide(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { // We use this event to keep the program open // when the user is looking at the log if(m_hEvent) SetEvent(m_hEvent); ShowWindow(SW_HIDE); return 0; } LRESULT CLogDlg::OnSave(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { // I think this is good enough. ie: saving on the desktop. if(IDYES == MessageBox(_T("The log will be saved on your desktop. Continue?"), _T("Save Log"), MB_YESNO | MB_ICONQUESTION)) SaveLog(); return 0; } LRESULT CLogDlg::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { OnHide(0, 0, NULL, bHandled); return 0; } /*LRESULT CLogDlg::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { return 0; } */ ////////////////////////////////////////////////////////////////// // InitDialog (what more can I say) LRESULT CLogDlg::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { m_hIcon = ::LoadIcon(_Module.m_hInst, MAKEINTRESOURCE(IDI_ERR)); SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // Create the small icon image list. m_ImageListSmall.Create(IDB_ERRORS, 16, 0, // list won't grow RGB(255, 0, 255)); // Set Mask color for Image Lists m_ImageListSmall.SetBkColor(CLR_NONE); m_ctlErrors = GetDlgItem(IDC_ERROR_LOG); // Associate the image lists with the list view control. m_ctlErrors.SetImageList(m_ImageListSmall, LVSIL_SMALL); // This sets the sizing of the dialog SetMin(IDC_MINSIZE); SetControlSizing(ID_SAVE, SD_HORZ | SD_MOVING); SetControlSizing(ID_HIDE, SD_HORZ | SD_MOVING); SetControlSizing(IDC_ERROR_LOG, SD_HORZ | SD_VERT | SD_SIZING); // We need at least one bogus column since we're using // report view LV_COLUMN lvC; // list view column structure // Now initialize the columns you will need. // Initialize the LV_COLUMN structure. // The mask specifies that the fmt, width, pszText, and subitem members // of the structure are valid. lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; lvC.fmt = LVCFMT_LEFT; // left-align column lvC.cx = 200; // width of column in pixels lvC.iSubItem = 0; lvC.pszText = _T("Errors"); if (m_ctlErrors.InsertColumn(0, &lvC) == -1) return true; // Make sure the column is the width of control RECT rect; m_ctlErrors.GetClientRect(&rect); m_ctlErrors.SetColumnWidth(0, rect.right); // Move Window to previous position if(!LoadPosition(_Module.m_settings)) // If fails then Center it on the desktop CenterWindow(GetDesktopWindow()); // This event gets set when we're hidden m_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } LRESULT CLogDlg::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { // Clear All Items (which also releases the COM Objects) while(m_ctlErrors.GetItemCount()) RemoveItem(0); // Just in case set the event if(m_hEvent) SetEvent(m_hEvent); // Put window position in registry for next time SavePosition(_Module.m_settings); return 0; } ////////////////////////////////////////////////////////////////// // Gives the item a tick or cross image depending on bState bool CLogDlg::AddItem(INightSecError* pError) { USES_CONVERSION; if(!pError) return false; // Get the Description of the Error BSTR bsTemp; HRESULT hr = pError->get_Description(&bsTemp); if(FAILED(hr)) return false; // Clean up any new lines replace(bsTemp, bsTemp + SysStringLen(bsTemp), L'\n', L' '); replace(bsTemp, bsTemp + SysStringLen(bsTemp), L'\r', L' '); // Make sure object stays around pError->AddRef(); // Create or Show the Log Window ShowLog(); // Setup Structures LV_ITEM lv; lv.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM; lv.iSubItem = 0; lv.iItem = m_ctlErrors.GetItemCount(); lv.iImage = 1; lv.lParam = (LPARAM)pError; lv.pszText = OLE2T(bsTemp); // Set right image for item return m_ctlErrors.InsertItem(&lv) ? true : false; } void CLogDlg::ShowLog() { // If not created then Create if(!IsWindow()) Create(m_hWndParent, NULL); // Make sure it's Visible if(!IsWindowVisible()) ShowWindow(SW_SHOW); // Make sure event is not signalled so we don't just close by self ResetEvent(m_hEvent); } void CLogDlg::WaitLog() { // Prompt the user UINT nRet = MessageBoxTimeout(g_site.m_log, _T("There were errors during Secure Shutdown. \n\nWould you like to pause a moment before continuing?"), _T("Secure Shutdown"), MB_YESNO | MB_ICONQUESTION, 10000, 5000); if(nRet == IDYES) { // Display the Log Window ShowLog(); // Wait for the Event if(m_hEvent) WaitForAndIdle(1, &m_hEvent, INFINITE); } // If we time out then save the log on the desktop else if(nRet == IDTIMEOUT) if(!_Module.m_settings.GetInt(NO_LOG_KEY, false)) SaveLog(); } HRESULT CLogDlg::SaveLog(/*const string& sFile*/) { string sFile; LPITEMIDLIST pidl; HRESULT hr; // Get Desktop PIDL hr = SHGetSpecialFolderLocation(*this, CSIDL_DESKTOPDIRECTORY, &pidl); if(FAILED(hr)) return 0; // Get the Folder from That if(!SHGetPathFromIDList(pidl, sFile.get_buffer(MAX_PATH))) return 0; sFile.release_buffer(); // This is the file name // LIMITATION: Can't be changed sFile += _T("\\Secure Shutdown Log.txt"); // Open the File HANDLE hFile = NULL; if((hFile = CreateFile(sFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) return HRESULT_FROM_WIN32(::GetLastError()); int nPos = -1; DWORD dwWritten; // Always use ANSI here // LIMITATION: Only errors up to 1K in length char* szText = new char[1024]; // Write Header char* szTemp = "SECURE SHUTDOWN ERROR LOG\r\n\r\n"; WriteFile(hFile, szTemp, strlen(szTemp), &dwWritten, NULL); LVITEMA lvi; memset(&lvi, 0, sizeof(LVITEMA)); while((nPos = m_ctlErrors.GetNextItem(nPos, 0)) != -1) { // We need all this crazy code here to make sure // We get ANSI text in UNICODE Builds. lvi.mask = LVIF_TEXT | LVIF_IMAGE; lvi.iItem = nPos; lvi.iSubItem = 0; lvi.cchTextMax = 1024; lvi.pszText = szText; lvi.iImage = 0; ::SendMessageA(m_ctlErrors, LVM_GETITEMA, 0, (LPARAM)&lvi); // Only write errors (No Fixed) if(lvi.iImage != IMAGE_OKAY) { // Write it to the file WriteFile(hFile, szText, strlen(szText), &dwWritten, NULL); WriteFile(hFile, "\r\n", 2, &dwWritten, NULL); } } // Close the File CloseHandle(hFile); return S_OK; } bool CLogDlg::HasErrors() { if(!IsWindow()) return false; // If we're created then we've got errors int nPos = -1; LV_ITEM lv; while((nPos = m_ctlErrors.GetNextItem(nPos, LVNI_ALL)) != -1) { lv.mask = LVIF_IMAGE; lv.iItem = nPos; lv.iSubItem = 0; m_ctlErrors.GetItem(&lv); if(lv.iImage == IMAGE_ERROR) return true; } return false; } LRESULT CLogDlg::OnListRClick(int idCtrl, LPNMHDR pnmh, BOOL& bHandled) { // First of all get the mouse incase we move (unlikely) POINT ptMouse; ::GetCursorPos(&ptMouse); LVHITTESTINFO lvHit; lvHit.pt = ptMouse; lvHit.flags = LVHT_ONITEM; m_ctlErrors.ScreenToClient(&lvHit.pt); int nIndex = m_ctlErrors.HitTest(&lvHit); // If we didn't click on an item then... if(nIndex == -1) { // ...Deselect All for(int nCnt = 0; nCnt < m_ctlErrors.GetItemCount(); nCnt ++) // Set right state for item ListView_SetItemState(m_ctlErrors, nCnt, 0, (UINT)-1); return 0; } // Focus on Current Item ListView_SetItemState(m_ctlErrors, nIndex, LVIS_SELECTED | LVIS_FOCUSED, LVIS_STATEIMAGEMASK); // Load the Menu HMENU hMenu = ::LoadMenu(_Module.m_hInstResource, MAKEINTRESOURCE(IDR_ERROR_MENU)); if (!hMenu) return 0; HMENU hSubMenu = GetSubMenu(hMenu, 0); if (!hSubMenu) { DestroyMenu(hMenu); return 0; } // Get a COM Pointer to the current Error INightSecErrorFixPtr pFix = (INightSecError*)m_ctlErrors.GetItemData(nIndex); if(pFix) { // Check if Fixable BOOL bFixable = FALSE; if(SUCCEEDED(pFix->get_Fixable(&bFixable)) && bFixable) ::EnableMenuItem(hSubMenu, ID_ERROR_FIX, MF_BYCOMMAND | MF_ENABLED); // Check if Retryable BOOL bRetryable = FALSE; if(SUCCEEDED(pFix->get_Retryable(&bRetryable)) && bRetryable) ::EnableMenuItem(hSubMenu, ID_ERROR_RETRY, MF_BYCOMMAND | MF_ENABLED); } // Make the default (same as double-click) Retry SetMenuDefaultItem(hSubMenu, ID_ERROR_RETRY, FALSE); // Show the Menu ::TrackPopupMenu(hSubMenu, 0, ptMouse.x, ptMouse.y, 0, m_hWnd, NULL); ::PostMessage(m_hWnd, WM_USER, 0, 0); return 0; } LRESULT CLogDlg::OnErrorFix(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { // Fix all selected items int nPos = -1; while((nPos = m_ctlErrors.GetNextItem(nPos, LVIS_SELECTED)) != -1) FixItem(nPos); return 0; } LRESULT CLogDlg::OnErrorRetry(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { // Retry all selected Items int nPos = -1; while((nPos = m_ctlErrors.GetNextItem(nPos, LVIS_SELECTED)) != -1) RetryItem(nPos); return 0; } LRESULT CLogDlg::OnErrorDelete(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { // Delete all selected items int nPos = -1; while((nPos = m_ctlErrors.GetNextItem(nPos, LVIS_SELECTED)) != -1) RemoveItem(nPos); return 0; } HRESULT CLogDlg::FixItem(int nIndex) { USES_CONVERSION; // Extract COM Pointer from ListCtrl INightSecErrorFixPtr pFix = (INightSecError*)m_ctlErrors.GetItemData(nIndex); if(pFix == NULL) return 0; // Check if actually fixable BOOL bFixable = FALSE; if(FAILED(pFix->get_Fixable(&bFixable)) || !bFixable) return 0; // Fix it HCURSOR hOldCursor = ::SetCursor(::LoadCursor(NULL, IDC_WAIT)); HRESULT hr = pFix->Fix(); ::SetCursor(hOldCursor); INightSecErrorPtr pError = pFix; if(pError == NULL) return 0; // Get New Error Messages BSTR bsTemp; pError->get_Description(&bsTemp); // Clean up newlines replace(bsTemp, bsTemp + SysStringLen(bsTemp), L'\n', L' '); replace(bsTemp, bsTemp + SysStringLen(bsTemp), L'\r', L' '); // Set Image and Text LV_ITEM lv; lv.mask = LVIF_IMAGE | LVIF_TEXT; lv.iSubItem = 0; lv.iItem = nIndex; lv.pszText = OLE2T(bsTemp); lv.iImage = SUCCEEDED(hr) ? 0 : 1; // Set right image and text for item m_ctlErrors.SetItem(&lv); return hr; } HRESULT CLogDlg::RetryItem(int nIndex) { USES_CONVERSION; // Extract COM Pointer from List Ctrl INightSecErrorFixPtr pFix = (INightSecError*)m_ctlErrors.GetItemData(nIndex); if(pFix == NULL) return 0; // Check if we can actually Retry BOOL bRetryable = FALSE; if(FAILED(pFix->get_Retryable(&bRetryable)) || !bRetryable) return 0; // Retry it HCURSOR hOldCursor = ::SetCursor(::LoadCursor(NULL, IDC_WAIT)); HRESULT hr = pFix->Retry(); ::SetCursor(hOldCursor); INightSecErrorPtr pError = pFix; if(pError == NULL) return 0; // Get new Error Messages BSTR bsTemp; pError->get_Description(&bsTemp); // Clean up newlines replace(bsTemp, bsTemp + SysStringLen(bsTemp), L'\n', L' '); replace(bsTemp, bsTemp + SysStringLen(bsTemp), L'\r', L' '); // Set Image and Text LV_ITEM lv; lv.mask = LVIF_IMAGE | LVIF_TEXT; lv.iSubItem = 0; lv.iItem = nIndex; lv.pszText = OLE2T(bsTemp); lv.iImage = SUCCEEDED(hr) ? IMAGE_OKAY : IMAGE_ERROR; // Set right image and text for item m_ctlErrors.SetItem(&lv); return hr; } bool CLogDlg::RemoveItem(int nIndex) { // Release COM Ptr INightSecError* pError = (INightSecError*)m_ctlErrors.GetItemData(nIndex); if(pError) pError->Release(); // Remove it return m_ctlErrors.DeleteItem(nIndex) ? true : false; } void CLogDlg::Init(HWND hWndParent) { m_hWndParent = hWndParent; }