Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1666 lines
52 KiB

  1. /*
  2. Copyright 1999 Microsoft Corporation
  3. Logging for MessageBoxes and the comment button (aka the "lame" button).
  4. Walter Smith (wsmith)
  5. Rajesh Soy (nsoy) - cleaned up code. 5/5/2000
  6. Rajesh Soy (nsoy) - rearranged code, added messagebox text handling. 6/6/2000
  7. */
  8. #ifdef THIS_FILE
  9. #undef THIS_FILE
  10. #endif
  11. static char __szTraceSourceFile[] = __FILE__;
  12. #define THIS_FILE __szTraceSourceFile
  13. #include "stdafx.h"
  14. #define NOTRACE
  15. #include "logging.h"
  16. #include "resource.h"
  17. #include <dbgtrace.h>
  18. #include <objbase.h>
  19. #include <atlcom.h>
  20. #include <faultrep.h>
  21. // MPC_Utils.h includes MPC_main.h, which apparently redefines ARRAYSIZE which
  22. // causes a compiler warning.
  23. #ifdef ARRAYSIZE
  24. #undef ARRAYSIZE
  25. #endif
  26. #include <MPC_utils.h>
  27. //
  28. // Globals
  29. //
  30. HINSTANCE ghDllInst;
  31. CComModule _Module;
  32. //
  33. // Routines Defined here
  34. //
  35. unsigned int __stdcall LogLameButtonThread(void* pvLogData);
  36. VOID WINAPI CommentReport( HWND hwnd, PVOID pv);
  37. //
  38. // CSurveyDlg: The Comments dialog implementation
  39. //
  40. class CSurveyDlg :
  41. public CAxDialogImpl<CSurveyDlg>
  42. {
  43. public:
  44. //
  45. // Constructor
  46. //
  47. CSurveyDlg()
  48. {
  49. ZeroMemory(&m_lldata, sizeof(m_lldata));
  50. m_hwndTarget = NULL;
  51. m_bShowHyperlink = FALSE;
  52. m_crAnchor = RGB(0, 0, 0);
  53. ZeroMemory(&m_szHyperlinkText, sizeof(m_szHyperlinkText));
  54. ZeroMemory(&m_szHyperlinkCmdLine, sizeof(m_szHyperlinkCmdLine));
  55. m_bHyperlinkHasFocus = FALSE;
  56. }
  57. //
  58. // Destructor
  59. //
  60. ~CSurveyDlg()
  61. {
  62. if (m_lldata.pbImage != NULL) {
  63. free(m_lldata.pbImage);
  64. m_lldata.pbImage = NULL;
  65. }
  66. }
  67. enum { IDD = IDD_SURVEYDLG };
  68. private:
  69. LAMELOGDATA m_lldata; // Data collected by LAMEBTN.dll
  70. HWND m_hwndTarget; // Window of dialog being commented on
  71. BOOL m_bShowHyperlink; // Set to TRUE if we should display the hyperlink on our dialog.
  72. COLORREF m_crAnchor; // Color of a hyperlink (anchor).
  73. TCHAR m_szHyperlinkText[256]; // Display string for the hyperlink.
  74. TCHAR m_szHyperlinkCmdLine[2048]; // Command line (obtained from the registry) to execute when the user clicks the hyperlink.
  75. TCHAR m_bHyperlinkHasFocus; // Set to TRUE when the hyperlink button (IDC_BUTTON_HYPERLINK) has focus.
  76. public:
  77. BEGIN_MSG_MAP(CSurveyDlg)
  78. MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
  79. MESSAGE_HANDLER(WM_DRAWITEM, OnDrawItem)
  80. MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor)
  81. COMMAND_ID_HANDLER(IDC_BUTTON_HYPERLINK, HyperlinkCmdHandler)
  82. COMMAND_ID_HANDLER(IDOK, OnOK)
  83. COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
  84. END_MSG_MAP()
  85. //
  86. // Init: Initializes the comment Dialog
  87. //
  88. void Init( HWND hwndTarget, PSTACKTRACEDATA pstdata);
  89. //
  90. // OnInitDialog: Fills up the Comment Dialog structures when the dialog is displayed
  91. //
  92. LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
  93. //
  94. // OnDrawItem: Draws owner-drawn controls (we have only one: IDC_BUTTON_HYPERLINK)
  95. //
  96. LRESULT OnDrawItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
  97. //
  98. // OnSetCursor: Sets the cursor to the hand if the mouse is over IDC_BUTTON_HYPERLINK
  99. //
  100. LRESULT OnSetCursor(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
  101. //
  102. // HyperlinkCmdHandler: Handles messages from the IDC_BUTTON_HYPERLINK button
  103. //
  104. LRESULT HyperlinkCmdHandler(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
  105. //
  106. // OnOK: Submits Comment
  107. //
  108. LRESULT OnOK(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
  109. //
  110. // OnCancel: Cancel the comment
  111. //
  112. LRESULT OnCancel(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
  113. private:
  114. //
  115. // CSurveyDlg::IsMessageBox: This routine returns TRUE if the hWnd passed is indeed
  116. // a message box
  117. //
  118. BOOL IsMessageBox();
  119. //
  120. // GetInfoAndLogLameButton: Gathers the comment and callstack information, formats into XML
  121. // and uploads to server
  122. void GetInfoAndLogLameButton();
  123. };
  124. //
  125. // CSurveyDlg::Init: Initializes the comment Dialog
  126. //
  127. void CSurveyDlg::Init(
  128. HWND hwndTarget, // [in] Window of dialog being commented on
  129. PSTACKTRACEDATA pstdata // [in] Call Stack
  130. )
  131. {
  132. TraceFunctEnter("CSurveyDlg::Init");
  133. _ASSERT(hwndTarget != NULL);
  134. //
  135. // Save off the target window
  136. //
  137. m_hwndTarget = hwndTarget;
  138. //
  139. // Save off the call stack
  140. //
  141. m_lldata.pStackTrace = pstdata;
  142. //
  143. // Grab the image of the target window
  144. //
  145. DebugTrace(0, "Calling SetActiveWindow");
  146. ::SetActiveWindow(hwndTarget);
  147. try {
  148. BYTE* pImageData = NULL;
  149. DebugTrace(0, "Calling GetWindowImage");
  150. GetWindowImage(hwndTarget, &pImageData, &m_lldata.cbImage);
  151. m_lldata.pbImage = pImageData;
  152. }
  153. catch (HRESULT hr) {
  154. m_lldata.pbImage = NULL;
  155. m_lldata.cbImage = 0;
  156. }
  157. TraceFunctLeave();
  158. }
  159. //
  160. // CSurveyDlg::OnInitDialog: Executes when Comments dialog is created
  161. //
  162. LRESULT
  163. CSurveyDlg::OnInitDialog(
  164. UINT uMsg,
  165. WPARAM wParam,
  166. LPARAM lParam,
  167. BOOL& bHandled
  168. )
  169. {
  170. DWORD idMessage;
  171. TraceFunctEnter("OnInitDialog");
  172. TCHAR szTitle[64];
  173. //
  174. // Obtain the Title of the Window being commented
  175. //
  176. if (::GetWindowText(m_hwndTarget, szTitle, ARRAYSIZE(szTitle)) != 0) {
  177. idMessage = IDS_INTRO_WITH_TITLE;
  178. if (lstrlen(szTitle) == ARRAYSIZE(szTitle) - 1) {
  179. // Truncate the title a little more nicely
  180. TCHAR* p = &szTitle[ARRAYSIZE(szTitle) - 4];
  181. *p++ = '.';
  182. *p++ = '.';
  183. *p++ = '.';
  184. }
  185. }
  186. else {
  187. idMessage = IDS_INTRO;
  188. }
  189. //
  190. // Load localized sting for Comment intro
  191. //
  192. TCHAR szTemplate[1024];
  193. LoadString(_Module.GetResourceInstance(),
  194. idMessage,
  195. szTemplate,
  196. ARRAYSIZE(szTemplate));
  197. TCHAR* fmArgs[] = { szTitle };
  198. TCHAR* szCommentIntro = NULL;
  199. FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
  200. FORMAT_MESSAGE_FROM_STRING |
  201. FORMAT_MESSAGE_ARGUMENT_ARRAY,
  202. szTemplate,
  203. 0,
  204. 0,
  205. (LPTSTR) &szCommentIntro,
  206. 1024,
  207. (va_list*) &fmArgs);
  208. if (szCommentIntro != NULL) {
  209. SetDlgItemText(IDC_STATIC_INTRO, szCommentIntro);
  210. LocalFree(szCommentIntro);
  211. }
  212. //
  213. // Set the maximum limits on the edit controls.
  214. //
  215. SendDlgItemMessage(IDC_EDIT_EMAIL_ADDRESS, EM_LIMITTEXT, MAX_EMAIL_ADDRESS_SIZE, 0);
  216. SendDlgItemMessage(IDC_EDIT_BETA_ID, EM_LIMITTEXT, MAX_BETA_ID_SIZE, 0);
  217. SendDlgItemMessage(IDC_EDIT_COMMENT, EM_LIMITTEXT, COMMENT_TEXT_SIZE, 0);
  218. //
  219. // Prepopulate the email address and beta ID edit controls with whatever
  220. // the user typed in last time. (We stored this in the registry the last
  221. // time they pressed the submit button.)
  222. //
  223. HKEY hKey;
  224. DWORD dwRet;
  225. if ((dwRet = RegOpenKeyEx(HKEY_CURRENT_USER,
  226. _T("Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments"),
  227. 0,
  228. KEY_QUERY_VALUE,
  229. &hKey)) != ERROR_SUCCESS) {
  230. DebugTrace(0, "No HKCU\\Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments key in the registry (RegOpenKeyEx returned %ld)", dwRet);
  231. }
  232. else {
  233. DWORD dwDataSize = sizeof(m_lldata.szEmailAddress);
  234. if ((dwRet = RegQueryValueEx(hKey,
  235. _T("Email Address"),
  236. NULL,
  237. NULL,
  238. (LPBYTE) m_lldata.szEmailAddress,
  239. &dwDataSize)) != ERROR_SUCCESS) {
  240. DebugTrace(0, "No Email Address value in the HKCU\\Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments registry key (RegQueryValueEx returned %ld)", dwRet);
  241. m_lldata.szEmailAddress[0] = 0;
  242. }
  243. else {
  244. DebugTrace(0, "Email address for this user in the registry: %s", m_lldata.szEmailAddress);
  245. SendDlgItemMessage(IDC_EDIT_EMAIL_ADDRESS, WM_SETTEXT, 0, (LPARAM) m_lldata.szEmailAddress);
  246. }
  247. dwDataSize = sizeof(m_lldata.szBetaID);
  248. if ((dwRet = RegQueryValueEx(hKey,
  249. _T("Beta ID"),
  250. NULL,
  251. NULL,
  252. (LPBYTE) m_lldata.szBetaID,
  253. &dwDataSize)) != ERROR_SUCCESS) {
  254. DebugTrace(0, "No Beta ID value in the HKCU\\Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments registry key (RegQueryValueEx returned %ld)", dwRet);
  255. m_lldata.szBetaID[0] = 0;
  256. }
  257. else {
  258. DebugTrace(0, "Beta ID for this user in the registry: %s", m_lldata.szBetaID);
  259. SendDlgItemMessage(IDC_EDIT_BETA_ID, WM_SETTEXT, 0, (LPARAM) m_lldata.szBetaID);
  260. }
  261. RegCloseKey(hKey);
  262. }
  263. //
  264. // Populate the event category dropdown.
  265. //
  266. TCHAR szEventCategoryString[1024];
  267. LoadString(_Module.GetResourceInstance(), IDS_EVENT_CATEGORY_1, szEventCategoryString, ARRAYSIZE(szEventCategoryString));
  268. SendDlgItemMessage(IDC_COMBO_EVENT_CATEGORY, CB_ADDSTRING, 0, (LPARAM) szEventCategoryString);
  269. LoadString(_Module.GetResourceInstance(), IDS_EVENT_CATEGORY_2, szEventCategoryString, ARRAYSIZE(szEventCategoryString));
  270. SendDlgItemMessage(IDC_COMBO_EVENT_CATEGORY, CB_ADDSTRING, 0, (LPARAM) szEventCategoryString);
  271. LoadString(_Module.GetResourceInstance(), IDS_EVENT_CATEGORY_3, szEventCategoryString, ARRAYSIZE(szEventCategoryString));
  272. SendDlgItemMessage(IDC_COMBO_EVENT_CATEGORY, CB_ADDSTRING, 0, (LPARAM) szEventCategoryString);
  273. LoadString(_Module.GetResourceInstance(), IDS_EVENT_CATEGORY_4, szEventCategoryString, ARRAYSIZE(szEventCategoryString));
  274. SendDlgItemMessage(IDC_COMBO_EVENT_CATEGORY, CB_ADDSTRING, 0, (LPARAM) szEventCategoryString);
  275. LoadString(_Module.GetResourceInstance(), IDS_EVENT_CATEGORY_5, szEventCategoryString, ARRAYSIZE(szEventCategoryString));
  276. SendDlgItemMessage(IDC_COMBO_EVENT_CATEGORY, CB_ADDSTRING, 0, (LPARAM) szEventCategoryString);
  277. LoadString(_Module.GetResourceInstance(), IDS_EVENT_CATEGORY_6, szEventCategoryString, ARRAYSIZE(szEventCategoryString));
  278. SendDlgItemMessage(IDC_COMBO_EVENT_CATEGORY, CB_ADDSTRING, 0, (LPARAM) szEventCategoryString);
  279. //
  280. // Populate the severity dropdown.
  281. //
  282. TCHAR szSeverityString[1024];
  283. LoadString(_Module.GetResourceInstance(), IDS_SEVERITY_1, szSeverityString, ARRAYSIZE(szSeverityString));
  284. SendDlgItemMessage(IDC_COMBO_SEVERITY, CB_ADDSTRING, 0, (LPARAM) szSeverityString);
  285. LoadString(_Module.GetResourceInstance(), IDS_SEVERITY_2, szSeverityString, ARRAYSIZE(szSeverityString));
  286. SendDlgItemMessage(IDC_COMBO_SEVERITY, CB_ADDSTRING, 0, (LPARAM) szSeverityString);
  287. LoadString(_Module.GetResourceInstance(), IDS_SEVERITY_3, szSeverityString, ARRAYSIZE(szSeverityString));
  288. SendDlgItemMessage(IDC_COMBO_SEVERITY, CB_ADDSTRING, 0, (LPARAM) szSeverityString);
  289. LoadString(_Module.GetResourceInstance(), IDS_SEVERITY_4, szSeverityString, ARRAYSIZE(szSeverityString));
  290. SendDlgItemMessage(IDC_COMBO_SEVERITY, CB_ADDSTRING, 0, (LPARAM) szSeverityString);
  291. //
  292. // Initialize m_crAnchor. Ideally we could read this from IE's registry,
  293. // but many other dialogs don't do that and we just don't have time now.
  294. // We'll default to pure blue, since that is the IE default.
  295. //
  296. m_crAnchor = RGB(0, 0, 255);
  297. //
  298. // Initialize m_szHyperlinkText from the IDS_HYPERLINK_TEXT string resource.
  299. //
  300. m_bShowHyperlink = TRUE;
  301. if (LoadString(_Module.GetResourceInstance(), IDS_HYPERLINK_TEXT, m_szHyperlinkText, ARRAYSIZE(m_szHyperlinkText)) == 0)
  302. m_bShowHyperlink = FALSE;
  303. //
  304. // Initialize m_szHyperlinkCmdLine from the registry.
  305. //
  306. if (m_bShowHyperlink)
  307. if ((dwRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  308. _T("Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments"),
  309. 0,
  310. KEY_QUERY_VALUE,
  311. &hKey)) != ERROR_SUCCESS) {
  312. DebugTrace(0, "No HKCU\\Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments key in the registry (RegOpenKeyEx returned %ld)", dwRet);
  313. m_bShowHyperlink = FALSE;
  314. }
  315. else {
  316. DWORD dwDataSize = sizeof(m_szHyperlinkCmdLine);
  317. if ((dwRet = RegQueryValueEx(hKey,
  318. _T("Hyperlink command line"),
  319. NULL,
  320. NULL,
  321. (LPBYTE) m_szHyperlinkCmdLine,
  322. &dwDataSize)) != ERROR_SUCCESS) {
  323. DebugTrace(0, "No Hyperlink command line value in the HKLM\\Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments registry key (RegQueryValueEx returned %ld)", dwRet);
  324. m_szHyperlinkCmdLine[0] = 0;
  325. m_bShowHyperlink = FALSE;
  326. }
  327. else
  328. DebugTrace(0, "Hyperlink command line in the registry: %s", m_szHyperlinkCmdLine);
  329. RegCloseKey(hKey);
  330. }
  331. //
  332. // Disable the hyperlink-related controls if we cannot successfully display
  333. // and launch the hyperlink.
  334. //
  335. if (!m_bShowHyperlink) {
  336. ::SetWindowPos(GetDlgItem(IDC_STATIC_HELP_AND_SUPPORT_1), 0, 0, 0, 0, 0, SWP_HIDEWINDOW + SWP_NOMOVE + SWP_NOSIZE + SWP_NOZORDER);
  337. ::SetWindowPos(GetDlgItem(IDC_STATIC_HELP_AND_SUPPORT_2), 0, 0, 0, 0, 0, SWP_HIDEWINDOW + SWP_NOMOVE + SWP_NOSIZE + SWP_NOZORDER);
  338. ::SetWindowPos(GetDlgItem(IDC_BUTTON_HYPERLINK), 0, 0, 0, 0, 0, SWP_HIDEWINDOW + SWP_NOMOVE + SWP_NOSIZE + SWP_NOZORDER);
  339. }
  340. //
  341. // Set the focus to:
  342. // Email address if the user hasn't entered it before.
  343. // Otherwise beta ID if the user hasn't entered it before.
  344. // Otherwise the event category dropdown.
  345. //
  346. if (SendDlgItemMessage(IDC_EDIT_EMAIL_ADDRESS, WM_GETTEXTLENGTH, 0, 0) == 0)
  347. GotoDlgCtrl(GetDlgItem(IDC_EDIT_EMAIL_ADDRESS));
  348. else if (SendDlgItemMessage(IDC_EDIT_BETA_ID, WM_GETTEXTLENGTH, 0, 0) == 0)
  349. GotoDlgCtrl(GetDlgItem(IDC_EDIT_BETA_ID));
  350. else
  351. GotoDlgCtrl(GetDlgItem(IDC_COMBO_EVENT_CATEGORY));
  352. m_bHyperlinkHasFocus = FALSE;
  353. TraceFunctLeave();
  354. bHandled = TRUE;
  355. return FALSE; // We set the focus, return FALSE to prevent O/S from setting focus.
  356. }
  357. //
  358. // CSurveyDlg::OnDrawItem: Draws owner-drawn controls (we have only one: IDC_BUTTON_HYPERLINK)
  359. //
  360. LRESULT
  361. CSurveyDlg::OnDrawItem(
  362. UINT uMsg,
  363. WPARAM wParam,
  364. LPARAM lParam,
  365. BOOL& bHandled
  366. )
  367. {
  368. TraceFunctEnter("OnDrawItem");
  369. HWND hwndHyperlink = NULL;
  370. HDC hdcHyperlink = NULL;
  371. HFONT hfontHelpAndSupport2 = NULL;
  372. HFONT hfontHyperlinkOld = NULL;
  373. HFONT hfontHyperlinkNew = NULL;
  374. LOGFONT lfHyperlinkNew;
  375. RECT rectHyperlink;
  376. //
  377. // If we cannot successfully display and launch the hyperlink, return now.
  378. //
  379. if (wParam != IDC_BUTTON_HYPERLINK)
  380. goto Done;
  381. if (!m_bShowHyperlink)
  382. goto Done;
  383. //
  384. // Get the DC for IDC_BUTTON_HYPERLINK.
  385. //
  386. if ((hwndHyperlink = GetDlgItem(IDC_BUTTON_HYPERLINK)) == NULL)
  387. goto Done;
  388. if ((hdcHyperlink = ::GetDC(hwndHyperlink)) == NULL)
  389. goto Done;
  390. //
  391. // Get the font used to draw IDC_STATIC_HELP_AND_SUPPORT_2. Create a new
  392. // font based on that one but with underline.
  393. //
  394. if ((hfontHelpAndSupport2 = (HFONT) SendDlgItemMessage(IDC_STATIC_HELP_AND_SUPPORT_2, WM_GETFONT, 0, 0)) == NULL)
  395. goto Done;
  396. if (!GetObject(hfontHelpAndSupport2, sizeof(LOGFONT), &lfHyperlinkNew))
  397. goto Done;
  398. lfHyperlinkNew.lfUnderline = TRUE;
  399. if ((hfontHyperlinkNew = CreateFontIndirect(&lfHyperlinkNew)) == NULL)
  400. goto Done;
  401. //
  402. // For IDC_BUTTON_HYPERLINK, select the new underline font but don't delete
  403. // the original font.
  404. //
  405. hfontHyperlinkOld = (HFONT) SelectObject(hdcHyperlink, hfontHyperlinkNew);
  406. if ((hfontHyperlinkOld == NULL) || (hfontHyperlinkOld == (HFONT) (ULONG_PTR)GDI_ERROR))
  407. goto Done;
  408. //
  409. // Draw the text using the IE anchor color. Center the text horizontally in
  410. // the button.
  411. //
  412. SetTextColor(hdcHyperlink, m_crAnchor);
  413. SetTextAlign(hdcHyperlink, TA_CENTER | TA_TOP);
  414. SetBkMode(hdcHyperlink, TRANSPARENT);
  415. ::GetClientRect(hwndHyperlink, &rectHyperlink);
  416. ExtTextOut(hdcHyperlink, (rectHyperlink.right / 2) + 1, 0, 0, NULL, m_szHyperlinkText, _tcslen(m_szHyperlinkText), NULL);
  417. //
  418. // For IDC_BUTTON_HYPERLINK, select the original font.
  419. //
  420. SelectObject(hdcHyperlink, hfontHyperlinkOld);
  421. //
  422. // Clean up.
  423. //
  424. Done:
  425. //
  426. // Delete the new font we created.
  427. //
  428. if (hfontHyperlinkNew != NULL)
  429. DeleteObject(hfontHyperlinkNew);
  430. //
  431. // Release the DC for IDC_BUTTON_HYPERLINK.
  432. //
  433. if (hdcHyperlink != NULL)
  434. ::ReleaseDC(hwndHyperlink, hdcHyperlink);
  435. //
  436. // Return.
  437. //
  438. TraceFunctLeave();
  439. bHandled = TRUE;
  440. return TRUE;
  441. }
  442. //
  443. // CSurveyDlg::OnSetCursor: Sets the cursor to the hand if the mouse is over the hyperlink window
  444. //
  445. LRESULT
  446. CSurveyDlg::OnSetCursor(
  447. UINT uMsg,
  448. WPARAM wParam,
  449. LPARAM lParam,
  450. BOOL& bHandled
  451. )
  452. {
  453. TraceFunctEnter("OnSetCursor");
  454. if (m_bShowHyperlink && ((HWND) wParam == GetDlgItem(IDC_BUTTON_HYPERLINK))) {
  455. HCURSOR hcursor = LoadCursor(NULL, IDC_HAND);
  456. SetCursor(hcursor);
  457. TraceFunctLeave();
  458. bHandled = TRUE;
  459. return TRUE;
  460. }
  461. TraceFunctLeave();
  462. bHandled = FALSE;
  463. return FALSE;
  464. }
  465. //
  466. // HyperlinkCmdHandler: Handles messages from the IDC_BUTTON_HYPERLINK button
  467. //
  468. LRESULT
  469. CSurveyDlg::HyperlinkCmdHandler(
  470. WORD wNotifyCode,
  471. WORD wID,
  472. HWND hWndCtl,
  473. BOOL& bHandled
  474. )
  475. {
  476. TraceFunctEnter("HyperlinkCmdHandler");
  477. //
  478. // If we gained or lost focus, update the focus rectangle.
  479. //
  480. if ((wNotifyCode == BN_SETFOCUS) || (wNotifyCode == BN_KILLFOCUS)) {
  481. HDC hdc;
  482. RECT rect;
  483. hdc = ::GetDC(hWndCtl);
  484. ::GetClientRect(hWndCtl, &rect);
  485. DrawFocusRect(hdc, &rect);
  486. ::ReleaseDC(hWndCtl, hdc);
  487. m_bHyperlinkHasFocus = (wNotifyCode == BN_SETFOCUS);
  488. }
  489. //
  490. // If we were clicked, launch the hyperlink program.
  491. //
  492. else if ((wNotifyCode = BN_CLICKED) || (wNotifyCode = BN_DOUBLECLICKED)) {
  493. DebugTrace(0, "User clicked IDC_BUTTON_HYPERLINK");
  494. TCHAR szCmdLine[2048];
  495. DWORD dwRet;
  496. dwRet = ExpandEnvironmentStrings(m_szHyperlinkCmdLine, szCmdLine, 2048);
  497. if ((dwRet == 0) || (dwRet > 2048)) {
  498. DebugTrace(0, "ExpandEnvironmentStrings() failed on cmdline \"%s\" with error %ld", m_szHyperlinkCmdLine, GetLastError());
  499. TCHAR szMsg[1024];
  500. TCHAR szTitle[1024];
  501. if ((LoadString(_Module.GetResourceInstance(), IDS_HYPERLINK_PROGRAM_FAILED, szMsg, ARRAYSIZE(szMsg)) != 0) &&
  502. (LoadString(_Module.GetResourceInstance(), IDS_HYPERLINK_PROGRAM_FAILED_TITLE, szTitle, ARRAYSIZE(szTitle)) != 0))
  503. MessageBox(szMsg, szTitle, MB_OK);
  504. }
  505. else {
  506. TCHAR szWindowsDir[MAX_PATH];
  507. STARTUPINFO si;
  508. PROCESS_INFORMATION pi;
  509. if (!GetSystemWindowsDirectory(szWindowsDir, MAX_PATH))
  510. szWindowsDir[0] = 0;
  511. ZeroMemory(&si, sizeof(si));
  512. si.cb = sizeof(si);
  513. if (CreateProcess(NULL, szCmdLine, NULL, NULL, FALSE, 0, NULL, szWindowsDir, &si, &pi)) {
  514. DebugTrace(0, "CreateProcess() succeeded on cmdline \"%s\" with PID %ld, TID %ld", szCmdLine, pi.dwProcessId, pi.dwThreadId);
  515. CloseHandle(pi.hProcess);
  516. CloseHandle(pi.hThread);
  517. }
  518. else {
  519. DebugTrace(0, "CreateProcess() failed cmdline \"%s\" with error %ld", szCmdLine, GetLastError());
  520. TCHAR szMsg[1024];
  521. TCHAR szTitle[1024];
  522. if ((LoadString(_Module.GetResourceInstance(), IDS_HYPERLINK_PROGRAM_FAILED, szMsg, ARRAYSIZE(szMsg)) != 0) &&
  523. (LoadString(_Module.GetResourceInstance(), IDS_HYPERLINK_PROGRAM_FAILED_TITLE, szTitle, ARRAYSIZE(szTitle)) != 0))
  524. MessageBox(szMsg, szTitle, MB_OK);
  525. }
  526. }
  527. }
  528. TraceFunctLeave();
  529. bHandled = TRUE;
  530. return 0;
  531. }
  532. //
  533. // CSurveyDlg::OnOK: Executes when user has done typing the comment
  534. // and has hit submit
  535. LRESULT
  536. CSurveyDlg::OnOK(
  537. WORD wNotifyCode,
  538. WORD wID,
  539. HWND hWndCtl,
  540. BOOL& bHandled
  541. )
  542. {
  543. TraceFunctEnter("OnOK");
  544. //
  545. // When IDC_BUTTON_HYPERLINK has focus and the user presses Enter, IDOK is
  546. // generated for some reason. I think this is because IDC_BUTTON_HYPERLINK
  547. // is owner-drawn. Anyway, if IDC_BUTTON_HYPERLINK currently has focus then
  548. // send BM_CLICK to that control and return immediately.
  549. //
  550. if (m_bHyperlinkHasFocus) {
  551. DebugTrace(0, "User clicked IDC_BUTTON_HYPERLINK, sending BM_CLICK to that control");
  552. SendDlgItemMessage(IDC_BUTTON_HYPERLINK, BM_CLICK, 0, 0);
  553. TraceFunctLeave();
  554. bHandled = TRUE;
  555. return 0;
  556. }
  557. //
  558. // verify that the user entered all required data.
  559. //
  560. if (SendDlgItemMessage(IDC_COMBO_EVENT_CATEGORY, CB_GETCURSEL, 0, 0) == CB_ERR) {
  561. DebugTrace(0, "User did not select an event category, calling MessageBox");
  562. TCHAR szMessageBoxTitle[1024];
  563. TCHAR szMessageBoxText[1024];
  564. LoadString(_Module.GetResourceInstance(), IDS_NEED_EVENT_CATEGORY, szMessageBoxText, ARRAYSIZE(szMessageBoxText));
  565. LoadString(_Module.GetResourceInstance(), IDS_NEED_EVENT_CATEGORY_TITLE, szMessageBoxTitle, ARRAYSIZE(szMessageBoxTitle));
  566. MessageBox(szMessageBoxText, szMessageBoxTitle, MB_OK);
  567. DebugTrace(0, "OnOK exiting but not calling EndDialog");
  568. TraceFunctLeave();
  569. bHandled = TRUE;
  570. return 0;
  571. }
  572. if (SendDlgItemMessage(IDC_COMBO_SEVERITY, CB_GETCURSEL, 0, 0) == CB_ERR) {
  573. DebugTrace(0, "User did not select a severity, calling MessageBox");
  574. TCHAR szMessageBoxTitle[1024];
  575. TCHAR szMessageBoxText[1024];
  576. LoadString(_Module.GetResourceInstance(), IDS_NEED_SEVERITY, szMessageBoxText, ARRAYSIZE(szMessageBoxText));
  577. LoadString(_Module.GetResourceInstance(), IDS_NEED_SEVERITY_TITLE, szMessageBoxTitle, ARRAYSIZE(szMessageBoxTitle));
  578. MessageBox(szMessageBoxText, szMessageBoxTitle, MB_OK);
  579. DebugTrace(0, "OnOK exiting but not calling EndDialog");
  580. TraceFunctLeave();
  581. bHandled = TRUE;
  582. return 0;
  583. }
  584. if (SendDlgItemMessage(IDC_EDIT_COMMENT, WM_GETTEXTLENGTH, 0, 0) == 0) {
  585. DebugTrace(0, "User did not type in a comment, calling MessageBox");
  586. TCHAR szMessageBoxTitle[1024];
  587. TCHAR szMessageBoxText[1024];
  588. LoadString(_Module.GetResourceInstance(), IDS_NEED_COMMENT, szMessageBoxText, ARRAYSIZE(szMessageBoxText));
  589. LoadString(_Module.GetResourceInstance(), IDS_NEED_COMMENT_TITLE, szMessageBoxTitle, ARRAYSIZE(szMessageBoxTitle));
  590. MessageBox(szMessageBoxText, szMessageBoxTitle, MB_OK);
  591. DebugTrace(0, "OnOK exiting but not calling EndDialog");
  592. TraceFunctLeave();
  593. bHandled = TRUE;
  594. return 0;
  595. }
  596. //
  597. // Write the email address and beta ID for this user to the registry.
  598. //
  599. SendDlgItemMessage(IDC_EDIT_EMAIL_ADDRESS, WM_GETTEXT, MAX_EMAIL_ADDRESS_SIZE + 1, (LPARAM) m_lldata.szEmailAddress);
  600. SendDlgItemMessage(IDC_EDIT_BETA_ID, WM_GETTEXT, MAX_BETA_ID_SIZE + 1, (LPARAM) m_lldata.szBetaID);
  601. HKEY hKey;
  602. DWORD dwRet;
  603. if ((dwRet = RegCreateKeyEx(HKEY_CURRENT_USER,
  604. _T("Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments"),
  605. 0,
  606. _T(""),
  607. REG_OPTION_NON_VOLATILE,
  608. KEY_READ + KEY_SET_VALUE,
  609. NULL,
  610. &hKey,
  611. NULL)) != ERROR_SUCCESS) {
  612. DebugTrace(0, "Could not create HKCU\\Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments key in the registry (RegCreateKeyEx returned %ld)", dwRet);
  613. }
  614. else {
  615. if ((dwRet = RegSetValueEx(hKey,
  616. _T("Email Address"),
  617. NULL,
  618. REG_SZ,
  619. (LPBYTE) m_lldata.szEmailAddress,
  620. (_tcslen(m_lldata.szEmailAddress) + 1) * sizeof(TCHAR))) != ERROR_SUCCESS)
  621. DebugTrace(0, "Failed to write Email Address value to the HKCU\\Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments registry key (RegSetValueEx returned %ld)", dwRet);
  622. else
  623. DebugTrace(0, "Wrote email address for this user to the registry: %s", m_lldata.szEmailAddress);
  624. if ((dwRet = RegSetValueEx(hKey,
  625. _T("Beta ID"),
  626. NULL,
  627. REG_SZ,
  628. (LPBYTE) m_lldata.szBetaID,
  629. (_tcslen(m_lldata.szBetaID) + 1) * sizeof(TCHAR))) != ERROR_SUCCESS)
  630. DebugTrace(0, "Failed to write Beta ID value to the HKCU\\Software\\Microsoft\\PCHealth\\Clients\\Dialog Comments registry key (RegSetValueEx returned %ld)", dwRet);
  631. else
  632. DebugTrace(0, "Wrote beta ID for this user to the registry: %s", m_lldata.szBetaID);
  633. RegCloseKey(hKey);
  634. }
  635. //
  636. // Call the routine to format data collected and transport
  637. // it to server
  638. DebugTrace(0, "Calling GetInfoAndLogLameButton");
  639. GetInfoAndLogLameButton();
  640. //
  641. // Kill the comment dialog
  642. //
  643. DebugTrace(0, "Calling EndDialog on wID: %ld", wID);
  644. EndDialog(wID);
  645. TraceFunctLeave();
  646. bHandled = TRUE;
  647. return 0;
  648. }
  649. //
  650. // CSurveyDlg::OnCancel: This executes when user cancels the comment
  651. //
  652. LRESULT
  653. CSurveyDlg::OnCancel(
  654. WORD wNotifyCode,
  655. WORD wID,
  656. HWND hWndCtl,
  657. BOOL& bHandled
  658. )
  659. {
  660. //
  661. // Kill the comment dialog
  662. //
  663. EndDialog(wID);
  664. bHandled = TRUE;
  665. return 0;
  666. }
  667. //
  668. // CSurveyDlg::IsMessageBox: This routine returns TRUE if the m_hwndTarget is indeed
  669. // a message box
  670. //
  671. BOOL
  672. CSurveyDlg::IsMessageBox()
  673. {
  674. TraceFunctEnter("CSurveyDlg::IsMessageBox");
  675. BOOL fRetVal = FALSE;
  676. LPMSGBOXPARAMS lpMsgData = NULL;
  677. UINT cbSize = 0;
  678. if(NULL == m_hwndTarget)
  679. {
  680. FatalTrace(0, "m_hwndTarget is NULL");
  681. goto done;
  682. }
  683. //
  684. // Looking at msgbox.c, I realized that the MsgBoxParams are stored in the MessageBox
  685. // dialog itself using a SetWindowLongPtr. Hence GetWindowLongPtr should retrieve it for
  686. // messageboxes.
  687. //
  688. lpMsgData = (LPMSGBOXPARAMS)::GetWindowLongPtr(m_hwndTarget, GWLP_USERDATA);
  689. if(NULL == lpMsgData)
  690. {
  691. FatalTrace(0, "GetWindowLongPtr failed. Error: %ld", GetLastError());
  692. fRetVal = FALSE;
  693. goto done;
  694. }
  695. //
  696. // There are windows other than msgboxes that can have data set in WindowLongPtr
  697. // The following is a check to see if we have a message box or not. If this is not
  698. // a real message box, it will throw an exception when we try to read cbSize for
  699. // some cases.
  700. //
  701. DebugTrace(0, "Figuring if this is a msgbox. Addr: %ld", lpMsgData);
  702. __try
  703. {
  704. cbSize = lpMsgData->cbSize;
  705. }
  706. __except( EXCEPTION_EXECUTE_HANDLER )
  707. {
  708. FatalTrace(0, "Corrupt MSGBOXPARAMS");
  709. goto done;
  710. }
  711. DebugTrace(0, "cbSize: %ld", cbSize);
  712. //
  713. // Some more sanity checks
  714. //
  715. if(( cbSize > MSGBOX_TEXT_SIZE)||( cbSize < sizeof(MSGBOXPARAMS)))
  716. {
  717. FatalTrace(0, "Invalid MSGBOXPARAMS");
  718. goto done;
  719. }
  720. else
  721. {
  722. //
  723. // We have a MessageBox
  724. //
  725. fRetVal = TRUE;
  726. //
  727. // Extract the message box text from the MSGBOXPARAMS
  728. //
  729. // NTRAID#NTBUG9-155100-2000/08/13-jasonr
  730. //
  731. // Before we determined the maximum number of characters to copy from
  732. // cbsize. I have no idea why we were doing that. Now the maximum is
  733. // based on the size of the m_lldata.szMsgBoxText buffer. I wrapped the
  734. // copy within a __try __except block just to be safe.
  735. //
  736. ZeroMemory(m_lldata.szMsgBoxText, sizeof(m_lldata.szMsgBoxText));
  737. __try
  738. {
  739. _tcsncpy((_TCHAR *) m_lldata.szMsgBoxText, (const _TCHAR *) lpMsgData->lpszText, (sizeof(m_lldata.szMsgBoxText) / sizeof(_TCHAR)) - 1);
  740. }
  741. __except (EXCEPTION_EXECUTE_HANDLER)
  742. {
  743. }
  744. DebugTrace(0,"MessageBox Text: %ls\n", m_lldata.szMsgBoxText);
  745. }
  746. done:
  747. TraceFunctLeave();
  748. return fRetVal;
  749. }
  750. //
  751. // CSurveyDlg::GetInfoAndLogLameButton: The routine that does most of the work to gather
  752. // and format the comment information and uploads
  753. // it to server
  754. void
  755. CSurveyDlg::GetInfoAndLogLameButton()
  756. {
  757. TraceFunctEnter("GetInfoAndLogLameButton");
  758. m_lldata.versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  759. //
  760. // Get version of binary that launched the comments hook
  761. //
  762. GetVersionEx(&m_lldata.versionInfo);
  763. TCHAR szClass[CLASS_SIZE];
  764. TCHAR szTitle[TITLE_SIZE];
  765. TCHAR szComment[COMMENT_TEXT_SIZE + 1];
  766. TCHAR szText[1024];
  767. //
  768. // Clear out buffers First
  769. //
  770. ZeroMemory(m_lldata.szTitle, TITLE_SIZE);
  771. ZeroMemory(m_lldata.szClass, CLASS_SIZE);
  772. ZeroMemory(m_lldata.szMsgBoxText, MSGBOX_TEXT_SIZE);
  773. //
  774. // Gather the Title of the target window
  775. //
  776. if (::GetWindowText(m_hwndTarget, szTitle, TITLE_SIZE) == 0)
  777. {
  778. DebugTrace(0, "szTitle is NULL");
  779. ZeroMemory(m_lldata.szTitle, TITLE_SIZE);
  780. }
  781. else
  782. {
  783. _tcscpy(m_lldata.szTitle, szTitle);
  784. }
  785. DebugTrace(0, "m_lldata.szTitle: %ls", m_lldata.szTitle);
  786. //
  787. // Gather the class of the target Window
  788. //
  789. if (::RealGetWindowClass(m_hwndTarget, szClass, CLASS_SIZE) == 0)
  790. {
  791. DebugTrace(0, "szClass is NULL");
  792. ZeroMemory(m_lldata.szClass, CLASS_SIZE);
  793. }
  794. else
  795. {
  796. _tcscpy(m_lldata.szClass, szClass);
  797. }
  798. DebugTrace(0, "m_lldata.szClass: %ls", m_lldata.szClass);
  799. //
  800. // Check if m_hwndTarget is a MessageBox.
  801. //
  802. if(TRUE == IsMessageBox())
  803. {
  804. //
  805. // We are dealing with a Message Box
  806. //
  807. DebugTrace(0, "We are dealing with a Message Box");
  808. //
  809. // The following disabled code is for reference reasons,
  810. // Earlier, we used to send a WM_COPY to obtain the message box text
  811. // Now, IsMessageBox does the trick
  812. #ifdef _SEND_WM_COPY
  813. if(NULL != m_hwndTarget)
  814. {
  815. HANDLE hMem = NULL;
  816. WCHAR *pData = NULL;
  817. TCHAR szTitleTmp [MSGBOX_TEXT_SIZE];
  818. INT nChar = 0;
  819. INT nChar1 = 0;
  820. //
  821. // Send WM_COPY to the message box. WM_COPY causes the MessageBox to write
  822. // it's message box text to the clipboard
  823. //
  824. DebugTrace(0, "Sending WM_COPY to m_hwndTarget");
  825. SendMessage( m_hwndTarget, WM_COPY, 0, 0 );
  826. //
  827. // Open the clipboard of the current task
  828. //
  829. DebugTrace(0, "OpenClipboard");
  830. if(FALSE == ::OpenClipboard( m_hwndTarget ))
  831. {
  832. FatalTrace(0, "OpenClipBoard failed. Error: %ld", GetLastError());
  833. goto done;
  834. }
  835. //
  836. // Get the Data written by the target Window to the Clipboard
  837. //
  838. DebugTrace(0, "GetClipboardData");
  839. hMem = GetClipboardData( CF_UNICODETEXT );
  840. if(NULL == hMem)
  841. {
  842. FatalTrace(0, "GetClipBoardData failed. Error: %ld\n", GetLastError());
  843. goto done;
  844. }
  845. //
  846. // Get the memory location for the data written to the clipboard
  847. //
  848. DebugTrace(0, "GlobalLock");
  849. pData = (LPWSTR) GlobalLock( hMem );
  850. if(NULL == pData)
  851. {
  852. FatalTrace(0, "GlobalLock failed. Error: %ld\n", GetLastError());
  853. goto done;
  854. }
  855. if(pData[0] == '\0')
  856. {
  857. FatalTrace(0, "Nothing in clipboard");
  858. goto done;
  859. }
  860. //
  861. // Advance past the first ---------------------------\r\n
  862. //
  863. DebugTrace(0, "Advancing past the first ---");
  864. for(nChar = 0; (nChar < _tcslen(pData))&&(!((pData[nChar] == '-')&&(pData[nChar+1] == '\r')&&(pData[nChar+2] == '\n'))); nChar++);
  865. //
  866. // Check to see if we have data in the right format.
  867. //
  868. if ( nChar == _tcslen(pData))
  869. {
  870. //
  871. // This check is necessary, because the check for messagebox based on the window class
  872. // is best effort and not 100% accurate. So, we may think that the target window is a
  873. // message box, where as it may not be. This check is therefore necessary.
  874. FatalTrace(0, "This is not a valid messagebox");
  875. goto done;
  876. }
  877. nChar += 3;
  878. DebugTrace(0, "nChar: %ld", nChar);
  879. //
  880. // Extract the Caption
  881. //
  882. DebugTrace(0, "Extracting the Caption");
  883. for(nChar1 = 0; !((pData[nChar] == '\r')&&(pData[nChar+1] == '\n')&&(pData[nChar+2] == '-')); nChar1++,nChar++)
  884. {
  885. szTitleTmp[ nChar1 ] = pData[nChar];
  886. }
  887. nChar += 2;
  888. DebugTrace(0, "nChar: %ld", nChar);
  889. szTitleTmp[ nChar1 + 1] = '\0';
  890. DebugTrace(0, "MessageBox Title: %ls", szTitleTmp);
  891. //
  892. // Advance past the second ---------------------------\r\n
  893. //
  894. DebugTrace(0, "Advancing past the second ---");
  895. for(; !((pData[nChar] == '-')&&(pData[nChar+1] == '\r')&&(pData[nChar+2] == '\n')); nChar++);
  896. nChar += 3;
  897. DebugTrace(0, "nChar: %ld", nChar);
  898. //
  899. // Extract the message box text
  900. //
  901. DebugTrace(0, "Extracting the MessageBox text");
  902. for(nChar1 = 0; !((pData[nChar] == '\r')&&(pData[nChar+1] == '\n')&&(pData[nChar+2] == '-')); nChar1++,nChar++)
  903. {
  904. m_lldata.szMsgBoxText[ nChar1 ] = pData[nChar];
  905. }
  906. m_lldata.szMsgBoxText[ nChar1 + 1] = '\0';
  907. DebugTrace(0,"MessageBox szText: %ls", m_lldata.szMsgBoxText);
  908. //
  909. // Clear out the clipboard
  910. //
  911. EmptyClipboard();
  912. done:
  913. if(NULL != hMem)
  914. {
  915. if(FALSE == GlobalUnlock( hMem ))
  916. {
  917. printf("GlobalUnlock failed. Error; %ld\n", GetLastError());
  918. }
  919. }
  920. CloseClipboard();
  921. }
  922. #endif // _SEND_WM_COPY
  923. }
  924. else
  925. {
  926. DebugTrace(0, "class: %s is not a MessageBox", szClass);
  927. }
  928. //
  929. // Obtain the Comment
  930. //
  931. if (GetDlgItemText(IDC_EDIT_COMMENT, szComment, COMMENT_TEXT_SIZE + 1) == 0)
  932. {
  933. DebugTrace(0, "Comment is NULL");
  934. ZeroMemory(m_lldata.szComment, COMMENT_TEXT_SIZE + 1);
  935. }
  936. else
  937. {
  938. _tcscpy(m_lldata.szComment, szComment);
  939. }
  940. DebugTrace(0, "sizeof Comment text: %ld", _tcslen( m_lldata.szComment ));
  941. //
  942. // Populate m_lldata.dwEventCategory from the selection in
  943. // IDC_COMBO_EVENT_CATEGORY. Note that this is purposefully done with a
  944. // switch statement, rather than with an algorithmic calculation such as:
  945. //
  946. // m_lldata.dwEventCategory = 1 + SendDlgItemMessage(IDC_COMBO_EVENT_CATEGORY, CB_GETCURSEL, 0, 0);
  947. //
  948. // If we ever change the possible selections in the future, the algorithm
  949. // might not be correct, so we don't use it.
  950. //
  951. LRESULT dwCurSel = SendDlgItemMessage(IDC_COMBO_EVENT_CATEGORY, CB_GETCURSEL, 0, 0);
  952. _ASSERT(dwCurSel == 0 || dwCurSel == 1 || dwCurSel == 2 || dwCurSel == 3 || dwCurSel == 4 || dwCurSel == 5);
  953. switch (dwCurSel) {
  954. case 0:
  955. m_lldata.dwEventCategory = 1;
  956. break;
  957. case 1:
  958. m_lldata.dwEventCategory = 2;
  959. break;
  960. case 2:
  961. m_lldata.dwEventCategory = 3;
  962. break;
  963. case 3:
  964. m_lldata.dwEventCategory = 4;
  965. break;
  966. case 4:
  967. m_lldata.dwEventCategory = 5;
  968. break;
  969. case 5:
  970. m_lldata.dwEventCategory = 6;
  971. break;
  972. }
  973. //
  974. // Populate m_lldata.dwSeverity from the selection in IDC_COMBO_SEVERITY.
  975. // Note that this is purposefully done with a switch statement, rather than
  976. // with an algorithmic calculation such as:
  977. //
  978. // m_lldata.dwSeverity = 1 + SendDlgItemMessage(IDC_COMBO_SEVERITY, CB_GETCURSEL, 0, 0);
  979. //
  980. // If we ever change the possible selections in the future, the algorithm
  981. // might not be correct, so we don't use it.
  982. //
  983. dwCurSel = SendDlgItemMessage(IDC_COMBO_SEVERITY, CB_GETCURSEL, 0, 0);
  984. _ASSERT(dwCurSel == 0 || dwCurSel == 1 || dwCurSel == 2 || dwCurSel == 3);
  985. switch (dwCurSel) {
  986. case 0:
  987. m_lldata.dwSeverity = 1;
  988. break;
  989. case 1:
  990. m_lldata.dwSeverity = 2;
  991. break;
  992. case 2:
  993. m_lldata.dwSeverity = 3;
  994. break;
  995. case 3:
  996. m_lldata.dwSeverity = 4;
  997. break;
  998. }
  999. //
  1000. // It doesn't seem like a good idea to initialize COM on somebody
  1001. // else's thread, so we launch a new thread to do the reporting.
  1002. //
  1003. DebugTrace(0, "Creating Seperate thread to deal with COM");
  1004. UINT uThreadId; // dummy
  1005. HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, LogLameButtonThread, &m_lldata, 0, &uThreadId);
  1006. if (hThread != 0) {
  1007. DWORD dwEvent = WaitForSingleObject(hThread, INFINITE);
  1008. _ASSERT(dwEvent == WAIT_OBJECT_0);
  1009. //
  1010. // NTRAID#NTBUG9-154248-2000/08/08-jasonr
  1011. // NTRAID#NTBUG9-152439-2000/08/08-jasonr
  1012. //
  1013. // We used to pop up the "Thank You" message box in the new thread.
  1014. // Now we pop it up in the dialog box thread instead to fix these bugs.
  1015. // The new thread now returns 0 to indicate success, 1 to indicate
  1016. // failure. We only pop up the dialog box on success.
  1017. //
  1018. DWORD dwExitCode;
  1019. if (!GetExitCodeThread(hThread, &dwExitCode))
  1020. dwExitCode = 1;
  1021. CloseHandle(hThread);
  1022. TCHAR szThankYouMessage[1024];
  1023. TCHAR szThankYouMessageTitle[1024];
  1024. LoadString(_Module.GetResourceInstance(),
  1025. IDS_THANK_YOU,
  1026. szThankYouMessage,
  1027. ARRAYSIZE(szThankYouMessage));
  1028. LoadString(_Module.GetResourceInstance(),
  1029. IDS_THANK_YOU_TITLE,
  1030. szThankYouMessageTitle,
  1031. ARRAYSIZE(szThankYouMessageTitle));
  1032. MessageBox(szThankYouMessage, szThankYouMessageTitle, MB_OK);
  1033. }
  1034. TraceFunctLeave();
  1035. }
  1036. //
  1037. // DllMain : The DllMain for LAMEBTN.DLL
  1038. //
  1039. BOOL WINAPI DllMain
  1040. (
  1041. HINSTANCE hInstance,
  1042. DWORD dwreason,
  1043. LPVOID reserved
  1044. )
  1045. {
  1046. switch (dwreason) {
  1047. case DLL_PROCESS_ATTACH:
  1048. // _CrtSetBreakAlloc(275);
  1049. #ifndef NOTRACE
  1050. InitAsyncTrace();
  1051. #endif
  1052. ghDllInst = hInstance;
  1053. DisableThreadLibraryCalls(hInstance);
  1054. _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_WNDW);
  1055. _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
  1056. _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
  1057. _Module.Init(NULL, hInstance);
  1058. break;
  1059. case DLL_THREAD_ATTACH:
  1060. case DLL_THREAD_DETACH:
  1061. break;
  1062. case DLL_PROCESS_DETACH:
  1063. _CrtDumpMemoryLeaks();
  1064. _Module.Term();
  1065. TermAsyncTrace();
  1066. break;
  1067. }
  1068. return TRUE;
  1069. }
  1070. //
  1071. // LogLameButtonThread: This is the thread spawned by the comments dialog
  1072. // to format the data collected into XML and upload
  1073. // to server
  1074. unsigned int __stdcall
  1075. LogLameButtonThread(
  1076. void* pvLogData
  1077. )
  1078. {
  1079. TraceFunctEnter("LogLameButtonThread");
  1080. //
  1081. // NTRAID#NTBUG9-154248-2000/08/08-jasonr
  1082. // NTRAID#NTBUG9-152439-2000/08/08-jasonr
  1083. //
  1084. // We used to pop up the "Thank You" message box in the new thread.
  1085. // Now we pop it up in the dialog box thread instead to fix these bugs.
  1086. // The new thread now returns 0 to indicate success, 1 to indicate
  1087. // failure. We only pop up the dialog box on success.
  1088. //
  1089. int iRet = 1;
  1090. //
  1091. // Initialize COM
  1092. //
  1093. DebugTrace(0, "Initializing COM");
  1094. HRESULT hrCoInit = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  1095. HRESULT hr = S_OK;
  1096. if (SUCCEEDED(hrCoInit)) {
  1097. //
  1098. // Create a temp file that will hold a minidump of the current process.
  1099. //
  1100. WCHAR szTempPath[MAX_PATH];
  1101. if ((GetTempPathW(MAX_PATH, szTempPath) == 0) || (GetTempFileNameW(szTempPath, _T("EMI"), 0, ((LAMELOGDATA *) pvLogData)->szMiniDumpPath) == 0)) {
  1102. DebugTrace(0, "GetTempPathW() or GetTempFileNameW() failed, minidump will not be created");
  1103. ((LAMELOGDATA *) pvLogData)->szMiniDumpPath[0] = 0;
  1104. }
  1105. else {
  1106. DebugTrace(0, "Minidump will be stored in temp file \"%s\"", ((LAMELOGDATA *) pvLogData)->szMiniDumpPath);
  1107. //
  1108. // Build a command line for executing DUMPREP.EXE.
  1109. //
  1110. WCHAR szWindowsDir[MAX_PATH+1];
  1111. WCHAR szCmdLine[1024];
  1112. ZeroMemory(szWindowsDir, sizeof(szWindowsDir));
  1113. ZeroMemory(szCmdLine, sizeof(szCmdLine));
  1114. GetSystemWindowsDirectoryW(szWindowsDir, MAX_PATH);
  1115. wsprintf(szCmdLine, L"%s\\system32\\dumprep.exe %lu -d 7 7 \"%s\"", szWindowsDir, GetCurrentProcessId(), ((LAMELOGDATA *) pvLogData)->szMiniDumpPath);
  1116. //
  1117. // CreateProcess() on DUMPREP.EXE to create the minidump.
  1118. //
  1119. STARTUPINFO si;
  1120. PROCESS_INFORMATION pi;
  1121. ZeroMemory(&si, sizeof(si));
  1122. si.cb = sizeof(si);
  1123. if (!CreateProcessW(NULL, szCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
  1124. DebugTrace(0, "CreateProcess() failed cmdline \"%s\" with error %ld; minidump will not be created", szCmdLine, GetLastError());
  1125. DeleteFileW(((LAMELOGDATA *) pvLogData)->szMiniDumpPath);
  1126. ((LAMELOGDATA *) pvLogData)->szMiniDumpPath[0] = 0;
  1127. }
  1128. else {
  1129. DebugTrace(0, "CreateProcess() succeeded on cmdline \"%s\" with PID %ld, TID %ld; waiting up to 60 seconds...", szCmdLine, pi.dwProcessId, pi.dwThreadId);
  1130. //
  1131. // Wait for DUMPREP.EXE to exit and interpret the exit code:
  1132. // frrvOk (defined in faultrep.h) is success; anything else is
  1133. // failure. Wait for a maximum of 60 seconds.
  1134. //
  1135. DWORD dwRet;
  1136. BOOL bSuccess = FALSE;
  1137. switch (dwRet = WaitForSingleObject(pi.hProcess, 60000)) {
  1138. case WAIT_OBJECT_0:
  1139. DebugTrace(0, "The process handle is signalled");
  1140. if (!GetExitCodeProcess(pi.hProcess, &dwRet)) {
  1141. DebugTrace(0, "GetExitCodeProcess() failed; GetLastError() = %lu; minidump will not be created", GetLastError());
  1142. DeleteFileW(((LAMELOGDATA *) pvLogData)->szMiniDumpPath);
  1143. ((LAMELOGDATA *) pvLogData)->szMiniDumpPath[0] = 0;
  1144. }
  1145. else if (dwRet != frrvOk) {
  1146. DebugTrace(0, "The process failed with exit code %lu; minidump will not be created", dwRet);
  1147. DeleteFileW(((LAMELOGDATA *) pvLogData)->szMiniDumpPath);
  1148. ((LAMELOGDATA *) pvLogData)->szMiniDumpPath[0] = 0;
  1149. }
  1150. else {
  1151. DebugTrace(0, "The process exited and the minidump was created successfully");
  1152. bSuccess = TRUE;
  1153. }
  1154. break;
  1155. case WAIT_TIMEOUT:
  1156. DebugTrace(0, "WaitForSingleObject() returned WAIT_TIMEOUT after 60 seconds; minidump will not be created");
  1157. DeleteFileW(((LAMELOGDATA *) pvLogData)->szMiniDumpPath);
  1158. ((LAMELOGDATA *) pvLogData)->szMiniDumpPath[0] = 0;
  1159. break;
  1160. case WAIT_FAILED:
  1161. DebugTrace(0, "WaitForSingleObject() returned WAIT_FAILED, GetLastError() = %lu; minidump will not be created", GetLastError());
  1162. DeleteFileW(((LAMELOGDATA *) pvLogData)->szMiniDumpPath);
  1163. ((LAMELOGDATA *) pvLogData)->szMiniDumpPath[0] = 0;
  1164. break;
  1165. default:
  1166. DebugTrace(0, "WaitForSingleObject() returned unknown code %lu, GetLastError() = %lu; minidump will not be created", dwRet, GetLastError());
  1167. DeleteFileW(((LAMELOGDATA *) pvLogData)->szMiniDumpPath);
  1168. ((LAMELOGDATA *) pvLogData)->szMiniDumpPath[0] = 0;
  1169. break;
  1170. }
  1171. CloseHandle(pi.hProcess);
  1172. CloseHandle(pi.hThread);
  1173. if (bSuccess) {
  1174. //
  1175. // Create a .CAB file
  1176. //
  1177. WCHAR szCABPath[MAX_PATH+1];
  1178. wcscpy(szCABPath, ((LAMELOGDATA *) pvLogData)->szMiniDumpPath);
  1179. wcscat(szCABPath, L".cab");
  1180. if (FAILED(MPC::CompressAsCabinet(((LAMELOGDATA *) pvLogData)->szMiniDumpPath, szCABPath, L"minidump.dmp")) ||
  1181. !MoveFileExW(szCABPath, ((LAMELOGDATA *) pvLogData)->szMiniDumpPath, MOVEFILE_REPLACE_EXISTING + MOVEFILE_WRITE_THROUGH)) {
  1182. DebugTrace(0, "MPC::CompressAsCabinet failed (more likely) or MoveFileExW failed (less likely); minidump will not be created");
  1183. DeleteFileW(((LAMELOGDATA *) pvLogData)->szMiniDumpPath);
  1184. DeleteFileW(szCABPath);
  1185. ((LAMELOGDATA *) pvLogData)->szMiniDumpPath[0] = 0;
  1186. }
  1187. else
  1188. DebugTrace(0, "The minidump was compressed successfully as a .CAB file");
  1189. }
  1190. }
  1191. }
  1192. //
  1193. // Call the Routine that does the real work (defined in logging.cpp)
  1194. //
  1195. DebugTrace(0, "Calling LogLameBtn");
  1196. iRet = LogLameButton((LAMELOGDATA*) (pvLogData));
  1197. CoUninitialize();
  1198. }
  1199. else
  1200. {
  1201. FatalTrace(0, "Failed to initialize COM");
  1202. goto done;
  1203. }
  1204. done:
  1205. TraceFunctLeave();
  1206. return iRet;
  1207. }
  1208. //
  1209. // CommentReport: user calls into CommentReport passing in the hWnd and call stack when
  1210. // the Comments? link is clicked (This is the routine called via the Comments hook)
  1211. VOID WINAPI
  1212. CommentReport(
  1213. HWND hwnd, // [in] Window of Dialog being commented on
  1214. PVOID pv // [in] Call stack
  1215. )
  1216. {
  1217. TraceFunctEnter("CommentReport");
  1218. //
  1219. // Fix for bug 141367. Walk up through the stack of windows examining the
  1220. // module file name of each one. Count the number of windows that are owned
  1221. // by lamebtn.dll. If we find two it means there are two lamebtn dialogs on
  1222. // the screen already. In that case, return immediately. (We allow up to
  1223. // two of them so the user can comment on the lamebtn dialog itself.)
  1224. //
  1225. HMODULE hThisModule = NULL;
  1226. if ((hThisModule = GetModuleHandle(L"lamebtn.dll")) != NULL)
  1227. {
  1228. TCHAR szThisModule[MAX_PATH] = { 0 };
  1229. if (GetModuleFileName(hThisModule, szThisModule, MAX_PATH) > 0)
  1230. {
  1231. HWND hWnd2 = hwnd;
  1232. TCHAR szWndModule[MAX_PATH] = { 0 };
  1233. DWORD dwLamebtnDLLWindows = 0;
  1234. while (hWnd2 != NULL)
  1235. {
  1236. if ((GetWindowModuleFileName(hWnd2, szWndModule, MAX_PATH) > 0) &&
  1237. (_wcsicmp(szThisModule, szWndModule) == 0))
  1238. {
  1239. dwLamebtnDLLWindows++;
  1240. if (dwLamebtnDLLWindows >= 2) {
  1241. DebugTrace(0, "Two lamebtn dialogs are already up, therefore we won't bring up another one");
  1242. return;
  1243. }
  1244. }
  1245. hWnd2 = GetParent(hWnd2);
  1246. }
  1247. }
  1248. }
  1249. #ifdef _GENERATE_STACK_AT_CLIENT
  1250. TCHAR szWindowText[MAX_BUF_SIZE + 1];
  1251. TCHAR szBigBuff[2 * MAX_BUF_SIZE + 1];
  1252. TCHAR tmpbuf[128 + 1];
  1253. //
  1254. // Obtain the WindowText
  1255. //
  1256. GetWindowText(hwnd, szWindowText, MAX_BUF_SIZE);
  1257. //
  1258. // pvStackTrace can be NULL
  1259. //
  1260. if(pv)
  1261. {
  1262. 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));
  1263. }
  1264. else
  1265. {
  1266. //
  1267. // Generating a stack walk at the client is not really a good idea. Since it will work only
  1268. // on x86. Absence of call stack in the uploaded comment report should be handled at the server
  1269. // The following code though disabled is still kept here for reference.
  1270. //
  1271. RtlWalkFrameChain_t fnRtlWalkFrameChain =
  1272. (RtlWalkFrameChain_t) GetProcAddress(GetModuleHandle(_T("NTDLL.DLL")), "RtlWalkFrameChain");
  1273. STACKTRACEDATA* pstd = (STACKTRACEDATA*) _alloca(offsetof(STACKTRACEDATA, callers[64]));
  1274. pstd->nCallers = fnRtlWalkFrameChain(pstd->callers, 64, 0);
  1275. pv = (PVOID)pstd;
  1276. if(NULL != pv)
  1277. {
  1278. DebugTrace(0, "Generated a new stacktrace");
  1279. 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));
  1280. }
  1281. else
  1282. {
  1283. FatalTrace(0, "StackTrace not available...");
  1284. wsprintf(tmpbuf, TEXT("StackTrace not available..."));
  1285. }
  1286. FatalTrace(0, "StackTrace not available...");
  1287. wsprintf(tmpbuf, TEXT("StackTrace not available..."));
  1288. }
  1289. DebugTrace(0, "Got comment from window '%ls'", szWindowText, tmpbuf);
  1290. DebugTrace(0, "StackTrace: '%ls'", tmpbuf);
  1291. #endif
  1292. //
  1293. // Create the Comment dialog
  1294. //
  1295. CSurveyDlg dlg;
  1296. //
  1297. // Initialize the comment dialog
  1298. //
  1299. DebugTrace(0, "Calling dlg.Init");
  1300. dlg.Init(hwnd, (PSTACKTRACEDATA) pv);
  1301. //
  1302. // Launch the comment dialog
  1303. //
  1304. DebugTrace(0, "Calling DoModal");
  1305. dlg.DoModal(hwnd, NULL);
  1306. TraceFunctLeave();
  1307. }
  1308. //
  1309. // nsoy sayz: The following code is used by the MessageBox hook(which for some
  1310. // mysterious reason disappeared when Neptune code was moved to Whistler)
  1311. // Hence, the code used by the MessageBox hook is henceforth
  1312. // not maintained.
  1313. //
  1314. // UploadInstrumentationCollectionData: Dunno why this is present. Walter must have
  1315. // had some use for this.
  1316. VOID WINAPI UploadInstrumentationCollectionData()
  1317. {
  1318. //bugbug: do something meaningful
  1319. ;
  1320. }
  1321. //
  1322. // LogMessageBoxThread: MessageBox hook data collection thread.
  1323. // - present for historical reasons. Not needed for Whistler
  1324. //
  1325. unsigned int __stdcall LogMessageBoxThread(void* pvLogData)
  1326. {
  1327. HRESULT hrCoInit = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  1328. if (SUCCEEDED(hrCoInit)) {
  1329. LogMessageBox((MSGBOXLOGDATA*) pvLogData);
  1330. CoUninitialize();
  1331. }
  1332. return 0;
  1333. }