1667 lines
52 KiB
C++
1667 lines
52 KiB
C++
/*
|
|
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 <dbgtrace.h>
|
|
#include <objbase.h>
|
|
#include <atlcom.h>
|
|
#include <faultrep.h>
|
|
|
|
// MPC_Utils.h includes MPC_main.h, which apparently redefines ARRAYSIZE which
|
|
// causes a compiler warning.
|
|
|
|
#ifdef ARRAYSIZE
|
|
#undef ARRAYSIZE
|
|
#endif
|
|
|
|
#include <MPC_utils.h>
|
|
|
|
//
|
|
// 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<CSurveyDlg>
|
|
{
|
|
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;
|
|
}
|