/* Copyright 1999 Microsoft Corporation Logging for MessageBoxes and the comment button (aka the "lame" button). Walter Smith (wsmith) Rajesh Soy (nsoy) - cleaned up code. 5/5/2000 Rajesh Soy (nsoy) - rearranged code, added messagebox text handling. 6/6/2000 */ #ifdef THIS_FILE #undef THIS_FILE #endif static char __szTraceSourceFile[] = __FILE__; #define THIS_FILE __szTraceSourceFile #include "stdafx.h" #define NOTRACE #include "logging.h" #include "resource.h" #include #include #include #include // MPC_Utils.h includes MPC_main.h, which apparently redefines ARRAYSIZE which // causes a compiler warning. #ifdef ARRAYSIZE #undef ARRAYSIZE #endif #include // // Globals // HINSTANCE ghDllInst; CComModule _Module; // // Routines Defined here // unsigned int __stdcall LogLameButtonThread(void* pvLogData); VOID WINAPI CommentReport( HWND hwnd, PVOID pv); // // CSurveyDlg: The Comments dialog implementation // class CSurveyDlg : public CAxDialogImpl { public: // // Constructor // CSurveyDlg() { ZeroMemory(&m_lldata, sizeof(m_lldata)); m_hwndTarget = NULL; m_bShowHyperlink = FALSE; m_crAnchor = RGB(0, 0, 0); ZeroMemory(&m_szHyperlinkText, sizeof(m_szHyperlinkText)); ZeroMemory(&m_szHyperlinkCmdLine, sizeof(m_szHyperlinkCmdLine)); m_bHyperlinkHasFocus = FALSE; } // // Destructor // ~CSurveyDlg() { if (m_lldata.pbImage != NULL) { free(m_lldata.pbImage); m_lldata.pbImage = NULL; } } enum { IDD = IDD_SURVEYDLG }; private: LAMELOGDATA m_lldata; // Data collected by LAMEBTN.dll HWND m_hwndTarget; // Window of dialog being commented on BOOL m_bShowHyperlink; // Set to TRUE if we should display the hyperlink on our dialog. COLORREF m_crAnchor; // Color of a hyperlink (anchor). TCHAR m_szHyperlinkText[256]; // Display string for the hyperlink. TCHAR m_szHyperlinkCmdLine[2048]; // Command line (obtained from the registry) to execute when the user clicks the hyperlink. TCHAR m_bHyperlinkHasFocus; // Set to TRUE when the hyperlink button (IDC_BUTTON_HYPERLINK) has focus. public: BEGIN_MSG_MAP(CSurveyDlg) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) MESSAGE_HANDLER(WM_DRAWITEM, OnDrawItem) MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor) COMMAND_ID_HANDLER(IDC_BUTTON_HYPERLINK, HyperlinkCmdHandler) COMMAND_ID_HANDLER(IDOK, OnOK) COMMAND_ID_HANDLER(IDCANCEL, OnCancel) END_MSG_MAP() // // Init: Initializes the comment Dialog // void Init( HWND hwndTarget, PSTACKTRACEDATA pstdata); // // OnInitDialog: Fills up the Comment Dialog structures when the dialog is displayed // LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); // // OnDrawItem: Draws owner-drawn controls (we have only one: IDC_BUTTON_HYPERLINK) // LRESULT OnDrawItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); // // OnSetCursor: Sets the cursor to the hand if the mouse is over IDC_BUTTON_HYPERLINK // LRESULT OnSetCursor(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); // // HyperlinkCmdHandler: Handles messages from the IDC_BUTTON_HYPERLINK button // LRESULT HyperlinkCmdHandler(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled); // // OnOK: Submits Comment // LRESULT OnOK(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled); // // OnCancel: Cancel the comment // LRESULT OnCancel(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled); private: // // CSurveyDlg::IsMessageBox: This routine returns TRUE if the hWnd passed is indeed // a message box // BOOL IsMessageBox(); // // GetInfoAndLogLameButton: Gathers the comment and callstack information, formats into XML // and uploads to server void GetInfoAndLogLameButton(); }; // // CSurveyDlg::Init: Initializes the comment Dialog // void CSurveyDlg::Init( HWND hwndTarget, // [in] Window of dialog being commented on PSTACKTRACEDATA pstdata // [in] Call Stack ) { TraceFunctEnter("CSurveyDlg::Init"); _ASSERT(hwndTarget != NULL); // // Save off the target window // m_hwndTarget = hwndTarget; // // Save off the call stack // m_lldata.pStackTrace = pstdata; // // Grab the image of the target window // DebugTrace(0, "Calling SetActiveWindow"); ::SetActiveWindow(hwndTarget); try { BYTE* pImageData = NULL; DebugTrace(0, "Calling GetWindowImage"); GetWindowImage(hwndTarget, &pImageData, &m_lldata.cbImage); m_lldata.pbImage = pImageData; } catch (HRESULT hr) { m_lldata.pbImage = NULL; m_lldata.cbImage = 0; } TraceFunctLeave(); } // // CSurveyDlg::OnInitDialog: Executes when Comments dialog is created // LRESULT CSurveyDlg::OnInitDialog( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { DWORD idMessage; TraceFunctEnter("OnInitDialog"); TCHAR szTitle[64]; // // Obtain the Title of the Window being commented // if (::GetWindowText(m_hwndTarget, szTitle, ARRAYSIZE(szTitle)) != 0) { idMessage = IDS_INTRO_WITH_TITLE; if (lstrlen(szTitle) == ARRAYSIZE(szTitle) - 1) { // Truncate the title a little more nicely TCHAR* p = &szTitle[ARRAYSIZE(szTitle) - 4]; *p++ = '.'; *p++ = '.'; *p++ = '.'; } } else { idMessage = IDS_INTRO; } // // Load localized sting for Comment intro // TCHAR szTemplate[1024]; LoadString(_Module.GetResourceInstance(), idMessage, szTemplate, ARRAYSIZE(szTemplate)); TCHAR* fmArgs[] = { szTitle }; TCHAR* szCommentIntro = NULL; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, szTemplate, 0, 0, (LPTSTR) &szCommentIntro, 1024, (va_list*) &fmArgs); if (szCommentIntro != NULL) { SetDlgItemText(IDC_STATIC_INTRO, szCommentIntro); LocalFree(szCommentIntro); } // // Set the maximum limits on the edit controls. // SendDlgItemMessage(IDC_EDIT_EMAIL_ADDRESS, EM_LIMITTEXT, MAX_EMAIL_ADDRESS_SIZE, 0); SendDlgItemMessage(IDC_EDIT_BETA_ID, EM_LIMITTEXT, MAX_BETA_ID_SIZE, 0); SendDlgItemMessage(IDC_EDIT_COMMENT, EM_LIMITTEXT, COMMENT_TEXT_SIZE, 0); // // Prepopulate the email address and beta ID edit controls with whatever // the user typed in last time. (We stored this in the registry the last // time they pressed the submit button.) // HKEY hKey; DWORD dwRet; if ((dwRet = RegOpenKeyEx(HKEY_CURRENT_USER, _T("Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments"), 0, KEY_QUERY_VALUE, &hKey)) != ERROR_SUCCESS) { DebugTrace(0, "No HKCU\\Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments key in the registry (RegOpenKeyEx returned %ld)", dwRet); } else { DWORD dwDataSize = sizeof(m_lldata.szEmailAddress); if ((dwRet = RegQueryValueEx(hKey, _T("Email Address"), NULL, NULL, (LPBYTE) m_lldata.szEmailAddress, &dwDataSize)) != ERROR_SUCCESS) { DebugTrace(0, "No Email Address value in the HKCU\\Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments registry key (RegQueryValueEx returned %ld)", dwRet); m_lldata.szEmailAddress[0] = 0; } else { DebugTrace(0, "Email address for this user in the registry: %s", m_lldata.szEmailAddress); SendDlgItemMessage(IDC_EDIT_EMAIL_ADDRESS, WM_SETTEXT, 0, (LPARAM) m_lldata.szEmailAddress); } dwDataSize = sizeof(m_lldata.szBetaID); if ((dwRet = RegQueryValueEx(hKey, _T("Beta ID"), NULL, NULL, (LPBYTE) m_lldata.szBetaID, &dwDataSize)) != ERROR_SUCCESS) { DebugTrace(0, "No Beta ID value in the HKCU\\Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments registry key (RegQueryValueEx returned %ld)", dwRet); m_lldata.szBetaID[0] = 0; } else { DebugTrace(0, "Beta ID for this user in the registry: %s", m_lldata.szBetaID); SendDlgItemMessage(IDC_EDIT_BETA_ID, WM_SETTEXT, 0, (LPARAM) m_lldata.szBetaID); } RegCloseKey(hKey); } // // Populate the event category dropdown. // TCHAR szEventCategoryString[1024]; LoadString(_Module.GetResourceInstance(), IDS_EVENT_CATEGORY_1, szEventCategoryString, ARRAYSIZE(szEventCategoryString)); SendDlgItemMessage(IDC_COMBO_EVENT_CATEGORY, CB_ADDSTRING, 0, (LPARAM) szEventCategoryString); LoadString(_Module.GetResourceInstance(), IDS_EVENT_CATEGORY_2, szEventCategoryString, ARRAYSIZE(szEventCategoryString)); SendDlgItemMessage(IDC_COMBO_EVENT_CATEGORY, CB_ADDSTRING, 0, (LPARAM) szEventCategoryString); LoadString(_Module.GetResourceInstance(), IDS_EVENT_CATEGORY_3, szEventCategoryString, ARRAYSIZE(szEventCategoryString)); SendDlgItemMessage(IDC_COMBO_EVENT_CATEGORY, CB_ADDSTRING, 0, (LPARAM) szEventCategoryString); LoadString(_Module.GetResourceInstance(), IDS_EVENT_CATEGORY_4, szEventCategoryString, ARRAYSIZE(szEventCategoryString)); SendDlgItemMessage(IDC_COMBO_EVENT_CATEGORY, CB_ADDSTRING, 0, (LPARAM) szEventCategoryString); LoadString(_Module.GetResourceInstance(), IDS_EVENT_CATEGORY_5, szEventCategoryString, ARRAYSIZE(szEventCategoryString)); SendDlgItemMessage(IDC_COMBO_EVENT_CATEGORY, CB_ADDSTRING, 0, (LPARAM) szEventCategoryString); LoadString(_Module.GetResourceInstance(), IDS_EVENT_CATEGORY_6, szEventCategoryString, ARRAYSIZE(szEventCategoryString)); SendDlgItemMessage(IDC_COMBO_EVENT_CATEGORY, CB_ADDSTRING, 0, (LPARAM) szEventCategoryString); // // Populate the severity dropdown. // TCHAR szSeverityString[1024]; LoadString(_Module.GetResourceInstance(), IDS_SEVERITY_1, szSeverityString, ARRAYSIZE(szSeverityString)); SendDlgItemMessage(IDC_COMBO_SEVERITY, CB_ADDSTRING, 0, (LPARAM) szSeverityString); LoadString(_Module.GetResourceInstance(), IDS_SEVERITY_2, szSeverityString, ARRAYSIZE(szSeverityString)); SendDlgItemMessage(IDC_COMBO_SEVERITY, CB_ADDSTRING, 0, (LPARAM) szSeverityString); LoadString(_Module.GetResourceInstance(), IDS_SEVERITY_3, szSeverityString, ARRAYSIZE(szSeverityString)); SendDlgItemMessage(IDC_COMBO_SEVERITY, CB_ADDSTRING, 0, (LPARAM) szSeverityString); LoadString(_Module.GetResourceInstance(), IDS_SEVERITY_4, szSeverityString, ARRAYSIZE(szSeverityString)); SendDlgItemMessage(IDC_COMBO_SEVERITY, CB_ADDSTRING, 0, (LPARAM) szSeverityString); // // Initialize m_crAnchor. Ideally we could read this from IE's registry, // but many other dialogs don't do that and we just don't have time now. // We'll default to pure blue, since that is the IE default. // m_crAnchor = RGB(0, 0, 255); // // Initialize m_szHyperlinkText from the IDS_HYPERLINK_TEXT string resource. // m_bShowHyperlink = TRUE; if (LoadString(_Module.GetResourceInstance(), IDS_HYPERLINK_TEXT, m_szHyperlinkText, ARRAYSIZE(m_szHyperlinkText)) == 0) m_bShowHyperlink = FALSE; // // Initialize m_szHyperlinkCmdLine from the registry. // if (m_bShowHyperlink) if ((dwRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments"), 0, KEY_QUERY_VALUE, &hKey)) != ERROR_SUCCESS) { DebugTrace(0, "No HKCU\\Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments key in the registry (RegOpenKeyEx returned %ld)", dwRet); m_bShowHyperlink = FALSE; } else { DWORD dwDataSize = sizeof(m_szHyperlinkCmdLine); if ((dwRet = RegQueryValueEx(hKey, _T("Hyperlink command line"), NULL, NULL, (LPBYTE) m_szHyperlinkCmdLine, &dwDataSize)) != ERROR_SUCCESS) { DebugTrace(0, "No Hyperlink command line value in the HKLM\\Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments registry key (RegQueryValueEx returned %ld)", dwRet); m_szHyperlinkCmdLine[0] = 0; m_bShowHyperlink = FALSE; } else DebugTrace(0, "Hyperlink command line in the registry: %s", m_szHyperlinkCmdLine); RegCloseKey(hKey); } // // Disable the hyperlink-related controls if we cannot successfully display // and launch the hyperlink. // if (!m_bShowHyperlink) { ::SetWindowPos(GetDlgItem(IDC_STATIC_HELP_AND_SUPPORT_1), 0, 0, 0, 0, 0, SWP_HIDEWINDOW + SWP_NOMOVE + SWP_NOSIZE + SWP_NOZORDER); ::SetWindowPos(GetDlgItem(IDC_STATIC_HELP_AND_SUPPORT_2), 0, 0, 0, 0, 0, SWP_HIDEWINDOW + SWP_NOMOVE + SWP_NOSIZE + SWP_NOZORDER); ::SetWindowPos(GetDlgItem(IDC_BUTTON_HYPERLINK), 0, 0, 0, 0, 0, SWP_HIDEWINDOW + SWP_NOMOVE + SWP_NOSIZE + SWP_NOZORDER); } // // Set the focus to: // Email address if the user hasn't entered it before. // Otherwise beta ID if the user hasn't entered it before. // Otherwise the event category dropdown. // if (SendDlgItemMessage(IDC_EDIT_EMAIL_ADDRESS, WM_GETTEXTLENGTH, 0, 0) == 0) GotoDlgCtrl(GetDlgItem(IDC_EDIT_EMAIL_ADDRESS)); else if (SendDlgItemMessage(IDC_EDIT_BETA_ID, WM_GETTEXTLENGTH, 0, 0) == 0) GotoDlgCtrl(GetDlgItem(IDC_EDIT_BETA_ID)); else GotoDlgCtrl(GetDlgItem(IDC_COMBO_EVENT_CATEGORY)); m_bHyperlinkHasFocus = FALSE; TraceFunctLeave(); bHandled = TRUE; return FALSE; // We set the focus, return FALSE to prevent O/S from setting focus. } // // CSurveyDlg::OnDrawItem: Draws owner-drawn controls (we have only one: IDC_BUTTON_HYPERLINK) // LRESULT CSurveyDlg::OnDrawItem( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { TraceFunctEnter("OnDrawItem"); HWND hwndHyperlink = NULL; HDC hdcHyperlink = NULL; HFONT hfontHelpAndSupport2 = NULL; HFONT hfontHyperlinkOld = NULL; HFONT hfontHyperlinkNew = NULL; LOGFONT lfHyperlinkNew; RECT rectHyperlink; // // If we cannot successfully display and launch the hyperlink, return now. // if (wParam != IDC_BUTTON_HYPERLINK) goto Done; if (!m_bShowHyperlink) goto Done; // // Get the DC for IDC_BUTTON_HYPERLINK. // if ((hwndHyperlink = GetDlgItem(IDC_BUTTON_HYPERLINK)) == NULL) goto Done; if ((hdcHyperlink = ::GetDC(hwndHyperlink)) == NULL) goto Done; // // Get the font used to draw IDC_STATIC_HELP_AND_SUPPORT_2. Create a new // font based on that one but with underline. // if ((hfontHelpAndSupport2 = (HFONT) SendDlgItemMessage(IDC_STATIC_HELP_AND_SUPPORT_2, WM_GETFONT, 0, 0)) == NULL) goto Done; if (!GetObject(hfontHelpAndSupport2, sizeof(LOGFONT), &lfHyperlinkNew)) goto Done; lfHyperlinkNew.lfUnderline = TRUE; if ((hfontHyperlinkNew = CreateFontIndirect(&lfHyperlinkNew)) == NULL) goto Done; // // For IDC_BUTTON_HYPERLINK, select the new underline font but don't delete // the original font. // hfontHyperlinkOld = (HFONT) SelectObject(hdcHyperlink, hfontHyperlinkNew); if ((hfontHyperlinkOld == NULL) || (hfontHyperlinkOld == (HFONT) (ULONG_PTR)GDI_ERROR)) goto Done; // // Draw the text using the IE anchor color. Center the text horizontally in // the button. // SetTextColor(hdcHyperlink, m_crAnchor); SetTextAlign(hdcHyperlink, TA_CENTER | TA_TOP); SetBkMode(hdcHyperlink, TRANSPARENT); ::GetClientRect(hwndHyperlink, &rectHyperlink); ExtTextOut(hdcHyperlink, (rectHyperlink.right / 2) + 1, 0, 0, NULL, m_szHyperlinkText, _tcslen(m_szHyperlinkText), NULL); // // For IDC_BUTTON_HYPERLINK, select the original font. // SelectObject(hdcHyperlink, hfontHyperlinkOld); // // Clean up. // Done: // // Delete the new font we created. // if (hfontHyperlinkNew != NULL) DeleteObject(hfontHyperlinkNew); // // Release the DC for IDC_BUTTON_HYPERLINK. // if (hdcHyperlink != NULL) ::ReleaseDC(hwndHyperlink, hdcHyperlink); // // Return. // TraceFunctLeave(); bHandled = TRUE; return TRUE; } // // CSurveyDlg::OnSetCursor: Sets the cursor to the hand if the mouse is over the hyperlink window // LRESULT CSurveyDlg::OnSetCursor( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { TraceFunctEnter("OnSetCursor"); if (m_bShowHyperlink && ((HWND) wParam == GetDlgItem(IDC_BUTTON_HYPERLINK))) { HCURSOR hcursor = LoadCursor(NULL, IDC_HAND); SetCursor(hcursor); TraceFunctLeave(); bHandled = TRUE; return TRUE; } TraceFunctLeave(); bHandled = FALSE; return FALSE; } // // HyperlinkCmdHandler: Handles messages from the IDC_BUTTON_HYPERLINK button // LRESULT CSurveyDlg::HyperlinkCmdHandler( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled ) { TraceFunctEnter("HyperlinkCmdHandler"); // // If we gained or lost focus, update the focus rectangle. // if ((wNotifyCode == BN_SETFOCUS) || (wNotifyCode == BN_KILLFOCUS)) { HDC hdc; RECT rect; hdc = ::GetDC(hWndCtl); ::GetClientRect(hWndCtl, &rect); DrawFocusRect(hdc, &rect); ::ReleaseDC(hWndCtl, hdc); m_bHyperlinkHasFocus = (wNotifyCode == BN_SETFOCUS); } // // If we were clicked, launch the hyperlink program. // else if ((wNotifyCode = BN_CLICKED) || (wNotifyCode = BN_DOUBLECLICKED)) { DebugTrace(0, "User clicked IDC_BUTTON_HYPERLINK"); TCHAR szCmdLine[2048]; DWORD dwRet; dwRet = ExpandEnvironmentStrings(m_szHyperlinkCmdLine, szCmdLine, 2048); if ((dwRet == 0) || (dwRet > 2048)) { DebugTrace(0, "ExpandEnvironmentStrings() failed on cmdline \"%s\" with error %ld", m_szHyperlinkCmdLine, GetLastError()); TCHAR szMsg[1024]; TCHAR szTitle[1024]; if ((LoadString(_Module.GetResourceInstance(), IDS_HYPERLINK_PROGRAM_FAILED, szMsg, ARRAYSIZE(szMsg)) != 0) && (LoadString(_Module.GetResourceInstance(), IDS_HYPERLINK_PROGRAM_FAILED_TITLE, szTitle, ARRAYSIZE(szTitle)) != 0)) MessageBox(szMsg, szTitle, MB_OK); } else { TCHAR szWindowsDir[MAX_PATH]; STARTUPINFO si; PROCESS_INFORMATION pi; if (!GetSystemWindowsDirectory(szWindowsDir, MAX_PATH)) szWindowsDir[0] = 0; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); if (CreateProcess(NULL, szCmdLine, NULL, NULL, FALSE, 0, NULL, szWindowsDir, &si, &pi)) { DebugTrace(0, "CreateProcess() succeeded on cmdline \"%s\" with PID %ld, TID %ld", szCmdLine, pi.dwProcessId, pi.dwThreadId); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } else { DebugTrace(0, "CreateProcess() failed cmdline \"%s\" with error %ld", szCmdLine, GetLastError()); TCHAR szMsg[1024]; TCHAR szTitle[1024]; if ((LoadString(_Module.GetResourceInstance(), IDS_HYPERLINK_PROGRAM_FAILED, szMsg, ARRAYSIZE(szMsg)) != 0) && (LoadString(_Module.GetResourceInstance(), IDS_HYPERLINK_PROGRAM_FAILED_TITLE, szTitle, ARRAYSIZE(szTitle)) != 0)) MessageBox(szMsg, szTitle, MB_OK); } } } TraceFunctLeave(); bHandled = TRUE; return 0; } // // CSurveyDlg::OnOK: Executes when user has done typing the comment // and has hit submit LRESULT CSurveyDlg::OnOK( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled ) { TraceFunctEnter("OnOK"); // // When IDC_BUTTON_HYPERLINK has focus and the user presses Enter, IDOK is // generated for some reason. I think this is because IDC_BUTTON_HYPERLINK // is owner-drawn. Anyway, if IDC_BUTTON_HYPERLINK currently has focus then // send BM_CLICK to that control and return immediately. // if (m_bHyperlinkHasFocus) { DebugTrace(0, "User clicked IDC_BUTTON_HYPERLINK, sending BM_CLICK to that control"); SendDlgItemMessage(IDC_BUTTON_HYPERLINK, BM_CLICK, 0, 0); TraceFunctLeave(); bHandled = TRUE; return 0; } // // verify that the user entered all required data. // if (SendDlgItemMessage(IDC_COMBO_EVENT_CATEGORY, CB_GETCURSEL, 0, 0) == CB_ERR) { DebugTrace(0, "User did not select an event category, calling MessageBox"); TCHAR szMessageBoxTitle[1024]; TCHAR szMessageBoxText[1024]; LoadString(_Module.GetResourceInstance(), IDS_NEED_EVENT_CATEGORY, szMessageBoxText, ARRAYSIZE(szMessageBoxText)); LoadString(_Module.GetResourceInstance(), IDS_NEED_EVENT_CATEGORY_TITLE, szMessageBoxTitle, ARRAYSIZE(szMessageBoxTitle)); MessageBox(szMessageBoxText, szMessageBoxTitle, MB_OK); DebugTrace(0, "OnOK exiting but not calling EndDialog"); TraceFunctLeave(); bHandled = TRUE; return 0; } if (SendDlgItemMessage(IDC_COMBO_SEVERITY, CB_GETCURSEL, 0, 0) == CB_ERR) { DebugTrace(0, "User did not select a severity, calling MessageBox"); TCHAR szMessageBoxTitle[1024]; TCHAR szMessageBoxText[1024]; LoadString(_Module.GetResourceInstance(), IDS_NEED_SEVERITY, szMessageBoxText, ARRAYSIZE(szMessageBoxText)); LoadString(_Module.GetResourceInstance(), IDS_NEED_SEVERITY_TITLE, szMessageBoxTitle, ARRAYSIZE(szMessageBoxTitle)); MessageBox(szMessageBoxText, szMessageBoxTitle, MB_OK); DebugTrace(0, "OnOK exiting but not calling EndDialog"); TraceFunctLeave(); bHandled = TRUE; return 0; } if (SendDlgItemMessage(IDC_EDIT_COMMENT, WM_GETTEXTLENGTH, 0, 0) == 0) { DebugTrace(0, "User did not type in a comment, calling MessageBox"); TCHAR szMessageBoxTitle[1024]; TCHAR szMessageBoxText[1024]; LoadString(_Module.GetResourceInstance(), IDS_NEED_COMMENT, szMessageBoxText, ARRAYSIZE(szMessageBoxText)); LoadString(_Module.GetResourceInstance(), IDS_NEED_COMMENT_TITLE, szMessageBoxTitle, ARRAYSIZE(szMessageBoxTitle)); MessageBox(szMessageBoxText, szMessageBoxTitle, MB_OK); DebugTrace(0, "OnOK exiting but not calling EndDialog"); TraceFunctLeave(); bHandled = TRUE; return 0; } // // Write the email address and beta ID for this user to the registry. // SendDlgItemMessage(IDC_EDIT_EMAIL_ADDRESS, WM_GETTEXT, MAX_EMAIL_ADDRESS_SIZE + 1, (LPARAM) m_lldata.szEmailAddress); SendDlgItemMessage(IDC_EDIT_BETA_ID, WM_GETTEXT, MAX_BETA_ID_SIZE + 1, (LPARAM) m_lldata.szBetaID); HKEY hKey; DWORD dwRet; if ((dwRet = RegCreateKeyEx(HKEY_CURRENT_USER, _T("Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments"), 0, _T(""), REG_OPTION_NON_VOLATILE, KEY_READ + KEY_SET_VALUE, NULL, &hKey, NULL)) != ERROR_SUCCESS) { DebugTrace(0, "Could not create HKCU\\Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments key in the registry (RegCreateKeyEx returned %ld)", dwRet); } else { if ((dwRet = RegSetValueEx(hKey, _T("Email Address"), NULL, REG_SZ, (LPBYTE) m_lldata.szEmailAddress, (_tcslen(m_lldata.szEmailAddress) + 1) * sizeof(TCHAR))) != ERROR_SUCCESS) DebugTrace(0, "Failed to write Email Address value to the HKCU\\Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments registry key (RegSetValueEx returned %ld)", dwRet); else DebugTrace(0, "Wrote email address for this user to the registry: %s", m_lldata.szEmailAddress); if ((dwRet = RegSetValueEx(hKey, _T("Beta ID"), NULL, REG_SZ, (LPBYTE) m_lldata.szBetaID, (_tcslen(m_lldata.szBetaID) + 1) * sizeof(TCHAR))) != ERROR_SUCCESS) DebugTrace(0, "Failed to write Beta ID value to the HKCU\\Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments registry key (RegSetValueEx returned %ld)", dwRet); else DebugTrace(0, "Wrote beta ID for this user to the registry: %s", m_lldata.szBetaID); RegCloseKey(hKey); } // // Call the routine to format data collected and transport // it to server DebugTrace(0, "Calling GetInfoAndLogLameButton"); GetInfoAndLogLameButton(); // // Kill the comment dialog // DebugTrace(0, "Calling EndDialog on wID: %ld", wID); EndDialog(wID); TraceFunctLeave(); bHandled = TRUE; return 0; } // // CSurveyDlg::OnCancel: This executes when user cancels the comment // LRESULT CSurveyDlg::OnCancel( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled ) { // // Kill the comment dialog // EndDialog(wID); bHandled = TRUE; return 0; } // // CSurveyDlg::IsMessageBox: This routine returns TRUE if the m_hwndTarget is indeed // a message box // BOOL CSurveyDlg::IsMessageBox() { TraceFunctEnter("CSurveyDlg::IsMessageBox"); BOOL fRetVal = FALSE; LPMSGBOXPARAMS lpMsgData = NULL; UINT cbSize = 0; if(NULL == m_hwndTarget) { FatalTrace(0, "m_hwndTarget is NULL"); goto done; } // // Looking at msgbox.c, I realized that the MsgBoxParams are stored in the MessageBox // dialog itself using a SetWindowLongPtr. Hence GetWindowLongPtr should retrieve it for // messageboxes. // lpMsgData = (LPMSGBOXPARAMS)::GetWindowLongPtr(m_hwndTarget, GWLP_USERDATA); if(NULL == lpMsgData) { FatalTrace(0, "GetWindowLongPtr failed. Error: %ld", GetLastError()); fRetVal = FALSE; goto done; } // // There are windows other than msgboxes that can have data set in WindowLongPtr // The following is a check to see if we have a message box or not. If this is not // a real message box, it will throw an exception when we try to read cbSize for // some cases. // DebugTrace(0, "Figuring if this is a msgbox. Addr: %ld", lpMsgData); __try { cbSize = lpMsgData->cbSize; } __except( EXCEPTION_EXECUTE_HANDLER ) { FatalTrace(0, "Corrupt MSGBOXPARAMS"); goto done; } DebugTrace(0, "cbSize: %ld", cbSize); // // Some more sanity checks // if(( cbSize > MSGBOX_TEXT_SIZE)||( cbSize < sizeof(MSGBOXPARAMS))) { FatalTrace(0, "Invalid MSGBOXPARAMS"); goto done; } else { // // We have a MessageBox // fRetVal = TRUE; // // Extract the message box text from the MSGBOXPARAMS // // NTRAID#NTBUG9-155100-2000/08/13-jasonr // // Before we determined the maximum number of characters to copy from // cbsize. I have no idea why we were doing that. Now the maximum is // based on the size of the m_lldata.szMsgBoxText buffer. I wrapped the // copy within a __try __except block just to be safe. // ZeroMemory(m_lldata.szMsgBoxText, sizeof(m_lldata.szMsgBoxText)); __try { _tcsncpy((_TCHAR *) m_lldata.szMsgBoxText, (const _TCHAR *) lpMsgData->lpszText, (sizeof(m_lldata.szMsgBoxText) / sizeof(_TCHAR)) - 1); } __except (EXCEPTION_EXECUTE_HANDLER) { } DebugTrace(0,"MessageBox Text: %ls\n", m_lldata.szMsgBoxText); } done: TraceFunctLeave(); return fRetVal; } // // CSurveyDlg::GetInfoAndLogLameButton: The routine that does most of the work to gather // and format the comment information and uploads // it to server void CSurveyDlg::GetInfoAndLogLameButton() { TraceFunctEnter("GetInfoAndLogLameButton"); m_lldata.versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); // // Get version of binary that launched the comments hook // GetVersionEx(&m_lldata.versionInfo); TCHAR szClass[CLASS_SIZE]; TCHAR szTitle[TITLE_SIZE]; TCHAR szComment[COMMENT_TEXT_SIZE + 1]; TCHAR szText[1024]; // // Clear out buffers First // ZeroMemory(m_lldata.szTitle, TITLE_SIZE); ZeroMemory(m_lldata.szClass, CLASS_SIZE); ZeroMemory(m_lldata.szMsgBoxText, MSGBOX_TEXT_SIZE); // // Gather the Title of the target window // if (::GetWindowText(m_hwndTarget, szTitle, TITLE_SIZE) == 0) { DebugTrace(0, "szTitle is NULL"); ZeroMemory(m_lldata.szTitle, TITLE_SIZE); } else { _tcscpy(m_lldata.szTitle, szTitle); } DebugTrace(0, "m_lldata.szTitle: %ls", m_lldata.szTitle); // // Gather the class of the target Window // if (::RealGetWindowClass(m_hwndTarget, szClass, CLASS_SIZE) == 0) { DebugTrace(0, "szClass is NULL"); ZeroMemory(m_lldata.szClass, CLASS_SIZE); } else { _tcscpy(m_lldata.szClass, szClass); } DebugTrace(0, "m_lldata.szClass: %ls", m_lldata.szClass); // // Check if m_hwndTarget is a MessageBox. // if(TRUE == IsMessageBox()) { // // We are dealing with a Message Box // DebugTrace(0, "We are dealing with a Message Box"); // // The following disabled code is for reference reasons, // Earlier, we used to send a WM_COPY to obtain the message box text // Now, IsMessageBox does the trick #ifdef _SEND_WM_COPY if(NULL != m_hwndTarget) { HANDLE hMem = NULL; WCHAR *pData = NULL; TCHAR szTitleTmp [MSGBOX_TEXT_SIZE]; INT nChar = 0; INT nChar1 = 0; // // Send WM_COPY to the message box. WM_COPY causes the MessageBox to write // it's message box text to the clipboard // DebugTrace(0, "Sending WM_COPY to m_hwndTarget"); SendMessage( m_hwndTarget, WM_COPY, 0, 0 ); // // Open the clipboard of the current task // DebugTrace(0, "OpenClipboard"); if(FALSE == ::OpenClipboard( m_hwndTarget )) { FatalTrace(0, "OpenClipBoard failed. Error: %ld", GetLastError()); goto done; } // // Get the Data written by the target Window to the Clipboard // DebugTrace(0, "GetClipboardData"); hMem = GetClipboardData( CF_UNICODETEXT ); if(NULL == hMem) { FatalTrace(0, "GetClipBoardData failed. Error: %ld\n", GetLastError()); goto done; } // // Get the memory location for the data written to the clipboard // DebugTrace(0, "GlobalLock"); pData = (LPWSTR) GlobalLock( hMem ); if(NULL == pData) { FatalTrace(0, "GlobalLock failed. Error: %ld\n", GetLastError()); goto done; } if(pData[0] == '\0') { FatalTrace(0, "Nothing in clipboard"); goto done; } // // Advance past the first ---------------------------\r\n // DebugTrace(0, "Advancing past the first ---"); for(nChar = 0; (nChar < _tcslen(pData))&&(!((pData[nChar] == '-')&&(pData[nChar+1] == '\r')&&(pData[nChar+2] == '\n'))); nChar++); // // Check to see if we have data in the right format. // if ( nChar == _tcslen(pData)) { // // This check is necessary, because the check for messagebox based on the window class // is best effort and not 100% accurate. So, we may think that the target window is a // message box, where as it may not be. This check is therefore necessary. FatalTrace(0, "This is not a valid messagebox"); goto done; } nChar += 3; DebugTrace(0, "nChar: %ld", nChar); // // Extract the Caption // DebugTrace(0, "Extracting the Caption"); for(nChar1 = 0; !((pData[nChar] == '\r')&&(pData[nChar+1] == '\n')&&(pData[nChar+2] == '-')); nChar1++,nChar++) { szTitleTmp[ nChar1 ] = pData[nChar]; } nChar += 2; DebugTrace(0, "nChar: %ld", nChar); szTitleTmp[ nChar1 + 1] = '\0'; DebugTrace(0, "MessageBox Title: %ls", szTitleTmp); // // Advance past the second ---------------------------\r\n // DebugTrace(0, "Advancing past the second ---"); for(; !((pData[nChar] == '-')&&(pData[nChar+1] == '\r')&&(pData[nChar+2] == '\n')); nChar++); nChar += 3; DebugTrace(0, "nChar: %ld", nChar); // // Extract the message box text // DebugTrace(0, "Extracting the MessageBox text"); for(nChar1 = 0; !((pData[nChar] == '\r')&&(pData[nChar+1] == '\n')&&(pData[nChar+2] == '-')); nChar1++,nChar++) { m_lldata.szMsgBoxText[ nChar1 ] = pData[nChar]; } m_lldata.szMsgBoxText[ nChar1 + 1] = '\0'; DebugTrace(0,"MessageBox szText: %ls", m_lldata.szMsgBoxText); // // Clear out the clipboard // EmptyClipboard(); done: if(NULL != hMem) { if(FALSE == GlobalUnlock( hMem )) { printf("GlobalUnlock failed. Error; %ld\n", GetLastError()); } } CloseClipboard(); } #endif // _SEND_WM_COPY } else { DebugTrace(0, "class: %s is not a MessageBox", szClass); } // // Obtain the Comment // if (GetDlgItemText(IDC_EDIT_COMMENT, szComment, COMMENT_TEXT_SIZE + 1) == 0) { DebugTrace(0, "Comment is NULL"); ZeroMemory(m_lldata.szComment, COMMENT_TEXT_SIZE + 1); } else { _tcscpy(m_lldata.szComment, szComment); } DebugTrace(0, "sizeof Comment text: %ld", _tcslen( m_lldata.szComment )); // // Populate m_lldata.dwEventCategory from the selection in // IDC_COMBO_EVENT_CATEGORY. Note that this is purposefully done with a // switch statement, rather than with an algorithmic calculation such as: // // m_lldata.dwEventCategory = 1 + SendDlgItemMessage(IDC_COMBO_EVENT_CATEGORY, CB_GETCURSEL, 0, 0); // // If we ever change the possible selections in the future, the algorithm // might not be correct, so we don't use it. // LRESULT dwCurSel = SendDlgItemMessage(IDC_COMBO_EVENT_CATEGORY, CB_GETCURSEL, 0, 0); _ASSERT(dwCurSel == 0 || dwCurSel == 1 || dwCurSel == 2 || dwCurSel == 3 || dwCurSel == 4 || dwCurSel == 5); switch (dwCurSel) { case 0: m_lldata.dwEventCategory = 1; break; case 1: m_lldata.dwEventCategory = 2; break; case 2: m_lldata.dwEventCategory = 3; break; case 3: m_lldata.dwEventCategory = 4; break; case 4: m_lldata.dwEventCategory = 5; break; case 5: m_lldata.dwEventCategory = 6; break; } // // Populate m_lldata.dwSeverity from the selection in IDC_COMBO_SEVERITY. // Note that this is purposefully done with a switch statement, rather than // with an algorithmic calculation such as: // // m_lldata.dwSeverity = 1 + SendDlgItemMessage(IDC_COMBO_SEVERITY, CB_GETCURSEL, 0, 0); // // If we ever change the possible selections in the future, the algorithm // might not be correct, so we don't use it. // dwCurSel = SendDlgItemMessage(IDC_COMBO_SEVERITY, CB_GETCURSEL, 0, 0); _ASSERT(dwCurSel == 0 || dwCurSel == 1 || dwCurSel == 2 || dwCurSel == 3); switch (dwCurSel) { case 0: m_lldata.dwSeverity = 1; break; case 1: m_lldata.dwSeverity = 2; break; case 2: m_lldata.dwSeverity = 3; break; case 3: m_lldata.dwSeverity = 4; break; } // // It doesn't seem like a good idea to initialize COM on somebody // else's thread, so we launch a new thread to do the reporting. // DebugTrace(0, "Creating Seperate thread to deal with COM"); UINT uThreadId; // dummy HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, LogLameButtonThread, &m_lldata, 0, &uThreadId); if (hThread != 0) { DWORD dwEvent = WaitForSingleObject(hThread, INFINITE); _ASSERT(dwEvent == WAIT_OBJECT_0); // // NTRAID#NTBUG9-154248-2000/08/08-jasonr // NTRAID#NTBUG9-152439-2000/08/08-jasonr // // We used to pop up the "Thank You" message box in the new thread. // Now we pop it up in the dialog box thread instead to fix these bugs. // The new thread now returns 0 to indicate success, 1 to indicate // failure. We only pop up the dialog box on success. // DWORD dwExitCode; if (!GetExitCodeThread(hThread, &dwExitCode)) dwExitCode = 1; CloseHandle(hThread); TCHAR szThankYouMessage[1024]; TCHAR szThankYouMessageTitle[1024]; LoadString(_Module.GetResourceInstance(), IDS_THANK_YOU, szThankYouMessage, ARRAYSIZE(szThankYouMessage)); LoadString(_Module.GetResourceInstance(), IDS_THANK_YOU_TITLE, szThankYouMessageTitle, ARRAYSIZE(szThankYouMessageTitle)); MessageBox(szThankYouMessage, szThankYouMessageTitle, MB_OK); } TraceFunctLeave(); } // // DllMain : The DllMain for LAMEBTN.DLL // BOOL WINAPI DllMain ( HINSTANCE hInstance, DWORD dwreason, LPVOID reserved ) { switch (dwreason) { case DLL_PROCESS_ATTACH: // _CrtSetBreakAlloc(275); #ifndef NOTRACE InitAsyncTrace(); #endif ghDllInst = hInstance; DisableThreadLibraryCalls(hInstance); _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_WNDW); _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG); _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); _Module.Init(NULL, hInstance); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: _CrtDumpMemoryLeaks(); _Module.Term(); TermAsyncTrace(); break; } return TRUE; } // // LogLameButtonThread: This is the thread spawned by the comments dialog // to format the data collected into XML and upload // to server unsigned int __stdcall LogLameButtonThread( void* pvLogData ) { TraceFunctEnter("LogLameButtonThread"); // // NTRAID#NTBUG9-154248-2000/08/08-jasonr // NTRAID#NTBUG9-152439-2000/08/08-jasonr // // We used to pop up the "Thank You" message box in the new thread. // Now we pop it up in the dialog box thread instead to fix these bugs. // The new thread now returns 0 to indicate success, 1 to indicate // failure. We only pop up the dialog box on success. // int iRet = 1; // // Initialize COM // DebugTrace(0, "Initializing COM"); HRESULT hrCoInit = CoInitializeEx(NULL, COINIT_MULTITHREADED); HRESULT hr = S_OK; if (SUCCEEDED(hrCoInit)) { // // Create a temp file that will hold a minidump of the current process. // WCHAR szTempPath[MAX_PATH]; if ((GetTempPathW(MAX_PATH, szTempPath) == 0) || (GetTempFileNameW(szTempPath, _T("EMI"), 0, ((LAMELOGDATA *) pvLogData)->szMiniDumpPath) == 0)) { DebugTrace(0, "GetTempPathW() or GetTempFileNameW() failed, minidump will not be created"); ((LAMELOGDATA *) pvLogData)->szMiniDumpPath[0] = 0; } else { DebugTrace(0, "Minidump will be stored in temp file \"%s\"", ((LAMELOGDATA *) pvLogData)->szMiniDumpPath); // // Build a command line for executing DUMPREP.EXE. // WCHAR szWindowsDir[MAX_PATH+1]; WCHAR szCmdLine[1024]; ZeroMemory(szWindowsDir, sizeof(szWindowsDir)); ZeroMemory(szCmdLine, sizeof(szCmdLine)); GetSystemWindowsDirectoryW(szWindowsDir, MAX_PATH); wsprintf(szCmdLine, L"%s\\system32\\dumprep.exe %lu -d 7 7 \"%s\"", szWindowsDir, GetCurrentProcessId(), ((LAMELOGDATA *) pvLogData)->szMiniDumpPath); // // CreateProcess() on DUMPREP.EXE to create the minidump. // STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); if (!CreateProcessW(NULL, szCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { DebugTrace(0, "CreateProcess() failed cmdline \"%s\" with error %ld; minidump will not be created", szCmdLine, GetLastError()); DeleteFileW(((LAMELOGDATA *) pvLogData)->szMiniDumpPath); ((LAMELOGDATA *) pvLogData)->szMiniDumpPath[0] = 0; } else { DebugTrace(0, "CreateProcess() succeeded on cmdline \"%s\" with PID %ld, TID %ld; waiting up to 60 seconds...", szCmdLine, pi.dwProcessId, pi.dwThreadId); // // Wait for DUMPREP.EXE to exit and interpret the exit code: // frrvOk (defined in faultrep.h) is success; anything else is // failure. Wait for a maximum of 60 seconds. // DWORD dwRet; BOOL bSuccess = FALSE; switch (dwRet = WaitForSingleObject(pi.hProcess, 60000)) { case WAIT_OBJECT_0: DebugTrace(0, "The process handle is signalled"); if (!GetExitCodeProcess(pi.hProcess, &dwRet)) { DebugTrace(0, "GetExitCodeProcess() failed; GetLastError() = %lu; minidump will not be created", GetLastError()); DeleteFileW(((LAMELOGDATA *) pvLogData)->szMiniDumpPath); ((LAMELOGDATA *) pvLogData)->szMiniDumpPath[0] = 0; } else if (dwRet != frrvOk) { DebugTrace(0, "The process failed with exit code %lu; minidump will not be created", dwRet); DeleteFileW(((LAMELOGDATA *) pvLogData)->szMiniDumpPath); ((LAMELOGDATA *) pvLogData)->szMiniDumpPath[0] = 0; } else { DebugTrace(0, "The process exited and the minidump was created successfully"); bSuccess = TRUE; } break; case WAIT_TIMEOUT: DebugTrace(0, "WaitForSingleObject() returned WAIT_TIMEOUT after 60 seconds; minidump will not be created"); DeleteFileW(((LAMELOGDATA *) pvLogData)->szMiniDumpPath); ((LAMELOGDATA *) pvLogData)->szMiniDumpPath[0] = 0; break; case WAIT_FAILED: DebugTrace(0, "WaitForSingleObject() returned WAIT_FAILED, GetLastError() = %lu; minidump will not be created", GetLastError()); DeleteFileW(((LAMELOGDATA *) pvLogData)->szMiniDumpPath); ((LAMELOGDATA *) pvLogData)->szMiniDumpPath[0] = 0; break; default: DebugTrace(0, "WaitForSingleObject() returned unknown code %lu, GetLastError() = %lu; minidump will not be created", dwRet, GetLastError()); DeleteFileW(((LAMELOGDATA *) pvLogData)->szMiniDumpPath); ((LAMELOGDATA *) pvLogData)->szMiniDumpPath[0] = 0; break; } CloseHandle(pi.hProcess); CloseHandle(pi.hThread); if (bSuccess) { // // Create a .CAB file // WCHAR szCABPath[MAX_PATH+1]; wcscpy(szCABPath, ((LAMELOGDATA *) pvLogData)->szMiniDumpPath); wcscat(szCABPath, L".cab"); if (FAILED(MPC::CompressAsCabinet(((LAMELOGDATA *) pvLogData)->szMiniDumpPath, szCABPath, L"minidump.dmp")) || !MoveFileExW(szCABPath, ((LAMELOGDATA *) pvLogData)->szMiniDumpPath, MOVEFILE_REPLACE_EXISTING + MOVEFILE_WRITE_THROUGH)) { DebugTrace(0, "MPC::CompressAsCabinet failed (more likely) or MoveFileExW failed (less likely); minidump will not be created"); DeleteFileW(((LAMELOGDATA *) pvLogData)->szMiniDumpPath); DeleteFileW(szCABPath); ((LAMELOGDATA *) pvLogData)->szMiniDumpPath[0] = 0; } else DebugTrace(0, "The minidump was compressed successfully as a .CAB file"); } } } // // Call the Routine that does the real work (defined in logging.cpp) // DebugTrace(0, "Calling LogLameBtn"); iRet = LogLameButton((LAMELOGDATA*) (pvLogData)); CoUninitialize(); } else { FatalTrace(0, "Failed to initialize COM"); goto done; } done: TraceFunctLeave(); return iRet; } // // CommentReport: user calls into CommentReport passing in the hWnd and call stack when // the Comments? link is clicked (This is the routine called via the Comments hook) VOID WINAPI CommentReport( HWND hwnd, // [in] Window of Dialog being commented on PVOID pv // [in] Call stack ) { TraceFunctEnter("CommentReport"); // // Fix for bug 141367. Walk up through the stack of windows examining the // module file name of each one. Count the number of windows that are owned // by lamebtn.dll. If we find two it means there are two lamebtn dialogs on // the screen already. In that case, return immediately. (We allow up to // two of them so the user can comment on the lamebtn dialog itself.) // HMODULE hThisModule = NULL; if ((hThisModule = GetModuleHandle(L"lamebtn.dll")) != NULL) { TCHAR szThisModule[MAX_PATH] = { 0 }; if (GetModuleFileName(hThisModule, szThisModule, MAX_PATH) > 0) { HWND hWnd2 = hwnd; TCHAR szWndModule[MAX_PATH] = { 0 }; DWORD dwLamebtnDLLWindows = 0; while (hWnd2 != NULL) { if ((GetWindowModuleFileName(hWnd2, szWndModule, MAX_PATH) > 0) && (_wcsicmp(szThisModule, szWndModule) == 0)) { dwLamebtnDLLWindows++; if (dwLamebtnDLLWindows >= 2) { DebugTrace(0, "Two lamebtn dialogs are already up, therefore we won't bring up another one"); return; } } hWnd2 = GetParent(hWnd2); } } } #ifdef _GENERATE_STACK_AT_CLIENT TCHAR szWindowText[MAX_BUF_SIZE + 1]; TCHAR szBigBuff[2 * MAX_BUF_SIZE + 1]; TCHAR tmpbuf[128 + 1]; // // Obtain the WindowText // GetWindowText(hwnd, szWindowText, MAX_BUF_SIZE); // // pvStackTrace can be NULL // if(pv) { wsprintf(tmpbuf, TEXT("\t(%x) = %x\n\t(%x) = %x\n\t(%x) = %x\n\t(%x) = %x"), ((int*)pv+12), (int)*((int*)pv+12), ((int*)pv+8),(int)*((int*)pv+8), ((int*)pv+4), (int)*((int*)pv+4),((int*)pv), (int)*((int*)pv)); } else { // // Generating a stack walk at the client is not really a good idea. Since it will work only // on x86. Absence of call stack in the uploaded comment report should be handled at the server // The following code though disabled is still kept here for reference. // RtlWalkFrameChain_t fnRtlWalkFrameChain = (RtlWalkFrameChain_t) GetProcAddress(GetModuleHandle(_T("NTDLL.DLL")), "RtlWalkFrameChain"); STACKTRACEDATA* pstd = (STACKTRACEDATA*) _alloca(offsetof(STACKTRACEDATA, callers[64])); pstd->nCallers = fnRtlWalkFrameChain(pstd->callers, 64, 0); pv = (PVOID)pstd; if(NULL != pv) { DebugTrace(0, "Generated a new stacktrace"); wsprintf(tmpbuf, TEXT("\t(%x) = %x\n\t(%x) = %x\n\t(%x) = %x\n\t(%x) = %x"), ((int*)pv+12), (int)*((int*)pv+12), ((int*)pv+8),(int)*((int*)pv+8), ((int*)pv+4), (int)*((int*)pv+4),((int*)pv), (int)*((int*)pv)); } else { FatalTrace(0, "StackTrace not available..."); wsprintf(tmpbuf, TEXT("StackTrace not available...")); } FatalTrace(0, "StackTrace not available..."); wsprintf(tmpbuf, TEXT("StackTrace not available...")); } DebugTrace(0, "Got comment from window '%ls'", szWindowText, tmpbuf); DebugTrace(0, "StackTrace: '%ls'", tmpbuf); #endif // // Create the Comment dialog // CSurveyDlg dlg; // // Initialize the comment dialog // DebugTrace(0, "Calling dlg.Init"); dlg.Init(hwnd, (PSTACKTRACEDATA) pv); // // Launch the comment dialog // DebugTrace(0, "Calling DoModal"); dlg.DoModal(hwnd, NULL); TraceFunctLeave(); } // // nsoy sayz: The following code is used by the MessageBox hook(which for some // mysterious reason disappeared when Neptune code was moved to Whistler) // Hence, the code used by the MessageBox hook is henceforth // not maintained. // // UploadInstrumentationCollectionData: Dunno why this is present. Walter must have // had some use for this. VOID WINAPI UploadInstrumentationCollectionData() { //bugbug: do something meaningful ; } // // LogMessageBoxThread: MessageBox hook data collection thread. // - present for historical reasons. Not needed for Whistler // unsigned int __stdcall LogMessageBoxThread(void* pvLogData) { HRESULT hrCoInit = CoInitializeEx(NULL, COINIT_MULTITHREADED); if (SUCCEEDED(hrCoInit)) { LogMessageBox((MSGBOXLOGDATA*) pvLogData); CoUninitialize(); } return 0; }