Leaked source code of windows server 2003
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.

1508 lines
37 KiB

  1. /**
  2. Copyright (c) Microsoft Corporation 1999-2000
  3. Module Name:
  4. monitor.cpp
  5. Abstract:
  6. This module implements the fax monitor dialog.
  7. **/
  8. #include <windows.h>
  9. #include <faxreg.h>
  10. #include <faxutil.h>
  11. #include <fxsapip.h>
  12. #include <commctrl.h>
  13. #include <tchar.h>
  14. #include <DebugEx.h>
  15. #include <list>
  16. using namespace std;
  17. #include "monitor.h"
  18. #include "resource.h"
  19. #define DURATION_TIMER_RESOLUTION 500 // Resolution (millisecs) of duration text update timer
  20. //////////////////////////////////////////////////////////////
  21. // Global data
  22. //
  23. extern HINSTANCE g_hModule; // DLL Global instance
  24. extern HINSTANCE g_hResource; // Resource DLL handle
  25. extern HANDLE g_hFaxSvcHandle;
  26. extern DWORD g_dwCurrentJobID;
  27. extern CONFIG_OPTIONS g_ConfigOptions;
  28. extern TCHAR g_szRemoteId[MAX_PATH]; // Sender ID or Recipient ID
  29. extern HCALL g_hCall; // Handle to call (from FAX_EVENT_TYPE_NEW_CALL)
  30. //
  31. // Events log
  32. //
  33. struct EVENT_ENTRY
  34. {
  35. eIconType eIcon; // Event icon
  36. TCHAR tszTime[30]; // Event time string
  37. TCHAR tszEvent[MAX_PATH]; // Event string
  38. };
  39. typedef EVENT_ENTRY *PEVENT_ENTRY;
  40. typedef list<EVENT_ENTRY> EVENTS_LIST, *PEVENTS_LIST;
  41. EVENTS_LIST g_lstEvents; // Global list of events
  42. #define MAX_EVENT_LIST_SIZE 50 // Maximal number of events in log
  43. //
  44. // Monitor dialog
  45. //
  46. HWND g_hMonitorDlg = NULL;
  47. //
  48. // Controls
  49. //
  50. HWND g_hStatus = NULL; // Status line (static text)
  51. HWND g_hElapsedTime = NULL; // Elapsed time line (static text)
  52. HWND g_hToFrom = NULL; // To/From line (static text)
  53. HWND g_hListDetails = NULL; // Details list control
  54. HWND g_hAnimation = NULL; // Animation control
  55. HWND g_hDisconnect = NULL; // Disconnect button
  56. HICON g_hDlgIcon = NULL; // Dialog main icon
  57. HIMAGELIST g_hDlgImageList = NULL; // Dialog's image list
  58. //
  59. // Data
  60. //
  61. BOOL g_bAnswerNow = FALSE; // TRUE if the dialog button shows 'Answer Now'. FALSE if it shows 'Disconnect'.
  62. DWORD g_dwHeightDelta = 0; // Used when pressing "More >>>" / "Less <<<" to resize the dialog
  63. DWORD g_dwDlgHeight = 0; // The dialog height
  64. BOOL g_bDetails = FALSE; // Is the "More >>>" button pressed?
  65. DeviceState g_devState = FAX_IDLE; // Current fax state (animation)
  66. DWORD g_dwStartTime = 0; // Activity start time (tick counts)
  67. UINT_PTR g_nElapsedTimerId = 0; // Timer id for elapsed time (ticks every 1 second)
  68. TCHAR g_tszTimeSeparator[5] = {0};
  69. DWORD g_dwCurrentAnimationId = 0; // Current animation resource ID
  70. TCHAR g_tszLastEvent[MAX_PATH] = {0}; // The last event string
  71. POINT g_ptPosition = {-1, -1}; // Dialog position
  72. BOOL g_bTopMost = FALSE; // Is the monitor dialog always visible?
  73. #define DETAILS_TIME_COLUMN_WIDTH 90
  74. /////////////////////////////////////////////////////////////////////
  75. // Function prototypes
  76. //
  77. // public
  78. BOOL IsUserGrantedAccess(DWORD);
  79. DWORD OpenFaxMonitor(VOID);
  80. void SetStatusMonitorDeviceState(DeviceState devState);
  81. void OnDisconnect();
  82. void FreeMonitorDialogData (BOOL bShutdown);
  83. // Private
  84. INT_PTR CALLBACK FaxMonitorDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  85. VOID CALLBACK ElapsedTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
  86. void InitMonitorDlg(HWND hDlg);
  87. DWORD UpdateMonitorData(HWND hDlg);
  88. void AddEventToView(PEVENT_ENTRY pEvent);
  89. void OnAlwaysOnTop(HWND hDlg);
  90. void OnDetailsButton(HWND hDlg, BOOL bDetails);
  91. void OnClearLog();
  92. int FaxMessageBox(HWND hWnd, DWORD dwTextID, UINT uType);
  93. DWORD RefreshImageList ();
  94. //////////////////////////////////////////////////////////////////////
  95. // Implementation
  96. //
  97. void
  98. FreeMonitorDialogData (
  99. BOOL bShutdown /* = FALSE */
  100. )
  101. /*++
  102. Routine name : FreeMonitorDialogData
  103. Routine description:
  104. Frees up all the data allocated by the monitor module
  105. Author:
  106. Eran Yariv (EranY), Mar, 2001
  107. Arguments:
  108. bShutdown - [in] TRUE only is the module is shutting down.
  109. Return Value:
  110. None.
  111. --*/
  112. {
  113. DWORD dwRes = ERROR_SUCCESS;
  114. DBG_ENTER(TEXT("FreeMonitorDialogData"), dwRes);
  115. RECT rc = {0};
  116. if(GetWindowRect(g_hMonitorDlg, &rc))
  117. {
  118. g_ptPosition.x = rc.left;
  119. g_ptPosition.y = rc.top;
  120. }
  121. g_hMonitorDlg = NULL;
  122. g_hStatus = NULL;
  123. g_hElapsedTime = NULL;
  124. g_hToFrom = NULL;
  125. g_hListDetails = NULL;
  126. g_hDisconnect = NULL;
  127. g_hAnimation = NULL;
  128. g_dwCurrentAnimationId = 0;
  129. if (g_hDlgImageList)
  130. {
  131. ImageList_Destroy (g_hDlgImageList);
  132. g_hDlgImageList = NULL;
  133. }
  134. if (bShutdown)
  135. {
  136. //
  137. // DLL is shutting down.
  138. //
  139. //
  140. // The icon is cached in memory even when the dialog is closed.
  141. // This is a good time to free it.
  142. //
  143. if(g_nElapsedTimerId)
  144. {
  145. if (!KillTimer(NULL, g_nElapsedTimerId))
  146. {
  147. CALL_FAIL (GENERAL_ERR, TEXT("KillTimer"), GetLastError ());
  148. }
  149. g_nElapsedTimerId = NULL;
  150. }
  151. if (g_hDlgIcon)
  152. {
  153. if (!DestroyIcon (g_hDlgIcon))
  154. {
  155. CALL_FAIL (WINDOW_ERR, TEXT("DestroyIcon"), GetLastError ());
  156. }
  157. g_hDlgIcon = NULL;
  158. }
  159. //
  160. // Also delete all the events from the list
  161. //
  162. try
  163. {
  164. g_lstEvents.clear();
  165. }
  166. catch (exception &ex)
  167. {
  168. VERBOSE (MEM_ERR,
  169. TEXT("Got an STL exception while clearing the events list (%S)"),
  170. ex.what());
  171. }
  172. g_ptPosition.x = -1;
  173. g_ptPosition.y = -1;
  174. }
  175. } // FreeMonitorDialogData
  176. INT_PTR
  177. CALLBACK
  178. FaxMonitorDlgProc(
  179. HWND hwndDlg, // handle to dialog box
  180. UINT uMsg, // message
  181. WPARAM wParam, // first message parameter
  182. LPARAM lParam // second message parameter
  183. )
  184. /*++
  185. Routine description:
  186. fax monitor dialog procedure
  187. Arguments:
  188. HWND hwndDlg, // handle to dialog box
  189. UINT uMsg, // message
  190. WPARAM wParam, // first message parameter
  191. LPARAM lParam // second message parameter
  192. Return Value:
  193. return TRUE if it processed the message
  194. --*/
  195. {
  196. switch ( uMsg )
  197. {
  198. case WM_INITDIALOG:
  199. InitMonitorDlg(hwndDlg);
  200. return TRUE;
  201. case WM_DESTROY:
  202. FreeMonitorDialogData ();
  203. return TRUE;
  204. case WM_COMMAND:
  205. switch(LOWORD(wParam))
  206. {
  207. case IDC_DETAILS:
  208. g_bDetails = !g_bDetails;
  209. OnDetailsButton(hwndDlg, g_bDetails);
  210. return TRUE;
  211. case IDC_ALWAYS_ON_TOP:
  212. OnAlwaysOnTop(hwndDlg);
  213. return TRUE;
  214. case IDC_CLEAR_LOG:
  215. OnClearLog();
  216. return TRUE;
  217. case IDC_DISCONNECT:
  218. OnDisconnect();
  219. return TRUE;
  220. case IDCANCEL:
  221. DestroyWindow( hwndDlg );
  222. return TRUE;
  223. } // switch(LOWORD(wParam))
  224. break;
  225. case WM_HELP:
  226. WinHelpContextPopup(((LPHELPINFO)lParam)->dwContextId, hwndDlg);
  227. return TRUE;
  228. case WM_CONTEXTMENU:
  229. WinHelpContextPopup(GetWindowContextHelpId((HWND)wParam), hwndDlg);
  230. return TRUE;
  231. case WM_SYSCOLORCHANGE:
  232. RefreshImageList ();
  233. return TRUE;
  234. } // switch ( uMsg )
  235. return FALSE;
  236. } // FaxMonitorDlgProc
  237. DWORD
  238. RefreshImageList ()
  239. /*++
  240. Routine name : RefreshImageList
  241. Routine description:
  242. Refreshes the image list and list view background color
  243. Author:
  244. Eran Yariv (EranY), May, 2001
  245. Arguments:
  246. Return Value:
  247. Standard Win32 error code
  248. --*/
  249. {
  250. DWORD dwRes = ERROR_SUCCESS;
  251. DBG_ENTER(TEXT("RefreshImageList"), dwRes);
  252. ListView_SetExtendedListViewStyle(g_hListDetails,
  253. LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP | LVS_EX_ONECLICKACTIVATE);
  254. if (NULL != g_hDlgImageList)
  255. {
  256. ImageList_Destroy (g_hDlgImageList);
  257. g_hDlgImageList = NULL;
  258. }
  259. g_hDlgImageList = ImageList_Create (16, 16, ILC_COLOR8, 4, 0);
  260. if(!g_hDlgImageList)
  261. {
  262. dwRes = GetLastError();
  263. CALL_FAIL (WINDOW_ERR, TEXT("ImageList_Create"), dwRes);
  264. return dwRes;
  265. }
  266. HBITMAP hBmp = (HBITMAP) LoadImage (
  267. g_hModule,
  268. MAKEINTRESOURCE(IDB_LIST_IMAGES),
  269. IMAGE_BITMAP,
  270. 0,
  271. 0,
  272. LR_DEFAULTSIZE | LR_LOADTRANSPARENT);
  273. if (!hBmp)
  274. {
  275. dwRes = GetLastError();
  276. CALL_FAIL (WINDOW_ERR, TEXT("LoadBitmap"), dwRes);
  277. ImageList_Destroy (g_hDlgImageList);
  278. g_hDlgImageList = NULL;
  279. return dwRes;
  280. }
  281. ImageList_Add (g_hDlgImageList, hBmp, NULL);
  282. //
  283. // ImageList_Add creates a copy of the bitmap - it's now safe to delete it
  284. //
  285. ::DeleteObject ((HGDIOBJ)hBmp);
  286. ListView_SetImageList(g_hListDetails, g_hDlgImageList, LVSIL_SMALL);
  287. ListView_SetBkColor (g_hListDetails, ::GetSysColor(COLOR_WINDOW));
  288. return dwRes;
  289. } // RefreshImageList
  290. void
  291. InitMonitorDlg(
  292. HWND hDlg
  293. )
  294. /*++
  295. Routine description:
  296. Initialize fax monitor dialog
  297. Arguments:
  298. hDlg [in] - fax monitor dialog handle
  299. Return Value:
  300. none
  301. --*/
  302. {
  303. DWORD dwRes = ERROR_SUCCESS;
  304. DBG_ENTER(TEXT("InitMonitorDlg"), dwRes);
  305. //
  306. // Set the dialog icon
  307. //
  308. if (NULL == g_hDlgIcon)
  309. {
  310. //
  311. // 1st time the dialog is opened - load the icons
  312. //
  313. g_hDlgIcon = LoadIcon(g_hModule, MAKEINTRESOURCE(IDI_FAX_MONITOR));
  314. if(!g_hDlgIcon)
  315. {
  316. dwRes = GetLastError();
  317. CALL_FAIL (WINDOW_ERR, TEXT ("LoadIcon"), dwRes);
  318. return;
  319. }
  320. }
  321. SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM)g_hDlgIcon);
  322. SendMessage(hDlg, WM_SETICON, ICON_SMALL, (LPARAM)g_hDlgIcon);
  323. //
  324. // Calculate the height of the details part
  325. //
  326. RECT rcList, rcDialog;
  327. if(!GetWindowRect(hDlg, &rcDialog))
  328. {
  329. dwRes = GetLastError();
  330. CALL_FAIL (WINDOW_ERR, TEXT ("GetWindowRect"), dwRes);
  331. return;
  332. }
  333. g_dwDlgHeight = rcDialog.bottom - rcDialog.top;
  334. g_hListDetails = GetDlgItem(hDlg, IDC_LIST_DETAILS);
  335. ASSERTION (g_hListDetails);
  336. if(!GetWindowRect(g_hListDetails, &rcList))
  337. {
  338. dwRes = GetLastError();
  339. CALL_FAIL (WINDOW_ERR, TEXT ("GetWindowRect"), dwRes);
  340. return;
  341. }
  342. g_dwHeightDelta = rcDialog.bottom - rcList.top;
  343. //
  344. // Shrink down to small size (initially)
  345. //
  346. OnDetailsButton(hDlg, g_bDetails);
  347. //
  348. // Init the list view
  349. //
  350. RefreshImageList ();
  351. //
  352. // Add time column
  353. //
  354. TCHAR tszHeader[MAX_PATH];
  355. LVCOLUMN lvColumn = {0};
  356. lvColumn.mask = LVCF_TEXT | LVCF_WIDTH;
  357. lvColumn.cx = DETAILS_TIME_COLUMN_WIDTH;
  358. lvColumn.pszText = tszHeader;
  359. if (ERROR_SUCCESS != (dwRes = LoadAndFormatString (IDS_DETAIL_TIME_HEADER, tszHeader, ARR_SIZE(tszHeader))))
  360. {
  361. return;
  362. }
  363. ListView_InsertColumn(g_hListDetails, 0, &lvColumn);
  364. //
  365. // add event column
  366. //
  367. if (ERROR_SUCCESS != (dwRes = LoadAndFormatString (IDS_DETAIL_EVENT_HEADER, tszHeader, ARR_SIZE(tszHeader))))
  368. {
  369. return;
  370. }
  371. ListView_InsertColumn(g_hListDetails, 1, &lvColumn);
  372. //
  373. // Autosize the last column width
  374. //
  375. ListView_SetColumnWidth(g_hListDetails, 1, LVSCW_AUTOSIZE_USEHEADER);
  376. //
  377. // Animation control
  378. //
  379. g_hAnimation = GetDlgItem(hDlg, IDC_ANIMATE);
  380. ASSERTION (g_hAnimation);
  381. //
  382. // Get static text controls
  383. //
  384. g_hStatus = GetDlgItem(hDlg, IDC_STATUS);
  385. ASSERTION (g_hStatus);
  386. g_hElapsedTime = GetDlgItem(hDlg, IDC_ELAPSED_TIME);
  387. ASSERTION (g_hElapsedTime);
  388. g_hToFrom = GetDlgItem(hDlg, IDC_MON_TITLE);
  389. ASSERTION (g_hToFrom);
  390. //
  391. // Disconnect button
  392. //
  393. g_hDisconnect = GetDlgItem(hDlg, IDC_DISCONNECT);
  394. ASSERTION (g_hDisconnect);
  395. //
  396. // Get the time separator string
  397. //
  398. if(!GetLocaleInfo(LOCALE_USER_DEFAULT,
  399. LOCALE_STIME,
  400. g_tszTimeSeparator,
  401. ARR_SIZE(g_tszTimeSeparator) - 1))
  402. {
  403. dwRes = GetLastError();
  404. CALL_FAIL (WINDOW_ERR, TEXT ("GetLocaleInfo(LOCALE_STIME)"), dwRes);
  405. }
  406. if(g_ptPosition.x != -1 && g_ptPosition.y != -1)
  407. {
  408. SetWindowPos(hDlg, 0, g_ptPosition.x, g_ptPosition.y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
  409. }
  410. UpdateMonitorData(hDlg);
  411. } // InitMonitorDlg
  412. DWORD
  413. UpdateMonitorData(
  414. HWND hDlg
  415. )
  416. /*++
  417. Routine description:
  418. Update monitor data and controls
  419. Arguments:
  420. hDlg [in] - fax monitor dialog handle
  421. Return Value:
  422. standard error code
  423. --*/
  424. {
  425. DWORD dwRes = ERROR_SUCCESS;
  426. DBG_ENTER(TEXT("UpdateMonitorData"), dwRes);
  427. if(!hDlg || !g_hStatus || !g_hElapsedTime || !g_hToFrom || !g_hListDetails || !g_hDisconnect)
  428. {
  429. return dwRes;
  430. }
  431. //
  432. // elapsed time
  433. //
  434. if(FAX_IDLE == g_devState)
  435. {
  436. if(!SetWindowText(g_hElapsedTime, TEXT("")))
  437. {
  438. dwRes = GetLastError();
  439. CALL_FAIL (WINDOW_ERR, TEXT ("SetWindowText"), dwRes);
  440. }
  441. }
  442. //
  443. // Disconnect/Answer button
  444. //
  445. BOOL bButtonEnable = FALSE;
  446. DWORD dwButtonTitleID = IDS_BUTTON_DISCONNECT;
  447. TCHAR tszButtonTitle[MAX_PATH] = {0};
  448. g_bAnswerNow = FALSE;
  449. if (ERROR_SUCCESS == CheckAnswerNowCapability (FALSE, // Don't force service to be up
  450. NULL)) // Don't care about device id
  451. {
  452. //
  453. // Answer Now option is valid
  454. //
  455. g_bAnswerNow = TRUE;
  456. bButtonEnable = TRUE;
  457. dwButtonTitleID = IDS_BUTTON_ANSWER;
  458. }
  459. else if((FAX_SENDING == g_devState ||
  460. FAX_RECEIVING == g_devState)
  461. &&
  462. (IsUserGrantedAccess(FAX_ACCESS_SUBMIT) ||
  463. IsUserGrantedAccess(FAX_ACCESS_SUBMIT_NORMAL) ||
  464. IsUserGrantedAccess(FAX_ACCESS_SUBMIT_HIGH) ||
  465. IsUserGrantedAccess(FAX_ACCESS_MANAGE_JOBS)))
  466. {
  467. //
  468. // Fax in progress
  469. //
  470. bButtonEnable = TRUE;
  471. dwButtonTitleID = IDS_BUTTON_DISCONNECT;
  472. }
  473. EnableWindow(g_hDisconnect, bButtonEnable);
  474. if (ERROR_SUCCESS == LoadAndFormatString (dwButtonTitleID, tszButtonTitle, ARR_SIZE(tszButtonTitle)))
  475. {
  476. SetWindowText(g_hDisconnect, tszButtonTitle);
  477. }
  478. else
  479. {
  480. ASSERTION_FAILURE;
  481. }
  482. //
  483. // Animation
  484. //
  485. DWORD dwAnimationId = IDR_FAX_IDLE;
  486. switch(g_devState)
  487. {
  488. case FAX_IDLE:
  489. dwAnimationId = IDR_FAX_IDLE;
  490. break;
  491. case FAX_RINGING:
  492. dwAnimationId = IDR_FAX_RINGING;
  493. break;
  494. case FAX_SENDING:
  495. dwAnimationId = IDR_FAX_SEND;
  496. break;
  497. case FAX_RECEIVING:
  498. dwAnimationId = IDR_FAX_RECEIVE;
  499. break;
  500. }
  501. if(g_dwCurrentAnimationId != dwAnimationId)
  502. {
  503. if(!Animate_OpenEx(g_hAnimation, g_hModule, MAKEINTRESOURCE(dwAnimationId)))
  504. {
  505. CALL_FAIL (WINDOW_ERR, TEXT ("Animate_Open"), 0);
  506. }
  507. else
  508. {
  509. if(!Animate_Play(g_hAnimation, 0, -1, -1))
  510. {
  511. CALL_FAIL (WINDOW_ERR, TEXT ("Animate_Play"), 0);
  512. }
  513. else
  514. {
  515. g_dwCurrentAnimationId = dwAnimationId;
  516. }
  517. }
  518. }
  519. //
  520. // Status
  521. //
  522. if(FAX_IDLE != g_devState) // Non-idle state and
  523. {
  524. if(!SetWindowText(g_hStatus, g_tszLastEvent))
  525. {
  526. dwRes = GetLastError();
  527. CALL_FAIL (WINDOW_ERR, TEXT ("SetWindowText"), dwRes);
  528. }
  529. }
  530. else // idle
  531. {
  532. DWORD dwStrId = IDS_FAX_READY;
  533. TCHAR tszReady[MAX_PATH];
  534. if(g_ConfigOptions.bSend &&
  535. (g_ConfigOptions.bReceive || g_ConfigOptions.dwManualAnswerDeviceId == g_ConfigOptions.dwMonitorDeviceId))
  536. {
  537. dwStrId = IDS_READY_TO_SND_AND_RCV;
  538. }
  539. else if(g_ConfigOptions.bSend)
  540. {
  541. dwStrId = IDS_READY_TO_SND;
  542. }
  543. else if(g_ConfigOptions.bReceive || g_ConfigOptions.dwManualAnswerDeviceId == g_ConfigOptions.dwMonitorDeviceId)
  544. {
  545. dwStrId = IDS_READY_TO_RCV;
  546. }
  547. if (ERROR_SUCCESS != (dwRes = LoadAndFormatString (dwStrId, tszReady, ARR_SIZE(tszReady))))
  548. {
  549. return dwRes;
  550. }
  551. if(!SetWindowText(g_hStatus, tszReady))
  552. {
  553. dwRes = GetLastError();
  554. CALL_FAIL (WINDOW_ERR, TEXT ("SetWindowText"), dwRes);
  555. }
  556. }
  557. //
  558. // to/from
  559. //
  560. TCHAR tszToFrom[MAX_PATH] = {0};
  561. if(FAX_SENDING == g_devState || FAX_RECEIVING == g_devState)
  562. {
  563. LPCTSTR lpctstrAddressParam = NULL;
  564. DWORD dwStringResId = (FAX_SENDING == g_devState) ? IDS_SENDING : IDS_RECEIVING;
  565. if(_tcslen(g_szRemoteId))
  566. {
  567. //
  568. // Remote ID is known
  569. //
  570. lpctstrAddressParam = g_szRemoteId;
  571. dwStringResId = (FAX_SENDING == g_devState) ? IDS_SENDING_TO : IDS_RECEIVING_FROM;
  572. }
  573. if (ERROR_SUCCESS != (dwRes = LoadAndFormatString (dwStringResId,
  574. tszToFrom,
  575. ARR_SIZE(tszToFrom),
  576. lpctstrAddressParam)))
  577. {
  578. return dwRes;
  579. }
  580. }
  581. if(!SetWindowText(g_hToFrom, tszToFrom))
  582. {
  583. dwRes = GetLastError();
  584. CALL_FAIL (WINDOW_ERR, TEXT ("SetWindowText"), dwRes);
  585. }
  586. //
  587. // Details log list
  588. //
  589. if(ListView_GetItemCount(g_hListDetails) == 0)
  590. {
  591. //
  592. // Log is empty - fill it with list data
  593. //
  594. ASSERTION (g_lstEvents.size() <= MAX_EVENT_LIST_SIZE);
  595. for (EVENTS_LIST::iterator it = g_lstEvents.begin(); it != g_lstEvents.end(); ++it)
  596. {
  597. EVENT_ENTRY &Event = *it;
  598. AddEventToView(&Event);
  599. }
  600. }
  601. if(!CheckDlgButton(hDlg, IDC_ALWAYS_ON_TOP, g_bTopMost ? BST_CHECKED : BST_UNCHECKED))
  602. {
  603. dwRes = GetLastError();
  604. CALL_FAIL (WINDOW_ERR, TEXT ("CheckDlgButton(IDC_ALWAYS_ON_TOP)"), dwRes);
  605. }
  606. OnAlwaysOnTop(hDlg);
  607. return dwRes;
  608. } // UpdateMonitorData
  609. void
  610. OnDetailsButton(
  611. HWND hDlg,
  612. BOOL bDetails
  613. )
  614. /*++
  615. Routine description:
  616. Show/Hide event log and change the details button text
  617. according to bDetails value
  618. Arguments:
  619. hDlg [in] - fax monitor dialog handle
  620. bDetails [in] - new details state
  621. Return Value:
  622. none
  623. --*/
  624. {
  625. DBG_ENTER(TEXT("OnDetailsButton"));
  626. if(!hDlg)
  627. {
  628. ASSERTION (FALSE);
  629. return;
  630. }
  631. //
  632. // Show/Hide the event log
  633. //
  634. RECT rc;
  635. GetWindowRect(hDlg, &rc);
  636. BOOL bLogOpened = (rc.bottom - rc.top > g_dwDlgHeight - g_dwHeightDelta/2);
  637. //
  638. // If the current dialog heigh more then
  639. // dlialog heigh with open log minus half log heigh
  640. // we suppose that the log is opened.
  641. // This done due to different dialog size in the high contrast mode.
  642. //
  643. if(bLogOpened != bDetails)
  644. {
  645. //
  646. // Current log state does not fit the new state
  647. //
  648. rc.bottom += g_dwHeightDelta * (bDetails ? 1 : -1);
  649. MoveWindow(hDlg, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
  650. }
  651. //
  652. // Set More/Less button text
  653. //
  654. TCHAR tszButtonText[MAX_PATH];
  655. if (ERROR_SUCCESS != LoadAndFormatString (bDetails ? IDS_BUTTON_LESS : IDS_BUTTON_MORE,
  656. tszButtonText,
  657. ARR_SIZE(tszButtonText)))
  658. {
  659. return;
  660. }
  661. if(!SetDlgItemText(hDlg, IDC_DETAILS, tszButtonText))
  662. {
  663. CALL_FAIL (WINDOW_ERR, TEXT ("SetDlgItemText"), GetLastError());
  664. }
  665. } // OnDetailsButton
  666. void
  667. OnAlwaysOnTop(
  668. HWND hDlg
  669. )
  670. /*++
  671. Routine description:
  672. Change monitor "on top" state and save it to the registry
  673. Arguments:
  674. hDlg [in] - fax monitor dialog handle
  675. Return Value:
  676. none
  677. --*/
  678. {
  679. DBG_ENTER(TEXT("OnAlwaysOnTop"));
  680. if(!hDlg)
  681. {
  682. ASSERTION (FALSE);
  683. return;
  684. }
  685. g_bTopMost = (IsDlgButtonChecked(hDlg, IDC_ALWAYS_ON_TOP) == BST_CHECKED) ? 1:0;
  686. DWORD dwRes;
  687. if(!SetWindowPos(hDlg,
  688. g_bTopMost ? HWND_TOPMOST : HWND_NOTOPMOST,
  689. 0,
  690. 0,
  691. 0,
  692. 0,
  693. SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE))
  694. {
  695. dwRes = GetLastError();
  696. CALL_FAIL (WINDOW_ERR, TEXT ("SetWindowPos"), dwRes);
  697. }
  698. HKEY hKey;
  699. dwRes = RegOpenKeyEx(HKEY_CURRENT_USER, REGKEY_FAX_USERINFO, 0, KEY_WRITE, &hKey);
  700. if (ERROR_SUCCESS == dwRes)
  701. {
  702. dwRes = RegSetValueEx(hKey, REGVAL_ALWAYS_ON_TOP, 0, REG_DWORD, (CONST BYTE*)&g_bTopMost, sizeof(g_bTopMost));
  703. if(ERROR_SUCCESS != dwRes)
  704. {
  705. CALL_FAIL (WINDOW_ERR, TEXT ("RegSetValueEx(REGVAL_ALWAYS_ON_TOP)"), dwRes);
  706. }
  707. RegCloseKey( hKey );
  708. }
  709. else
  710. {
  711. CALL_FAIL (WINDOW_ERR, TEXT ("RegOpenKeyEx"), dwRes);
  712. }
  713. } // OnAlwaysOnTop
  714. void
  715. SetStatusMonitorDeviceState(
  716. DeviceState devState
  717. )
  718. /*++
  719. Routine description:
  720. Change device state
  721. Start/stop elapsed timer
  722. Arguments:
  723. devState - [in] device state
  724. Return Value:
  725. none
  726. --*/
  727. {
  728. DWORD dwRes = ERROR_SUCCESS;
  729. DBG_ENTER(TEXT("SetStatusMonitorDeviceState"), dwRes);
  730. if(g_devState != devState)
  731. {
  732. //
  733. // State has changed
  734. //
  735. if(g_nElapsedTimerId)
  736. {
  737. //
  738. // Old timer exists
  739. //
  740. if(!KillTimer(NULL, g_nElapsedTimerId))
  741. {
  742. dwRes = GetLastError();
  743. CALL_FAIL (WINDOW_ERR, TEXT ("KillTimer"), dwRes);
  744. }
  745. g_nElapsedTimerId = 0;
  746. }
  747. }
  748. if(!g_nElapsedTimerId && (devState == FAX_SENDING || devState == FAX_RECEIVING))
  749. {
  750. //
  751. // We need to count elapsed time for send / receive states.
  752. //
  753. g_dwStartTime = GetTickCount();
  754. g_nElapsedTimerId = SetTimer(NULL, 0, DURATION_TIMER_RESOLUTION, ElapsedTimerProc);
  755. if(!g_nElapsedTimerId)
  756. {
  757. dwRes = GetLastError();
  758. CALL_FAIL (WINDOW_ERR, TEXT ("SetTimer"), dwRes);
  759. }
  760. }
  761. g_devState = devState;
  762. UpdateMonitorData(g_hMonitorDlg);
  763. } // SetStatusMonitorDeviceState
  764. VOID
  765. CALLBACK
  766. ElapsedTimerProc(
  767. HWND hwnd, // handle to window
  768. UINT uMsg, // WM_TIMER message
  769. UINT_PTR idEvent, // timer identifier
  770. DWORD dwTime // current system time
  771. )
  772. /*++
  773. Routine description:
  774. Timer precedure to update elapsed time value
  775. Arguments:
  776. HWND hwnd, // handle to window
  777. UINT uMsg, // WM_TIMER message
  778. UINT_PTR idEvent, // timer identifier
  779. DWORD dwTime // current system time
  780. Return Value:
  781. none
  782. --*/
  783. {
  784. DBG_ENTER(TEXT("ElapsedTimerProc"));
  785. if(!g_hElapsedTime)
  786. {
  787. return;
  788. }
  789. TCHAR tszTime[MAX_PATH] = {0};
  790. TCHAR tszTimeFormat[MAX_PATH] = {0};
  791. DWORD dwRes;
  792. if (ERROR_SUCCESS != (dwRes = LoadAndFormatString (IDS_ELAPSED_TIME, tszTimeFormat, ARR_SIZE(tszTimeFormat))))
  793. {
  794. return;
  795. }
  796. DWORD dwElapsedTime = (GetTickCount() - g_dwStartTime)/1000;
  797. _sntprintf(tszTime,
  798. ARR_SIZE(tszTime) - 1,
  799. tszTimeFormat,
  800. dwElapsedTime/60,
  801. g_tszTimeSeparator,
  802. dwElapsedTime%60);
  803. if(!SetWindowText(g_hElapsedTime, tszTime))
  804. {
  805. dwRes = GetLastError();
  806. CALL_FAIL (WINDOW_ERR, TEXT ("SetWindowText"), dwRes);
  807. }
  808. } // ElapsedTimerProc
  809. DWORD
  810. LoadAndFormatString (
  811. IN DWORD dwStringResourceId,
  812. OUT LPTSTR lptstrFormattedString,
  813. IN DWORD dwOutStrSize,
  814. IN LPCTSTR lpctstrAdditionalParam /* = NULL */
  815. )
  816. /*++
  817. Routine name : LoadAndFormatString
  818. Routine description:
  819. Loads a string from the resource and optionally formats it with another string
  820. Author:
  821. Eran Yariv (EranY), Dec, 2000
  822. Arguments:
  823. dwStringResourceId [in] - String resource id
  824. lptstrFormattedString [out] - Result buffer. Must be at least MAX_PATH charactes long.
  825. dwOutStrSize [in] - size of lptstrFormattedString in TCHARs
  826. lpctstrAdditionalParam [in] - Optional string paramter.
  827. If non-NULL, this loaded strings is used as a format specifier (sprintf-like) to
  828. format this additional string.
  829. Return Value:
  830. Standard Win32 error code
  831. --*/
  832. {
  833. DWORD dwRes = ERROR_SUCCESS;
  834. DBG_ENTER(TEXT("LoadAndFormatString"),
  835. dwRes,
  836. TEXT("ResourceId=%d, Param=%s"),
  837. dwStringResourceId,
  838. lpctstrAdditionalParam);
  839. ASSERTION (lptstrFormattedString && dwStringResourceId);
  840. TCHAR tszString[MAX_PATH] = {0};
  841. if (!LoadString(g_hResource, dwStringResourceId, tszString, ARR_SIZE(tszString)-1))
  842. {
  843. dwRes = GetLastError();
  844. CALL_FAIL (RESOURCE_ERR, TEXT("LoadString"), dwRes);
  845. return dwRes;
  846. }
  847. if (lpctstrAdditionalParam)
  848. {
  849. _sntprintf(lptstrFormattedString,
  850. dwOutStrSize - 1,
  851. tszString,
  852. lpctstrAdditionalParam);
  853. lptstrFormattedString[dwOutStrSize -1] = _T('\0');
  854. }
  855. else
  856. {
  857. lstrcpyn (lptstrFormattedString, tszString, dwOutStrSize - 1);
  858. }
  859. return dwRes;
  860. } // LoadAndFormatString
  861. DWORD
  862. AddStatusMonitorLogEvent (
  863. IN eIconType eIcon,
  864. IN DWORD dwStringResourceId,
  865. IN LPCTSTR lpctstrAdditionalParam /* = NULL */,
  866. OUT LPTSTR lptstrFormattedEvent /* = NULL */,
  867. IN DWORD dwOutStrSize /* = 0 */
  868. )
  869. /*++
  870. Routine name : AddStatusMonitorLogEvent
  871. Routine description:
  872. Adds a status monitor event log line
  873. Author:
  874. Eran Yariv (EranY), Dec, 2000
  875. Arguments:
  876. eIcon [in] - Icon to display in log entry
  877. dwStringResourceId [in] - String resource id to use
  878. lpctstrAdditionalParam [in] - Optional string. If non-NULL, the string loaded from dwStringResourceId
  879. is used to format the additional parameter.
  880. lptstrFormattedEvent [out] - Optional, if non-NULL, points to a buffer to receive the final status string.
  881. Buffer must be at least MAX_PATH characters long.
  882. dwOutStrSize [in] - Optional size of lptstrFormattedEvent in TCHARs
  883. Return Value:
  884. Standard Win32 error code
  885. --*/
  886. {
  887. DWORD dwRes = ERROR_SUCCESS;
  888. DBG_ENTER(TEXT("AddStatusMonitorLogEvent"),
  889. dwRes,
  890. TEXT("Icon=%d, ResourceId=%d, Param=%s"),
  891. eIcon,
  892. dwStringResourceId,
  893. lpctstrAdditionalParam);
  894. TCHAR tszStatus[MAX_PATH * 2] = {0};
  895. dwRes = LoadAndFormatString (dwStringResourceId, tszStatus, ARR_SIZE(tszStatus), lpctstrAdditionalParam);
  896. if (ERROR_SUCCESS != dwRes)
  897. {
  898. return dwRes;
  899. }
  900. if (lptstrFormattedEvent)
  901. {
  902. lstrcpyn (lptstrFormattedEvent, tszStatus, dwOutStrSize - 1);
  903. }
  904. dwRes = AddStatusMonitorLogEvent (eIcon, tszStatus);
  905. return dwRes;
  906. } // AddStatusMonitorLogEvent
  907. DWORD
  908. AddStatusMonitorLogEvent (
  909. eIconType eIcon,
  910. LPCTSTR lpctstrString
  911. )
  912. /*++
  913. Routine description:
  914. Add new event to the event list
  915. Arguments:
  916. eIcon - [in] icon index
  917. lpctstrString - [in] event description
  918. Return Value:
  919. standard error code
  920. --*/
  921. {
  922. DWORD dwRes = ERROR_SUCCESS;
  923. DBG_ENTER(TEXT("AddStatusMonitorLogEvent"),
  924. dwRes,
  925. TEXT("Icon=%d, Status=%s"),
  926. eIcon,
  927. lpctstrString);
  928. TCHAR tszTime [MAX_PATH] = {0};
  929. ASSERTION (lpctstrString);
  930. static TCHAR tszRinging[MAX_PATH] = {0};
  931. if(_tcslen(tszRinging) == 0)
  932. {
  933. if (ERROR_SUCCESS != (dwRes = LoadAndFormatString (IDS_RINGING, tszRinging, ARR_SIZE(tszRinging))))
  934. {
  935. ASSERTION_FAILURE;
  936. return dwRes;
  937. }
  938. }
  939. if(_tcscmp(lpctstrString, g_tszLastEvent) == 0 &&
  940. _tcscmp(lpctstrString, tszRinging) != 0)
  941. {
  942. //
  943. // Do not display the same string twice
  944. // except "Ringing"
  945. //
  946. return dwRes;
  947. }
  948. EVENT_ENTRY Event;
  949. Event.eIcon = eIcon;
  950. SYSTEMTIME sysTime;
  951. GetLocalTime(&sysTime);
  952. if(!FaxTimeFormat(LOCALE_USER_DEFAULT, 0, &sysTime, NULL, Event.tszTime, ARR_SIZE(Event.tszTime) - 1))
  953. {
  954. dwRes = GetLastError();
  955. CALL_FAIL (WINDOW_ERR, TEXT ("FaxTimeFormat"), dwRes);
  956. return dwRes;
  957. }
  958. lstrcpyn (Event.tszEvent, lpctstrString, ARR_SIZE(Event.tszEvent) - 1);
  959. lstrcpyn (g_tszLastEvent, lpctstrString, ARR_SIZE(g_tszLastEvent) - 1);
  960. try
  961. {
  962. g_lstEvents.push_back (Event);
  963. if (g_lstEvents.size() > MAX_EVENT_LIST_SIZE)
  964. {
  965. //
  966. // We exceeded the maximal size we permit - remove the most ancient entry
  967. //
  968. g_lstEvents.pop_front ();
  969. }
  970. }
  971. catch (exception &ex)
  972. {
  973. VERBOSE (MEM_ERR,
  974. TEXT("Got an STL exception while handling with event list (%S)"),
  975. ex.what());
  976. return ERROR_NOT_ENOUGH_MEMORY;
  977. }
  978. AddEventToView(&Event);
  979. dwRes = UpdateMonitorData(g_hMonitorDlg);
  980. return dwRes;
  981. } // AddStatusMonitorLogEvent
  982. void
  983. AddEventToView(
  984. PEVENT_ENTRY pEvent
  985. )
  986. /*++
  987. Routine description:
  988. Add event to the list view
  989. Arguments:
  990. pEvent - event data
  991. Return Value:
  992. none
  993. --*/
  994. {
  995. DBG_ENTER(TEXT("AddEventToView"));
  996. ASSERTION (pEvent);
  997. if(!g_hListDetails)
  998. {
  999. return;
  1000. }
  1001. LV_ITEM lvi = {0};
  1002. DWORD dwItem;
  1003. lvi.pszText = pEvent->tszTime ? pEvent->tszTime : TEXT("");
  1004. lvi.iItem = ListView_GetItemCount( g_hListDetails );
  1005. lvi.iSubItem = 0;
  1006. lvi.mask = LVIF_TEXT | LVIF_IMAGE;
  1007. lvi.iImage = pEvent->eIcon;
  1008. dwItem = ListView_InsertItem( g_hListDetails, &lvi );
  1009. lvi.pszText = pEvent->tszEvent ? pEvent->tszEvent : TEXT("");
  1010. lvi.iItem = dwItem;
  1011. lvi.iSubItem = 1;
  1012. lvi.mask = LVIF_TEXT;
  1013. ListView_SetItem( g_hListDetails, &lvi );
  1014. ListView_EnsureVisible(g_hListDetails, dwItem, FALSE);
  1015. if(ListView_GetItemCount(g_hListDetails) > MAX_EVENT_LIST_SIZE)
  1016. {
  1017. ListView_DeleteItem(g_hListDetails, 0);
  1018. }
  1019. //
  1020. // Autosize the last column to get rid of unnecessary horizontal scroll bar
  1021. //
  1022. ListView_SetColumnWidth(g_hListDetails, 1, LVSCW_AUTOSIZE_USEHEADER);
  1023. } // AddEventToView
  1024. DWORD
  1025. OpenFaxMonitor(VOID)
  1026. /*++
  1027. Routine description:
  1028. Opens fax monitor dialog
  1029. Arguments:
  1030. none
  1031. Return Value:
  1032. Standard error code.
  1033. --*/
  1034. {
  1035. DWORD dwRes = ERROR_SUCCESS;
  1036. DBG_ENTER(TEXT("OpenFaxMonitor"), dwRes);
  1037. if(!g_hMonitorDlg)
  1038. {
  1039. //
  1040. // Read 'top most' value
  1041. //
  1042. HKEY hKey;
  1043. dwRes = RegOpenKeyEx(HKEY_CURRENT_USER, REGKEY_FAX_USERINFO, 0, KEY_READ, &hKey);
  1044. if (ERROR_SUCCESS == dwRes)
  1045. {
  1046. g_bTopMost = GetRegistryDword(hKey, REGVAL_ALWAYS_ON_TOP);
  1047. RegCloseKey( hKey );
  1048. }
  1049. else
  1050. {
  1051. CALL_FAIL (WINDOW_ERR, TEXT ("RegOpenKeyEx"), dwRes);
  1052. }
  1053. //
  1054. // Create the dialog
  1055. //
  1056. g_hMonitorDlg = CreateDialogParam(g_hResource,
  1057. MAKEINTRESOURCE(IDD_MONITOR),
  1058. NULL,
  1059. FaxMonitorDlgProc,
  1060. NULL);
  1061. if(!g_hMonitorDlg)
  1062. {
  1063. dwRes = GetLastError();
  1064. CALL_FAIL (WINDOW_ERR, TEXT ("CreateDialogParam"), dwRes);
  1065. return dwRes;
  1066. }
  1067. }
  1068. //
  1069. // Set the focus on the dialog and make it the top window
  1070. //
  1071. SetFocus(g_hMonitorDlg);
  1072. SetActiveWindow(g_hMonitorDlg);
  1073. SetWindowPos(g_hMonitorDlg,
  1074. HWND_TOPMOST,
  1075. 0,
  1076. 0,
  1077. 0,
  1078. 0,
  1079. SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
  1080. if (!g_bTopMost)
  1081. {
  1082. SetWindowPos(g_hMonitorDlg,
  1083. HWND_NOTOPMOST,
  1084. 0,
  1085. 0,
  1086. 0,
  1087. 0,
  1088. SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
  1089. }
  1090. return dwRes;
  1091. } // OpenFaxMonitor
  1092. void
  1093. OnDisconnect()
  1094. /*++
  1095. Routine description:
  1096. Abort current transmission
  1097. OR
  1098. Answer a call
  1099. Return Value:
  1100. none
  1101. --*/
  1102. {
  1103. DWORD dwRes = ERROR_SUCCESS;
  1104. DBG_ENTER(TEXT("OnDisconnect"), dwRes);
  1105. if(g_bAnswerNow)
  1106. {
  1107. //
  1108. // The button shows 'Answer Now'
  1109. //
  1110. AnswerTheCall();
  1111. return;
  1112. }
  1113. //
  1114. // Else, the button shows 'Disconnect'
  1115. //
  1116. if(!g_dwCurrentJobID)
  1117. {
  1118. //
  1119. // No job - nothing to disconnect
  1120. //
  1121. SetStatusMonitorDeviceState(FAX_IDLE);
  1122. return;
  1123. }
  1124. DWORD dwMsgId = (FAX_SENDING == g_devState) ? IDS_ABORT_SEND_CONFIRM : IDS_ABORT_RECEIVE_CONFIRM;
  1125. if(IDYES != FaxMessageBox(g_hMonitorDlg, dwMsgId, MB_YESNO | MB_DEFBUTTON2 | MB_ICONQUESTION))
  1126. {
  1127. return;
  1128. }
  1129. if(!Connect())
  1130. {
  1131. dwRes = GetLastError();
  1132. CALL_FAIL (RPC_ERR, TEXT ("Connect"), dwRes);
  1133. return;
  1134. }
  1135. FAX_JOB_ENTRY fje = {0};
  1136. fje.SizeOfStruct = sizeof(FAX_JOB_ENTRY);
  1137. if(g_hDisconnect)
  1138. {
  1139. EnableWindow(g_hDisconnect, FALSE);
  1140. }
  1141. if (!FaxSetJob (g_hFaxSvcHandle, g_dwCurrentJobID, JC_DELETE, &fje))
  1142. {
  1143. dwRes = GetLastError();
  1144. CALL_FAIL (RPC_ERR, TEXT ("FaxSetJob"), dwRes);
  1145. if(g_hDisconnect)
  1146. {
  1147. EnableWindow(g_hDisconnect, TRUE);
  1148. }
  1149. if(ERROR_ACCESS_DENIED == dwRes)
  1150. {
  1151. FaxMessageBox(g_hMonitorDlg, IDS_DELETE_ACCESS_DENIED, MB_OK | MB_ICONSTOP);
  1152. }
  1153. }
  1154. } // OnDisconnect
  1155. void
  1156. OnClearLog()
  1157. /*++
  1158. Routine description:
  1159. Clear the monitor event log
  1160. Return Value:
  1161. none
  1162. --*/
  1163. {
  1164. DBG_ENTER(TEXT("OnClearLog"));
  1165. ASSERTION (g_hListDetails);
  1166. try
  1167. {
  1168. g_lstEvents.clear();
  1169. }
  1170. catch (exception &ex)
  1171. {
  1172. VERBOSE (MEM_ERR,
  1173. TEXT("Got an STL exception while clearing the events list (%S)"),
  1174. ex.what());
  1175. }
  1176. if(!ListView_DeleteAllItems(g_hListDetails))
  1177. {
  1178. CALL_FAIL (WINDOW_ERR, TEXT ("ListView_DeleteAllItems"), 0);
  1179. }
  1180. } // OnClearLog
  1181. int
  1182. FaxMessageBox(
  1183. HWND hWnd,
  1184. DWORD dwTextID,
  1185. UINT uType
  1186. )
  1187. /*++
  1188. Routine description:
  1189. Open standard message box
  1190. Arguments:
  1191. hWnd - handle to owner window
  1192. dwTextID - text resource ID in message box
  1193. uType - message box style
  1194. Return Value:
  1195. MessageBox() return value
  1196. --*/
  1197. {
  1198. int iRes;
  1199. DBG_ENTER(TEXT("FaxMessageBox"), iRes);
  1200. TCHAR tsCaption[MAX_PATH];
  1201. TCHAR tsText[MAX_PATH];
  1202. DWORD dwRes;
  1203. if (ERROR_SUCCESS != (dwRes = LoadAndFormatString (IDS_MESSAGE_BOX_CAPTION, tsCaption, ARR_SIZE(tsCaption))))
  1204. {
  1205. SetLastError (dwRes);
  1206. iRes = 0;
  1207. return iRes;
  1208. }
  1209. if (ERROR_SUCCESS != (dwRes = LoadAndFormatString (dwTextID, tsText, ARR_SIZE(tsText))))
  1210. {
  1211. SetLastError (dwRes);
  1212. iRes = 0;
  1213. return iRes;
  1214. }
  1215. iRes = AlignedMessageBox(hWnd, tsText, tsCaption, uType);
  1216. return iRes;
  1217. } // FaxMessageBox