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.

3255 lines
90 KiB

  1. /**
  2. Copyright (c) Microsoft Corporation 1999-2000
  3. Module Name:
  4. fxsst.cpp
  5. Abstract:
  6. This module implements the tray icon for fax.
  7. The purpose of the tray icon is to provide
  8. status and feedback to the fax user.
  9. **/
  10. #include <windows.h>
  11. #include <faxreg.h>
  12. #include <fxsapip.h>
  13. #include <faxutil.h>
  14. #include <shellapi.h>
  15. #include <winspool.h>
  16. #include <shlobj.h>
  17. #include <Mmsystem.h>
  18. #include <tchar.h>
  19. #include <DebugEx.h>
  20. #include <FaxRes.h>
  21. #include "monitor.h"
  22. #include "resource.h"
  23. ////////////////////////////////////////////////////////////
  24. // Global data
  25. //
  26. //
  27. // The following message ids are used for internal custom messages.
  28. //
  29. #define WM_FAX_STARTED (WM_USER + 204) // Message indicating the loca fax service is up and running
  30. #define WM_TRAYCALLBACK (WM_USER + 205) // Notification bar icon callback message
  31. #define WM_FAX_EVENT (WM_USER + 300) // Fax extended event message
  32. #define TRAY_ICON_ID 12345 // Unique enough
  33. HINSTANCE g_hModule = NULL; // DLL Global instance
  34. HINSTANCE g_hResource = NULL; // Resource DLL handle
  35. HANDLE g_hFaxSvcHandle = NULL; // Handle to the fax service (from FaxConnectFaxServer)
  36. DWORDLONG g_dwlCurrentMsgID = 0; // ID of current message being monitored
  37. DWORD g_dwCurrentJobID = 0; // ID of current queue job being monitored
  38. HANDLE g_hServerStartupThread = NULL; // Handle of thread which waits for the server startup event
  39. HANDLE g_hStopStartupThreadEvent = NULL; // Event for stop Server Startup Thread
  40. BOOL g_bShuttingDown = FALSE; // Are we shutting down now?
  41. HWND g_hWndFaxNotify = NULL; // Local (hidden) window handle
  42. HANDLE g_hNotification = NULL; // Fax extended notification handle
  43. HCALL g_hCall = NULL; // Handle to call (from FAX_EVENT_TYPE_NEW_CALL)
  44. DWORDLONG g_dwlNewMsgId; // ID of the last incoming fax
  45. DWORDLONG g_dwlSendFailedMsgId; // ID of the last outgoing failed fax
  46. DWORDLONG g_dwlSendSuccessMsgId; // ID of the last successfully sent fax
  47. TCHAR g_szAddress[MAX_PATH] = {0}; // Current caller ID or recipient number
  48. TCHAR g_szRemoteId[MAX_PATH] = {0}; // Sender ID or Recipient ID
  49. //
  50. // Sender ID (receive):
  51. // TSID or
  52. // Caller ID or
  53. // "unknown caller"
  54. //
  55. // Recipient ID (send):
  56. // Recipient name or
  57. // CSID or
  58. // Recipient phone number.
  59. //
  60. BOOL g_bRecipientNameValid = FALSE; // TRUE if the g_szRecipientName has valid data
  61. TCHAR g_szRecipientName[MAX_PATH] = {0}; // Keep the recipient name during sending
  62. //
  63. // Configuration options - read from the registry / Service
  64. // Default values are set here.
  65. //
  66. CONFIG_OPTIONS g_ConfigOptions = {0};
  67. //
  68. // Notification bar icon states
  69. //
  70. typedef
  71. enum
  72. {
  73. ICON_RINGING=0, // Device is ringing
  74. ICON_SENDING, // Device is sending
  75. ICON_RECEIVING, // Device is receiving
  76. ICON_SEND_FAILED, // Send operation failed
  77. ICON_RECEIVE_FAILED, // Receive operation failed
  78. ICON_NEW_FAX, // New unread fax
  79. ICON_SEND_SUCCESS, // Send was successful
  80. ICON_IDLE, // Don't display an icon
  81. ICONS_COUNT // Number of icons we support
  82. } eIconState;
  83. eIconState g_CurrentIcon = ICONS_COUNT; // The index of the currently displayed icon
  84. #define TOOLTIP_SIZE 128 // Number of characters in the tooltip
  85. struct SIconState
  86. {
  87. BOOL bEnable; // Is the state active? (e.g. are there any new unread faxes?)
  88. DWORD dwIconResId; // Resource id of the icon to use
  89. HICON hIcon; // Handle to icon to use
  90. LPCTSTR pctsSound; // Name of sound event
  91. TCHAR tszToolTip[TOOLTIP_SIZE]; // Text to display in icon tooltip
  92. DWORD dwBalloonTimeout; // Timeout of balloon (millisecs)
  93. DWORD dwBalloonIcon; // The icon to display in the balloon. (see NIIF_* constants)
  94. };
  95. //
  96. // Fax notification icon state array.
  97. // Several states may have the bEnable flag on.
  98. // The array is sorted by priority and EvaluateIcon() scans it looking
  99. // for the first active state.
  100. //
  101. SIconState g_Icons[ICONS_COUNT] =
  102. {
  103. {FALSE, IDI_RINGING_1, NULL, TEXT("FaxLineRings"), TEXT(""), 30000, NIIF_INFO}, // ICON_RINGING
  104. {FALSE, IDI_SENDING, NULL, TEXT(""), TEXT(""), 0, NIIF_INFO}, // ICON_SENDING
  105. {FALSE, IDI_RECEIVING, NULL, TEXT(""), TEXT(""), 0, NIIF_INFO}, // ICON_RECEIVING
  106. {FALSE, IDI_SEND_FAILED, NULL, TEXT("FaxError"), TEXT(""), 15000, NIIF_WARNING}, // ICON_SEND_FAILED
  107. {FALSE, IDI_RECEIVE_FAILED, NULL, TEXT("FaxError"), TEXT(""), 15000, NIIF_WARNING}, // ICON_RECEIVE_FAILED
  108. {FALSE, IDI_NEW_FAX, NULL, TEXT("FaxNew"), TEXT(""), 15000, NIIF_INFO}, // ICON_NEW_FAX
  109. {FALSE, IDI_SEND_SUCCESS, NULL, TEXT("FaxSent"), TEXT(""), 10000, NIIF_INFO}, // ICON_SEND_SUCCESS
  110. {FALSE, IDI_FAX_NORMAL, NULL, TEXT(""), TEXT(""), 0, NIIF_NONE} // ICON_IDLE
  111. };
  112. //
  113. // Icons array for ringing animation
  114. //
  115. struct SRingIcon
  116. {
  117. HICON hIcon; // Handle to loaded icon
  118. DWORD dwIconResId; // Resource ID of icon
  119. };
  120. #define RING_ICONS_NUM 4 // Number of frames (different icons) in ringing animation
  121. #define RING_ANIMATION_FRAME_DELAY 300 // Delay (millisecs) between ring animation frames
  122. #define RING_ANIMATION_TIMEOUT 10000 // Timeout (millisecs) of ring animation. When the timeout expires, the animation
  123. // stops and the icon becomes static.
  124. SRingIcon g_RingIcons[RING_ICONS_NUM] =
  125. {
  126. NULL, IDI_RINGING_1,
  127. NULL, IDI_RINGING_2,
  128. NULL, IDI_RINGING_3,
  129. NULL, IDI_RINGING_4
  130. };
  131. UINT_PTR g_uRingTimerID = 0; // Timer of ringing animation
  132. DWORD g_dwCurrRingIconIndex = 0; // Index of current frame (into g_RingIcons)
  133. DWORD g_dwRingAnimationStartTick; // Tick count (time) of animation start
  134. #define MAX_BALLOON_TEXT_LEN 256 // Max number of character in balloon text
  135. #define MAX_BALLOON_TITLE_LEN 64 // Max number of character in balloon title
  136. struct SBalloonInfo
  137. {
  138. BOOL bEnable; // This flag is set when there's a need to display some balloon.
  139. // EvaluateIcon() detects this bit, asks for a balloon and turns the bit off.
  140. BOOL bDelete; // This flag is set when there's a need to destroy some balloon.
  141. eIconState eState; // The current state of the icon
  142. TCHAR szInfo[MAX_BALLOON_TEXT_LEN]; // The text to display on the balloon
  143. TCHAR szInfoTitle[MAX_BALLOON_TITLE_LEN]; // The title to display on the balloon
  144. };
  145. BOOL g_bIconAdded = FALSE; // Do we have an icon on the status bar?
  146. SBalloonInfo g_BalloonInfo = {0}; // The current icon + ballon state
  147. struct EVENT_INFO
  148. {
  149. DWORD dwExtStatus; // Extended status code
  150. UINT uResourceId; // String for display
  151. eIconType eIcon;
  152. };
  153. static const EVENT_INFO g_StatusEx[] =
  154. {
  155. JS_EX_DISCONNECTED, IDS_FAX_DISCONNECTED, LIST_IMAGE_ERROR,
  156. JS_EX_INITIALIZING, IDS_FAX_INITIALIZING, LIST_IMAGE_NONE,
  157. JS_EX_DIALING, IDS_FAX_DIALING, LIST_IMAGE_NONE,
  158. JS_EX_TRANSMITTING, IDS_FAX_SENDING, LIST_IMAGE_NONE,
  159. JS_EX_ANSWERED, IDS_FAX_ANSWERED, LIST_IMAGE_NONE,
  160. JS_EX_RECEIVING, IDS_FAX_RECEIVING, LIST_IMAGE_NONE,
  161. JS_EX_LINE_UNAVAILABLE, IDS_FAX_LINE_UNAVAILABLE, LIST_IMAGE_ERROR,
  162. JS_EX_BUSY, IDS_FAX_BUSY, LIST_IMAGE_WARNING,
  163. JS_EX_NO_ANSWER, IDS_FAX_NO_ANSWER, LIST_IMAGE_WARNING,
  164. JS_EX_BAD_ADDRESS, IDS_FAX_BAD_ADDRESS, LIST_IMAGE_ERROR,
  165. JS_EX_NO_DIAL_TONE, IDS_FAX_NO_DIAL_TONE, LIST_IMAGE_ERROR,
  166. JS_EX_FATAL_ERROR, IDS_FAX_FATAL_ERROR_SND, LIST_IMAGE_ERROR,
  167. JS_EX_CALL_DELAYED, IDS_FAX_CALL_DELAYED, LIST_IMAGE_ERROR,
  168. JS_EX_CALL_BLACKLISTED, IDS_FAX_CALL_BLACKLISTED, LIST_IMAGE_ERROR,
  169. JS_EX_NOT_FAX_CALL, IDS_FAX_NOT_FAX_CALL, LIST_IMAGE_ERROR,
  170. JS_EX_PARTIALLY_RECEIVED, IDS_FAX_PARTIALLY_RECEIVED, LIST_IMAGE_WARNING,
  171. JS_EX_CALL_COMPLETED, IDS_FAX_CALL_COMPLETED, LIST_IMAGE_NONE,
  172. JS_EX_CALL_ABORTED, IDS_FAX_CALL_ABORTED, LIST_IMAGE_NONE,
  173. 0, 0, LIST_IMAGE_NONE
  174. };
  175. /////////////////////////////////////////////////////////////////////
  176. // Function prototypes
  177. //
  178. BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, void* lpReserved);
  179. void GetConfiguration();
  180. DWORD WaitForRestartThread(LPVOID ThreadData);
  181. VOID WaitForFaxRestart(HWND hWnd);
  182. LRESULT CALLBACK NotifyWndProc (HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
  183. BOOL Connect();
  184. BOOL RegisterForServerEvents();
  185. VOID OnFaxEvent(FAX_EVENT_EX *pEvent);
  186. VOID OnNewCall (const FAX_EVENT_NEW_CALL &NewCall);
  187. VOID StatusUpdate (PFAX_JOB_STATUS pStatus);
  188. BOOL GetStatusEx(PFAX_JOB_STATUS pStatus, eIconType* peIcon, TCHAR* ptsStatusEx, DWORD dwSize);
  189. BOOL IsUserGrantedAccess(DWORD);
  190. void EvaluateIcon();
  191. void SetIconState(eIconState eIcon, BOOL bEnable, TCHAR* ptsStatus = NULL);
  192. VOID AnswerTheCall();
  193. VOID InvokeClientConsole();
  194. VOID DoFaxContextMenu(HWND hwnd);
  195. VOID OnTrayCallback (HWND hwnd, WPARAM wp, LPARAM lp);
  196. VOID CALLBACK RingTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
  197. VOID OnDeviceRing(DWORD dwDeviceID);
  198. VOID InitGlobals ();
  199. VOID GetRemoteId(PFAX_JOB_STATUS pStatus);
  200. BOOL InitModule ();
  201. BOOL DestroyModule ();
  202. DWORD CheckAnswerNowCapability (BOOL bForceReconnect, LPDWORD lpdwDeviceId /* = NULL */);
  203. VOID FaxPrinterProperties(DWORD dwPage);
  204. VOID CopyLTRString(TCHAR* szDest, LPCTSTR szSource, DWORD dwSize);
  205. //////////////////////////////////////////////////////////////////////
  206. // Implementation
  207. //
  208. extern "C"
  209. BOOL
  210. FaxMonitorShutdown()
  211. {
  212. g_bShuttingDown = TRUE;
  213. return DestroyModule();
  214. } // FaxMonitorShutdown
  215. extern "C"
  216. BOOL
  217. IsFaxMessage(
  218. PMSG pMsg
  219. )
  220. /*++
  221. Routine name : IsFaxMessage
  222. Routine description:
  223. Fax message handle
  224. Arguments:
  225. pMsg - pointer to a message
  226. Return Value:
  227. TRUE if the message was handled
  228. FALSE otherwise
  229. --*/
  230. {
  231. BOOL bRes = FALSE;
  232. if(g_hMonitorDlg)
  233. {
  234. bRes = IsDialogMessage(g_hMonitorDlg, pMsg);
  235. }
  236. return bRes;
  237. } // IsFaxMessage
  238. VOID
  239. InitGlobals ()
  240. /*++
  241. Routine name : InitGlobals
  242. Routine description:
  243. Initializes all server connection related global variables
  244. Author:
  245. Eran Yariv (EranY), Dec, 2000
  246. Arguments:
  247. Return Value:
  248. None.
  249. --*/
  250. {
  251. DBG_ENTER(TEXT("InitGlobals"));
  252. g_hFaxSvcHandle = NULL;
  253. g_dwlCurrentMsgID = 0;
  254. g_dwCurrentJobID = 0;
  255. g_hNotification = NULL;
  256. g_hCall = NULL;
  257. g_szAddress[0] = TEXT('\0');
  258. g_szRemoteId[0] = TEXT('\0');
  259. g_bRecipientNameValid = FALSE;
  260. g_szRecipientName[0] = TEXT('\0');
  261. BOOL bDesktopSKU = IsDesktopSKU();
  262. g_ConfigOptions.dwMonitorDeviceId = 0;
  263. g_ConfigOptions.bSend = FALSE;
  264. g_ConfigOptions.bReceive = FALSE;
  265. g_ConfigOptions.dwManualAnswerDeviceId = 0;
  266. g_ConfigOptions.dwAccessRights = 0;
  267. g_ConfigOptions.bNotifyProgress = bDesktopSKU;
  268. g_ConfigOptions.bNotifyInCompletion = bDesktopSKU;
  269. g_ConfigOptions.bNotifyOutCompletion = bDesktopSKU;
  270. g_ConfigOptions.bMonitorOnSend = bDesktopSKU;
  271. g_ConfigOptions.bMonitorOnReceive = bDesktopSKU;
  272. g_ConfigOptions.bSoundOnRing = bDesktopSKU;
  273. g_ConfigOptions.bSoundOnReceive = bDesktopSKU;
  274. g_ConfigOptions.bSoundOnSent = bDesktopSKU;
  275. g_ConfigOptions.bSoundOnError = bDesktopSKU;
  276. for (DWORD dw = 0; dw < ICONS_COUNT; dw++)
  277. {
  278. g_Icons[dw].bEnable = FALSE;
  279. g_Icons[dw].tszToolTip[0] = TEXT('\0');
  280. }
  281. g_uRingTimerID = 0;
  282. g_dwCurrRingIconIndex = 0;
  283. g_dwRingAnimationStartTick = 0;
  284. g_BalloonInfo.bEnable = FALSE;
  285. g_BalloonInfo.bDelete = FALSE;
  286. g_BalloonInfo.szInfo[0] = TEXT('\0');
  287. g_BalloonInfo.szInfoTitle[0] = TEXT('\0');
  288. g_CurrentIcon = ICONS_COUNT;
  289. } // InitGlobals
  290. BOOL
  291. InitModule ()
  292. /*++
  293. Routine name : InitModule
  294. Routine description:
  295. Initializes the DLL module. Call only once.
  296. Author:
  297. Eran Yariv (EranY), Mar, 2001
  298. Arguments:
  299. Return Value:
  300. TRUE on success
  301. --*/
  302. {
  303. BOOL bRes = FALSE;
  304. DWORD dwRes;
  305. DBG_ENTER(TEXT("InitModule"), bRes);
  306. InitGlobals ();
  307. //
  308. // Don't have DllMain called for thread inits and shutdown.
  309. //
  310. DisableThreadLibraryCalls(g_hModule);
  311. //
  312. // Load icons
  313. //
  314. for(DWORD dw=0; dw < ICONS_COUNT; ++dw)
  315. {
  316. g_Icons[dw].hIcon = LoadIcon(g_hModule, MAKEINTRESOURCE(g_Icons[dw].dwIconResId));
  317. if(!g_Icons[dw].hIcon)
  318. {
  319. dwRes = GetLastError();
  320. CALL_FAIL (RESOURCE_ERR, TEXT ("LoadIcon"), dwRes);
  321. bRes = FALSE;
  322. return bRes;
  323. }
  324. }
  325. //
  326. // Load animation icons
  327. //
  328. for(dw=0; dw < RING_ICONS_NUM; ++dw)
  329. {
  330. g_RingIcons[dw].hIcon = LoadIcon(g_hModule, MAKEINTRESOURCE(g_RingIcons[dw].dwIconResId));
  331. if(!g_RingIcons[dw].hIcon)
  332. {
  333. dwRes = GetLastError();
  334. CALL_FAIL (RESOURCE_ERR, TEXT ("LoadIcon"), dwRes);
  335. bRes = FALSE;
  336. return bRes;
  337. }
  338. }
  339. //
  340. // Load "new fax" tooltip
  341. //
  342. if (ERROR_SUCCESS != (dwRes = LoadAndFormatString (IDS_NEW_FAX, g_Icons[ICON_NEW_FAX].tszToolTip, TOOLTIP_SIZE)))
  343. {
  344. SetLastError (dwRes);
  345. bRes = FALSE;
  346. return bRes;
  347. }
  348. //
  349. // Register our hidden window and create it
  350. //
  351. WNDCLASSEX wndclass = {0};
  352. wndclass.cbSize = sizeof(wndclass);
  353. wndclass.style = CS_HREDRAW | CS_VREDRAW;
  354. wndclass.lpfnWndProc = NotifyWndProc;
  355. wndclass.hInstance = g_hModule;
  356. wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
  357. wndclass.hbrBackground = (HBRUSH) (COLOR_INACTIVEBORDER + 1);
  358. wndclass.lpszClassName = FAXSTAT_WINCLASS;
  359. if(!RegisterClassEx(&wndclass))
  360. {
  361. dwRes = GetLastError();
  362. CALL_FAIL (WINDOW_ERR, TEXT ("RegisterClassEx"), dwRes);
  363. bRes = FALSE;
  364. return bRes;
  365. }
  366. g_hWndFaxNotify = CreateWindow (FAXSTAT_WINCLASS,
  367. TEXT("HiddenFaxWindow"),
  368. 0,
  369. CW_USEDEFAULT,
  370. 0,
  371. CW_USEDEFAULT,
  372. 0,
  373. NULL,
  374. NULL,
  375. g_hModule,
  376. NULL);
  377. if(!g_hWndFaxNotify)
  378. {
  379. dwRes = GetLastError();
  380. CALL_FAIL (WINDOW_ERR, TEXT ("CreateWindow"), dwRes);
  381. bRes = FALSE;
  382. return bRes;
  383. }
  384. //
  385. // Create stop thread event
  386. //
  387. g_hStopStartupThreadEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
  388. if(!g_hStopStartupThreadEvent)
  389. {
  390. dwRes = GetLastError();
  391. CALL_FAIL (WINDOW_ERR, TEXT ("CreateEvent"), dwRes);
  392. bRes = FALSE;
  393. return bRes;
  394. }
  395. //
  396. // Launch a thread which waits for the local fax service startup event.
  397. // When the event is set, the thread posts WM_FAX_STARTED to our hidden window.
  398. //
  399. WaitForFaxRestart(g_hWndFaxNotify);
  400. bRes = TRUE;
  401. return bRes;
  402. } // InitModule
  403. DWORD
  404. WaitForBackgroundThreadToDie ()
  405. {
  406. DWORD dwRes = ERROR_SUCCESS;
  407. DBG_ENTER(TEXT("WaitForBackgroundThreadToDie"), dwRes);
  408. ASSERTION (g_hServerStartupThread);
  409. DWORD dwWaitRes = WaitForSingleObject (g_hServerStartupThread, INFINITE);
  410. switch (dwWaitRes)
  411. {
  412. case WAIT_OBJECT_0:
  413. //
  414. // Thread terminated - hooray
  415. //
  416. VERBOSE (DBG_MSG, TEXT("Background thread terminated successfully"));
  417. CloseHandle (g_hServerStartupThread);
  418. g_hServerStartupThread = NULL;
  419. break;
  420. case WAIT_FAILED:
  421. //
  422. // Error waiting for thread to die
  423. //
  424. dwRes = GetLastError ();
  425. VERBOSE (DBG_MSG, TEXT("Can't wait for background thread: %ld"), dwRes);
  426. break;
  427. default:
  428. //
  429. // No other return value from WaitForSingleObject is valid
  430. //
  431. ASSERTION_FAILURE;
  432. dwRes = ERROR_GEN_FAILURE;
  433. break;
  434. }
  435. return dwRes;
  436. } // WaitForBackgroundThreadToDie
  437. BOOL
  438. DestroyModule ()
  439. /*++
  440. Routine name : DestroyModule
  441. Routine description:
  442. Destroys the DLL module. Call only once.
  443. Author:
  444. Eran Yariv (EranY), Mar, 2001
  445. Arguments:
  446. Return Value:
  447. TRUE on success
  448. --*/
  449. {
  450. BOOL bRes = FALSE;
  451. DBG_ENTER(TEXT("DestroyModule"), bRes);
  452. //
  453. // Prepare for shutdown - destroy all active windows
  454. //
  455. if (g_hMonitorDlg)
  456. {
  457. //
  458. // Fake 'hide' key press on the monitor dialog
  459. //
  460. SendMessage (g_hMonitorDlg, WM_COMMAND, IDCANCEL, 0);
  461. }
  462. //
  463. // Delete the system tray icon if existed
  464. //
  465. if (g_bIconAdded)
  466. {
  467. NOTIFYICONDATA iconData = {0};
  468. iconData.cbSize = sizeof(iconData);
  469. iconData.hWnd = g_hWndFaxNotify;
  470. iconData.uID = TRAY_ICON_ID;
  471. Shell_NotifyIcon(NIM_DELETE, &iconData);
  472. g_bIconAdded = FALSE;
  473. }
  474. //
  475. // Destory this window
  476. //
  477. if (!DestroyWindow (g_hWndFaxNotify))
  478. {
  479. CALL_FAIL (WINDOW_ERR, TEXT("DestroyWindow"), GetLastError ());
  480. }
  481. g_hWndFaxNotify = NULL;
  482. //
  483. // Signal the DLL shutdown event
  484. //
  485. ASSERTION (g_hStopStartupThreadEvent);
  486. if (SetEvent (g_hStopStartupThreadEvent))
  487. {
  488. VERBOSE (DBG_MSG, TEXT("DLL shutdown event signaled"));
  489. if (g_hServerStartupThread)
  490. {
  491. //
  492. // Wait for background thread to die
  493. //
  494. DWORD dwRes = WaitForBackgroundThreadToDie();
  495. if (ERROR_SUCCESS != dwRes)
  496. {
  497. CALL_FAIL (GENERAL_ERR, TEXT("WaitForBackgroundThreadToDie"), dwRes);
  498. }
  499. }
  500. }
  501. else
  502. {
  503. CALL_FAIL (GENERAL_ERR, TEXT("SetEvent (g_hStopStartupThreadEvent)"), GetLastError ());
  504. }
  505. //
  506. // Release our DLL shutdown event
  507. //
  508. CloseHandle (g_hStopStartupThreadEvent);
  509. g_hStopStartupThreadEvent = NULL;
  510. //
  511. // Free the data of the monitor module
  512. //
  513. FreeMonitorDialogData (TRUE);
  514. //
  515. // Unregister window class
  516. //
  517. if (!UnregisterClass (FAXSTAT_WINCLASS, g_hModule))
  518. {
  519. CALL_FAIL (WINDOW_ERR, TEXT("UnregisterClass"), GetLastError ());
  520. }
  521. //
  522. // Unregister from server notifications
  523. //
  524. if (g_hNotification)
  525. {
  526. if(!FaxUnregisterForServerEvents(g_hNotification))
  527. {
  528. CALL_FAIL (RPC_ERR, TEXT("FaxUnregisterForServerEvents"), GetLastError());
  529. }
  530. g_hNotification = NULL;
  531. }
  532. //
  533. // Disconnect from the fax service
  534. //
  535. if (g_hFaxSvcHandle)
  536. {
  537. if (!FaxClose (g_hFaxSvcHandle))
  538. {
  539. CALL_FAIL (GENERAL_ERR, TEXT("FaxClose"), GetLastError ());
  540. }
  541. g_hFaxSvcHandle = NULL;
  542. }
  543. //
  544. // Unload all icons
  545. //
  546. for (DWORD dw = 0; dw < ICONS_COUNT; dw++)
  547. {
  548. if (g_Icons[dw].hIcon)
  549. {
  550. if (!DestroyIcon (g_Icons[dw].hIcon))
  551. {
  552. CALL_FAIL (WINDOW_ERR, TEXT("DestroyIcon"), GetLastError ());
  553. }
  554. g_Icons[dw].hIcon = NULL;
  555. }
  556. }
  557. for (DWORD dw = 0; dw < RING_ICONS_NUM; dw++)
  558. {
  559. if (g_RingIcons[dw].hIcon)
  560. {
  561. if (!DestroyIcon (g_RingIcons[dw].hIcon))
  562. {
  563. CALL_FAIL (WINDOW_ERR, TEXT("DestroyIcon"), GetLastError ());
  564. }
  565. g_RingIcons[dw].hIcon = NULL;
  566. }
  567. }
  568. //
  569. // Kill animation timer
  570. //
  571. if(g_uRingTimerID)
  572. {
  573. if (!KillTimer(NULL, g_uRingTimerID))
  574. {
  575. CALL_FAIL (GENERAL_ERR, TEXT("KillTimer"), GetLastError ());
  576. }
  577. g_uRingTimerID = NULL;
  578. }
  579. bRes = TRUE;
  580. return bRes;
  581. } // DestroyModule
  582. BOOL
  583. WINAPI
  584. DllMain(
  585. HINSTANCE hModule,
  586. DWORD dwReason,
  587. void* lpReserved
  588. )
  589. /*++
  590. Routine description:
  591. Fax notifications startup
  592. Arguments:
  593. hinstDLL - handle to the DLL module
  594. fdwReason - reason for calling function
  595. lpvReserved - reserved
  596. Return Value:
  597. TRUE if success
  598. FALSE otherwise
  599. --*/
  600. {
  601. BOOL bRes = TRUE;
  602. DBG_ENTER(TEXT("DllMain"), bRes, TEXT("Reason = %ld"), dwReason);
  603. switch (dwReason)
  604. {
  605. case DLL_PROCESS_ATTACH:
  606. g_hModule = hModule;
  607. g_hResource = GetResInstance(hModule);
  608. if(!g_hResource)
  609. {
  610. return FALSE;
  611. }
  612. bRes = InitModule ();
  613. return bRes;
  614. case DLL_PROCESS_DETACH:
  615. //
  616. // If g_bShuttingDown is not TRUE, someone (STOBJECT.DLL) forgot to call
  617. // FaxMonitorShutdown() (our shutdown procedure) before doing FreeLibrary on us.
  618. // This is not the way we're supposed to be used - a bug.
  619. //
  620. ASSERTION (g_bShuttingDown);
  621. HeapCleanup();
  622. FreeResInstance();
  623. return bRes;
  624. default:
  625. return bRes;
  626. }
  627. } // DllMain
  628. DWORD
  629. WaitForRestartThread(
  630. LPVOID ThreadData
  631. )
  632. {
  633. //
  634. // Wait for event to be signaled, indicating fax service started
  635. //
  636. DWORD dwRes = ERROR_SUCCESS;
  637. HKEY hKey = NULL;
  638. HANDLE hEvents[2] = {0};
  639. DBG_ENTER(TEXT("WaitForRestartThread"), dwRes);
  640. //
  641. // NOTICE: Events order in the array matters - we want to detect DLL shutdown BEFORE we detect service startup
  642. //
  643. hEvents[0] = g_hStopStartupThreadEvent;
  644. if (hEvents[1])
  645. {
  646. CloseHandle (hEvents[1]);
  647. }
  648. if (hKey)
  649. {
  650. RegCloseKey (hKey);
  651. }
  652. //
  653. // Obtain service startup event handle.
  654. // We need to do this every time before calling WaitForMultipleObjects
  655. // because the event returned from CreateSvcStartEvent is a single-shot event.
  656. //
  657. dwRes = CreateSvcStartEvent (&(hEvents[1]), &hKey);
  658. if (ERROR_SUCCESS != dwRes)
  659. {
  660. CALL_FAIL (GENERAL_ERR, TEXT("CreateSvcStartEvent"), dwRes);
  661. goto ExitThisThread;
  662. }
  663. //
  664. // Wait for either the service startup event or the DLL shutdown event
  665. //
  666. DWORD dwWaitRes = WaitForMultipleObjects(ARR_SIZE(hEvents),
  667. hEvents,
  668. FALSE,
  669. INFINITE);
  670. switch (dwWaitRes)
  671. {
  672. case WAIT_OBJECT_0 + 1:
  673. //
  674. // Service startup event
  675. //
  676. VERBOSE (DBG_MSG, TEXT("Service startup event received"));
  677. PostMessage((HWND) ThreadData, WM_FAX_STARTED, 0, 0);
  678. break;
  679. case WAIT_OBJECT_0:
  680. //
  681. // Stop thread event - exit thread ASAP.
  682. //
  683. VERBOSE (DBG_MSG, TEXT("DLL shutdown event received"));
  684. break;
  685. case WAIT_FAILED:
  686. dwRes = GetLastError ();
  687. CALL_FAIL (GENERAL_ERR, TEXT("WaitForMultipleObjects"), dwRes);
  688. break;
  689. default:
  690. //
  691. // No other return value from WaitForMultipleObjects is valid.
  692. //
  693. ASSERTION_FAILURE;
  694. break;
  695. } // switch (dwWaitRes)
  696. ExitThisThread:
  697. if (hEvents[1])
  698. {
  699. CloseHandle (hEvents[1]);
  700. }
  701. if (hKey)
  702. {
  703. RegCloseKey (hKey);
  704. }
  705. return dwRes;
  706. } // WaitForRestartThread
  707. VOID
  708. WaitForFaxRestart(
  709. HWND hWnd
  710. )
  711. {
  712. DBG_ENTER(TEXT("WaitForFaxRestart"));
  713. if (g_bShuttingDown)
  714. {
  715. //
  716. // Shutting down - no thread creation allowed
  717. //
  718. return;
  719. }
  720. if (g_hServerStartupThread)
  721. {
  722. //
  723. // Signal to Startup Thread to stop
  724. //
  725. if (!SetEvent (g_hStopStartupThreadEvent))
  726. {
  727. CALL_FAIL (GENERAL_ERR, TEXT("SetEvent"), GetLastError());
  728. return;
  729. }
  730. //
  731. // A Previous thead exists - wait for it to die
  732. //
  733. DWORD dwRes = WaitForBackgroundThreadToDie();
  734. if (ERROR_SUCCESS != dwRes)
  735. {
  736. CALL_FAIL (GENERAL_ERR, TEXT("WaitForBackgroundThreadToDie"), dwRes);
  737. return;
  738. }
  739. }
  740. if (!ResetEvent (g_hStopStartupThreadEvent))
  741. {
  742. CALL_FAIL (GENERAL_ERR, TEXT("ResetEvent"), GetLastError());
  743. return;
  744. }
  745. ASSERTION (NULL == g_hServerStartupThread);
  746. g_hServerStartupThread = CreateThread(NULL, 0, WaitForRestartThread, (LPVOID) hWnd, 0, NULL);
  747. if (g_hServerStartupThread)
  748. {
  749. VERBOSE (DBG_MSG, TEXT("Background therad created successfully"));
  750. }
  751. else
  752. {
  753. CALL_FAIL (GENERAL_ERR, TEXT("CreateThread(WaitForRestartThread)"), GetLastError());
  754. }
  755. } // WaitForFaxRestart
  756. void
  757. GetConfiguration()
  758. /*++
  759. Routine description:
  760. Read notification configuration from the registry
  761. Arguments:
  762. none
  763. Return Value:
  764. none
  765. --*/
  766. {
  767. DWORD dwRes;
  768. DBG_ENTER(TEXT("GetConfiguration"));
  769. HKEY hKey;
  770. if(Connect())
  771. {
  772. if (!FaxAccessCheckEx(g_hFaxSvcHandle, MAXIMUM_ALLOWED, &g_ConfigOptions.dwAccessRights))
  773. {
  774. dwRes = GetLastError ();
  775. CALL_FAIL (RPC_ERR, TEXT("FaxAccessCheckEx"), dwRes);
  776. }
  777. }
  778. dwRes = RegOpenKeyEx(HKEY_CURRENT_USER, REGKEY_FAX_USERINFO, 0, KEY_READ, &hKey);
  779. if (dwRes != ERROR_SUCCESS)
  780. {
  781. //
  782. // Can't open user information key - use defaults
  783. //
  784. CALL_FAIL (GENERAL_ERR, TEXT("RegOpenKeyEx(REGKEY_FAX_USERINFO)"), dwRes);
  785. BOOL bDesktopSKU = IsDesktopSKU();
  786. g_ConfigOptions.dwMonitorDeviceId = 0;
  787. g_ConfigOptions.bNotifyProgress = bDesktopSKU;
  788. g_ConfigOptions.bNotifyInCompletion = bDesktopSKU;
  789. g_ConfigOptions.bNotifyOutCompletion = bDesktopSKU;
  790. g_ConfigOptions.bMonitorOnSend = bDesktopSKU;
  791. g_ConfigOptions.bMonitorOnReceive = bDesktopSKU;
  792. g_ConfigOptions.bSoundOnRing = bDesktopSKU;
  793. g_ConfigOptions.bSoundOnReceive = bDesktopSKU;
  794. g_ConfigOptions.bSoundOnSent = bDesktopSKU;
  795. g_ConfigOptions.bSoundOnError = bDesktopSKU;
  796. }
  797. else
  798. {
  799. GetRegistryDwordEx(hKey, REGVAL_NOTIFY_PROGRESS, &g_ConfigOptions.bNotifyProgress);
  800. GetRegistryDwordEx(hKey, REGVAL_NOTIFY_IN_COMPLETE, &g_ConfigOptions.bNotifyInCompletion);
  801. GetRegistryDwordEx(hKey, REGVAL_NOTIFY_OUT_COMPLETE, &g_ConfigOptions.bNotifyOutCompletion);
  802. GetRegistryDwordEx(hKey, REGVAL_MONITOR_ON_SEND, &g_ConfigOptions.bMonitorOnSend);
  803. GetRegistryDwordEx(hKey, REGVAL_MONITOR_ON_RECEIVE, &g_ConfigOptions.bMonitorOnReceive);
  804. GetRegistryDwordEx(hKey, REGVAL_SOUND_ON_RING, &g_ConfigOptions.bSoundOnRing);
  805. GetRegistryDwordEx(hKey, REGVAL_SOUND_ON_RECEIVE, &g_ConfigOptions.bSoundOnReceive);
  806. GetRegistryDwordEx(hKey, REGVAL_SOUND_ON_SENT, &g_ConfigOptions.bSoundOnSent);
  807. GetRegistryDwordEx(hKey, REGVAL_SOUND_ON_ERROR, &g_ConfigOptions.bSoundOnError);
  808. GetRegistryDwordEx(hKey, REGVAL_DEVICE_TO_MONITOR, &g_ConfigOptions.dwMonitorDeviceId);
  809. RegCloseKey( hKey );
  810. }
  811. g_ConfigOptions.dwManualAnswerDeviceId = 0;
  812. if(Connect() && IsUserGrantedAccess(FAX_ACCESS_QUERY_CONFIG))
  813. {
  814. PFAX_PORT_INFO_EX pPortsInfo = NULL;
  815. DWORD dwPorts = 0;
  816. if(!FaxEnumPortsEx(g_hFaxSvcHandle, &pPortsInfo, &dwPorts))
  817. {
  818. dwRes = GetLastError ();
  819. CALL_FAIL (RPC_ERR, TEXT("FaxEnumPortsEx"), dwRes);
  820. }
  821. else
  822. {
  823. if (dwPorts)
  824. {
  825. DWORD dwDevIndex = 0;
  826. for(DWORD dw=0; dw < dwPorts; ++dw)
  827. {
  828. //
  829. // Iterate all fax devices
  830. //
  831. if ((g_ConfigOptions.dwMonitorDeviceId == pPortsInfo[dw].dwDeviceID) || // Found the monitored device or
  832. (!g_ConfigOptions.dwMonitorDeviceId && // No monitored device and
  833. (pPortsInfo[dw].bSend || // the device is send-enabled or
  834. (FAX_DEVICE_RECEIVE_MODE_OFF != pPortsInfo[dw].ReceiveMode) // the device is receive-enabled
  835. )
  836. )
  837. )
  838. {
  839. //
  840. // Mark the index of the device we use for monitoring.
  841. //
  842. dwDevIndex = dw;
  843. }
  844. if (FAX_DEVICE_RECEIVE_MODE_MANUAL == pPortsInfo[dw].ReceiveMode)
  845. {
  846. //
  847. // Mark the id of the device set for manual-answer
  848. //
  849. g_ConfigOptions.dwManualAnswerDeviceId = pPortsInfo[dw].dwDeviceID;
  850. }
  851. }
  852. //
  853. // Update the device used for monitoring from the index we found
  854. //
  855. g_ConfigOptions.dwMonitorDeviceId = pPortsInfo[dwDevIndex].dwDeviceID;
  856. g_ConfigOptions.bSend = pPortsInfo[dwDevIndex].bSend;
  857. g_ConfigOptions.bReceive = FAX_DEVICE_RECEIVE_MODE_OFF != pPortsInfo[dwDevIndex].ReceiveMode;
  858. }
  859. else
  860. {
  861. //
  862. // No devices
  863. //
  864. g_ConfigOptions.dwMonitorDeviceId = 0;
  865. g_ConfigOptions.bSend = FALSE;
  866. g_ConfigOptions.bReceive = FALSE;
  867. }
  868. FaxFreeBuffer(pPortsInfo);
  869. }
  870. }
  871. } // GetConfiguration
  872. BOOL
  873. Connect(
  874. )
  875. {
  876. BOOL bRes = FALSE;
  877. DBG_ENTER(TEXT("Connect"), bRes);
  878. if (g_hFaxSvcHandle)
  879. {
  880. //
  881. // Already connected
  882. //
  883. bRes = TRUE;
  884. return bRes;
  885. }
  886. if (!FaxConnectFaxServer(NULL, &g_hFaxSvcHandle))
  887. {
  888. CALL_FAIL (RPC_ERR, TEXT("FaxConnectFaxServer"), GetLastError());
  889. return bRes;
  890. }
  891. bRes = TRUE;
  892. return bRes;
  893. } // Connect
  894. VOID
  895. CALLBACK
  896. WaitForFaxRestartTimerProc(
  897. HWND hwnd, // handle to window
  898. UINT uMsg, // WM_TIMER message
  899. UINT_PTR idEvent, // timer identifier
  900. DWORD dwTime // current system time
  901. )
  902. /*++
  903. Routine description:
  904. Timer proc for restart waiting thread
  905. Arguments:
  906. hwnd - handle to window
  907. uMsg - WM_TIMER message
  908. idEvent - timer identifier
  909. dwTime - current system time
  910. Return Value:
  911. none
  912. --*/
  913. {
  914. DBG_ENTER(TEXT("WaitForFaxRestartTimerProc"));
  915. if(!KillTimer(NULL, idEvent))
  916. {
  917. CALL_FAIL (GENERAL_ERR, TEXT ("KillTimer"), GetLastError());
  918. }
  919. WaitForFaxRestart(g_hWndFaxNotify);
  920. } // WaitForFaxRestartTimerProc
  921. BOOL
  922. RegisterForServerEvents()
  923. /*++
  924. Routine description:
  925. Register for fax notifications
  926. Arguments:
  927. none
  928. Return Value:
  929. none
  930. --*/
  931. {
  932. BOOL bRes = FALSE;
  933. DWORD dwEventTypes;
  934. DBG_ENTER(TEXT("RegisterForServerEvents"));
  935. if (!Connect())
  936. {
  937. goto exit;
  938. }
  939. //
  940. // Load configuration
  941. //
  942. GetConfiguration ();
  943. if(g_hNotification)
  944. {
  945. if(!FaxUnregisterForServerEvents(g_hNotification))
  946. {
  947. CALL_FAIL (RPC_ERR, TEXT("FaxUnregisterForServerEvents"), GetLastError());
  948. }
  949. g_hNotification = NULL;
  950. }
  951. //
  952. // Register for the fax events
  953. //
  954. dwEventTypes = FAX_EVENT_TYPE_FXSSVC_ENDED;
  955. VERBOSE (DBG_MSG,
  956. TEXT("User has the following rights: %x. Asking for FAX_EVENT_TYPE_FXSSVC_ENDED"),
  957. g_ConfigOptions.dwAccessRights);
  958. if(IsUserGrantedAccess(FAX_ACCESS_SUBMIT) ||
  959. IsUserGrantedAccess(FAX_ACCESS_SUBMIT_NORMAL) ||
  960. IsUserGrantedAccess(FAX_ACCESS_SUBMIT_HIGH)) // User can submit new faxes (and view his own faxes)
  961. {
  962. dwEventTypes |= FAX_EVENT_TYPE_OUT_QUEUE;
  963. VERBOSE (DBG_MSG, TEXT("Also asking for FAX_EVENT_TYPE_OUT_QUEUE"));
  964. }
  965. if(IsUserGrantedAccess(FAX_ACCESS_QUERY_JOBS)) // User can view all jobs (in and out)
  966. {
  967. dwEventTypes |= FAX_EVENT_TYPE_OUT_QUEUE | FAX_EVENT_TYPE_IN_QUEUE;
  968. VERBOSE (DBG_MSG, TEXT("Also asking for FAX_EVENT_TYPE_OUT_QUEUE & FAX_EVENT_TYPE_IN_QUEUE"));
  969. }
  970. if(IsUserGrantedAccess(FAX_ACCESS_QUERY_CONFIG))
  971. {
  972. dwEventTypes |= FAX_EVENT_TYPE_CONFIG | FAX_EVENT_TYPE_DEVICE_STATUS;
  973. VERBOSE (DBG_MSG, TEXT("Also asking for FAX_EVENT_TYPE_CONFIG & FAX_EVENT_TYPE_DEVICE_STATUS"));
  974. }
  975. if(IsUserGrantedAccess(FAX_ACCESS_QUERY_IN_ARCHIVE))
  976. {
  977. dwEventTypes |= FAX_EVENT_TYPE_IN_ARCHIVE | FAX_EVENT_TYPE_NEW_CALL;
  978. VERBOSE (DBG_MSG, TEXT("Also asking for FAX_EVENT_TYPE_IN_ARCHIVE"));
  979. }
  980. if (!FaxRegisterForServerEvents (g_hFaxSvcHandle,
  981. dwEventTypes, // Types of events to receive
  982. NULL, // Not using completion ports
  983. 0, // Not using completion ports
  984. g_hWndFaxNotify, // Handle of window to receive notification messages
  985. WM_FAX_EVENT, // Message id
  986. &g_hNotification)) // Notification handle
  987. {
  988. DWORD dwRes = GetLastError ();
  989. CALL_FAIL (RPC_ERR, TEXT("FaxRegisterForServerEvents"), dwRes);
  990. g_hNotification = NULL;
  991. }
  992. else
  993. {
  994. bRes = TRUE;
  995. }
  996. if(!FaxRelease(g_hFaxSvcHandle))
  997. {
  998. CALL_FAIL (RPC_ERR, TEXT("FaxRelease"), GetLastError ());
  999. }
  1000. exit:
  1001. if(!bRes)
  1002. {
  1003. //
  1004. // FaxRegisterForServerEvents failed, try again 1 minute later
  1005. //
  1006. if(!SetTimer(NULL, 0, 60000, WaitForFaxRestartTimerProc))
  1007. {
  1008. CALL_FAIL (GENERAL_ERR, TEXT("SetTimer"), GetLastError ());
  1009. }
  1010. }
  1011. return bRes;
  1012. } // RegisterForServerEvents
  1013. VOID
  1014. OnFaxEvent(FAX_EVENT_EX* pEvent)
  1015. /*++
  1016. Routine description:
  1017. Handle fax events
  1018. Arguments:
  1019. pEvent - fax event data
  1020. Return Value:
  1021. none
  1022. --*/
  1023. {
  1024. DBG_ENTER(TEXT("OnFaxEvent"), TEXT("%x"), pEvent);
  1025. if(!pEvent || pEvent->dwSizeOfStruct != sizeof(FAX_EVENT_EX))
  1026. {
  1027. VERBOSE (DBG_MSG, TEXT("Either event is bad or it has bad size"));
  1028. return;
  1029. }
  1030. switch (pEvent->EventType)
  1031. {
  1032. case FAX_EVENT_TYPE_NEW_CALL:
  1033. OnNewCall (pEvent->EventInfo.NewCall);
  1034. break;
  1035. case FAX_EVENT_TYPE_IN_QUEUE:
  1036. case FAX_EVENT_TYPE_OUT_QUEUE:
  1037. switch (pEvent->EventInfo.JobInfo.Type)
  1038. {
  1039. case FAX_JOB_EVENT_TYPE_ADDED:
  1040. case FAX_JOB_EVENT_TYPE_REMOVED:
  1041. break;
  1042. case FAX_JOB_EVENT_TYPE_STATUS:
  1043. if(pEvent->EventInfo.JobInfo.pJobData &&
  1044. pEvent->EventInfo.JobInfo.pJobData->dwDeviceID &&
  1045. pEvent->EventInfo.JobInfo.pJobData->dwDeviceID == g_ConfigOptions.dwMonitorDeviceId)
  1046. {
  1047. if(g_dwlCurrentMsgID != pEvent->EventInfo.JobInfo.dwlMessageId)
  1048. {
  1049. g_bRecipientNameValid = FALSE;
  1050. }
  1051. g_dwlCurrentMsgID = pEvent->EventInfo.JobInfo.dwlMessageId;
  1052. }
  1053. if(g_dwlCurrentMsgID == pEvent->EventInfo.JobInfo.dwlMessageId)
  1054. {
  1055. StatusUpdate(pEvent->EventInfo.JobInfo.pJobData);
  1056. }
  1057. break;
  1058. }
  1059. break;
  1060. case FAX_EVENT_TYPE_IN_ARCHIVE:
  1061. if(FAX_JOB_EVENT_TYPE_ADDED == pEvent->EventInfo.JobInfo.Type)
  1062. {
  1063. g_dwlNewMsgId = pEvent->EventInfo.JobInfo.dwlMessageId;
  1064. SetIconState(ICON_NEW_FAX, TRUE);
  1065. }
  1066. break;
  1067. case FAX_EVENT_TYPE_CONFIG:
  1068. if (FAX_CONFIG_TYPE_SECURITY == pEvent->EventInfo.ConfigType)
  1069. {
  1070. //
  1071. // Security has changed.
  1072. // We should re-register for events now.
  1073. // Also re-read the current user rights
  1074. //
  1075. RegisterForServerEvents();
  1076. }
  1077. else if (FAX_CONFIG_TYPE_DEVICES == pEvent->EventInfo.ConfigType)
  1078. {
  1079. //
  1080. // Device configuration has changed.
  1081. // The only reason we need to know that is because the device we were listening on might be gone now.
  1082. // If that's true, we should pick the first available device as the monitoring device.
  1083. //
  1084. GetConfiguration();
  1085. UpdateMonitorData(g_hMonitorDlg);
  1086. }
  1087. else
  1088. {
  1089. //
  1090. // Non-interesting configuraton change - ignore.
  1091. //
  1092. }
  1093. break;
  1094. case FAX_EVENT_TYPE_DEVICE_STATUS:
  1095. if(pEvent->EventInfo.DeviceStatus.dwDeviceId == g_ConfigOptions.dwMonitorDeviceId ||
  1096. pEvent->EventInfo.DeviceStatus.dwDeviceId == g_ConfigOptions.dwManualAnswerDeviceId)
  1097. {
  1098. //
  1099. // we only care about the monitored / manual-answer devices
  1100. //
  1101. if ((pEvent->EventInfo.DeviceStatus.dwNewStatus) & FAX_DEVICE_STATUS_RINGING)
  1102. {
  1103. //
  1104. // Device is ringing
  1105. //
  1106. OnDeviceRing (pEvent->EventInfo.DeviceStatus.dwDeviceId);
  1107. }
  1108. else
  1109. {
  1110. if (FAX_RINGING == g_devState)
  1111. {
  1112. //
  1113. // Device is not ringing anymore but the monitor shows 'ringing'.
  1114. // Set the monitor to idle state.
  1115. //
  1116. SetStatusMonitorDeviceState(FAX_IDLE);
  1117. }
  1118. }
  1119. }
  1120. break;
  1121. case FAX_EVENT_TYPE_FXSSVC_ENDED:
  1122. //
  1123. // Service was stopped
  1124. //
  1125. SetIconState(ICON_RINGING, FALSE);
  1126. SetIconState(ICON_SENDING, FALSE);
  1127. SetIconState(ICON_RECEIVING, FALSE);
  1128. SetStatusMonitorDeviceState(FAX_IDLE);
  1129. //
  1130. // We just lost our RPC connection handle and our notification handle. Close and zero them.
  1131. //
  1132. if (g_hNotification)
  1133. {
  1134. FaxUnregisterForServerEvents (g_hNotification);
  1135. g_hNotification = NULL;
  1136. }
  1137. if (g_hFaxSvcHandle)
  1138. {
  1139. FaxClose (g_hFaxSvcHandle);
  1140. g_hFaxSvcHandle = NULL;
  1141. }
  1142. WaitForFaxRestart(g_hWndFaxNotify);
  1143. break;
  1144. }
  1145. FaxFreeBuffer (pEvent);
  1146. } // OnFaxEvent
  1147. VOID
  1148. OnDeviceRing(
  1149. DWORD dwDeviceID
  1150. )
  1151. /*++
  1152. Routine description:
  1153. Called when a device is ringing
  1154. Arguments:
  1155. dwDeviceID - device ID
  1156. Return Value:
  1157. none
  1158. --*/
  1159. {
  1160. DBG_ENTER(TEXT("OnDeviceRing"), TEXT("%d"), dwDeviceID);
  1161. //
  1162. // It can be monitored or manual answer device
  1163. //
  1164. SetStatusMonitorDeviceState(FAX_RINGING);
  1165. AddStatusMonitorLogEvent(LIST_IMAGE_NONE, IDS_RINGING);
  1166. if(g_ConfigOptions.bSoundOnRing)
  1167. {
  1168. if(!PlaySound(g_Icons[ICON_RINGING].pctsSound, NULL, SND_ASYNC | SND_APPLICATION | SND_NODEFAULT))
  1169. {
  1170. CALL_FAIL (WINDOW_ERR, TEXT ("PlaySound"), 0);
  1171. }
  1172. }
  1173. }
  1174. VOID
  1175. OnNewCall (
  1176. const FAX_EVENT_NEW_CALL &NewCall
  1177. )
  1178. /*++
  1179. Routine description:
  1180. Handle "new call" fax event
  1181. Arguments:
  1182. NewCall - fax event data
  1183. Return Value:
  1184. none
  1185. --*/
  1186. {
  1187. DBG_ENTER(TEXT("OnNewCall"));
  1188. //
  1189. // It can be any manual answer device
  1190. //
  1191. g_hCall = NewCall.hCall;
  1192. if(NewCall.hCall)
  1193. {
  1194. LPCTSTR lpctstrParam = NULL;
  1195. DWORD dwStringResId = IDS_INCOMING_CALL;
  1196. CopyLTRString(g_szAddress, NewCall.lptstrCallerId, ARR_SIZE(g_szAddress) - 1);
  1197. _tcscpy(g_szRemoteId, g_szAddress);
  1198. if(NewCall.lptstrCallerId && _tcslen(NewCall.lptstrCallerId))
  1199. {
  1200. //
  1201. // We know the caller id.
  1202. // Use another string which formats the caller ID parameter
  1203. //
  1204. lpctstrParam = NewCall.lptstrCallerId;
  1205. dwStringResId = IDS_INCOMING_CALL_FROM;
  1206. }
  1207. TCHAR tszEvent[MAX_PATH] = {0};
  1208. AddStatusMonitorLogEvent (LIST_IMAGE_NONE, dwStringResId, lpctstrParam, tszEvent, ARR_SIZE(tszEvent));
  1209. SetStatusMonitorDeviceState(FAX_RINGING);
  1210. SetIconState(ICON_RINGING, TRUE, tszEvent);
  1211. }
  1212. else
  1213. {
  1214. //
  1215. // Call is gone
  1216. //
  1217. SetStatusMonitorDeviceState(FAX_IDLE);
  1218. SetIconState(ICON_RINGING, FALSE, TEXT(""));
  1219. }
  1220. } // OnNewCall
  1221. VOID
  1222. GetRemoteId(
  1223. PFAX_JOB_STATUS pStatus
  1224. )
  1225. /*++
  1226. Routine description:
  1227. Write Sender ID or Recipient ID into g_szRemoteId
  1228. Sender ID (receive):
  1229. TSID or
  1230. Caller ID or
  1231. "unknown caller"
  1232. Recipient ID (send):
  1233. Recipient name or
  1234. CSID or
  1235. Recipient phone number.
  1236. Arguments:
  1237. pStatus - job status data
  1238. Return Value:
  1239. none
  1240. --*/
  1241. {
  1242. DBG_ENTER(TEXT("GetRemoteId"));
  1243. if(!pStatus)
  1244. {
  1245. return;
  1246. }
  1247. if(JT_SEND == pStatus->dwJobType)
  1248. {
  1249. //
  1250. // Recipient ID (send)
  1251. //
  1252. if(!g_bRecipientNameValid)
  1253. {
  1254. //
  1255. // Store the recipient name into g_szRecipientName
  1256. //
  1257. PFAX_JOB_ENTRY_EX pJobEntry = NULL;
  1258. if(!FaxGetJobEx(g_hFaxSvcHandle, g_dwlCurrentMsgID, &pJobEntry))
  1259. {
  1260. CALL_FAIL (RPC_ERR, TEXT ("FaxGetJobEx"), GetLastError());
  1261. g_szRecipientName[0] = TEXT('\0');
  1262. }
  1263. else
  1264. {
  1265. if(pJobEntry->lpctstrRecipientName && _tcslen(pJobEntry->lpctstrRecipientName))
  1266. {
  1267. _tcsncpy(g_szRecipientName, pJobEntry->lpctstrRecipientName, ARR_SIZE(g_szRecipientName) - 1);
  1268. }
  1269. else
  1270. {
  1271. g_szRecipientName[0] = TEXT('\0');
  1272. }
  1273. g_bRecipientNameValid = TRUE;
  1274. FaxFreeBuffer(pJobEntry);
  1275. }
  1276. }
  1277. if(_tcslen(g_szRecipientName))
  1278. {
  1279. //
  1280. // Recipient name
  1281. //
  1282. _tcsncpy(g_szRemoteId, g_szRecipientName, ARR_SIZE(g_szRemoteId) - 1);
  1283. }
  1284. else if(pStatus->lpctstrCsid && _tcslen(pStatus->lpctstrCsid))
  1285. {
  1286. //
  1287. // CSID
  1288. //
  1289. CopyLTRString(g_szRemoteId, pStatus->lpctstrCsid, ARR_SIZE(g_szRemoteId) - 1);
  1290. }
  1291. else if(pStatus->lpctstrCallerID && _tcslen(pStatus->lpctstrCallerID))
  1292. {
  1293. //
  1294. // Recipient number
  1295. // For outgoing fax FAX_JOB_STATUS.lpctstrCallerID field
  1296. // contains a recipient fax number.
  1297. //
  1298. CopyLTRString(g_szRemoteId, pStatus->lpctstrCallerID, ARR_SIZE(g_szRemoteId) - 1);
  1299. }
  1300. }
  1301. else if(JT_RECEIVE == pStatus->dwJobType)
  1302. {
  1303. //
  1304. // Sender ID (receive)
  1305. //
  1306. if(pStatus->lpctstrTsid && _tcslen(pStatus->lpctstrTsid) &&
  1307. pStatus->lpctstrCallerID && _tcslen(pStatus->lpctstrCallerID))
  1308. {
  1309. //
  1310. // We have Caller ID and TSID
  1311. //
  1312. TCHAR szTmp[MAX_PATH] = {0};
  1313. _sntprintf(szTmp,
  1314. ARR_SIZE(szTmp)-1,
  1315. TEXT("%s (%s)"),
  1316. pStatus->lpctstrCallerID,
  1317. pStatus->lpctstrTsid);
  1318. CopyLTRString(g_szRemoteId, szTmp, ARR_SIZE(g_szRemoteId) - 1);
  1319. }
  1320. else if(pStatus->lpctstrTsid && _tcslen(pStatus->lpctstrTsid))
  1321. {
  1322. //
  1323. // TSID
  1324. //
  1325. CopyLTRString(g_szRemoteId, pStatus->lpctstrTsid, ARR_SIZE(g_szRemoteId) - 1);
  1326. }
  1327. else if(pStatus->lpctstrCallerID && _tcslen(pStatus->lpctstrCallerID))
  1328. {
  1329. //
  1330. // Caller ID
  1331. //
  1332. CopyLTRString(g_szRemoteId, pStatus->lpctstrCallerID, ARR_SIZE(g_szRemoteId) - 1);
  1333. }
  1334. else
  1335. {
  1336. //
  1337. // unknown caller
  1338. //
  1339. _tcsncpy(g_szRemoteId, TEXT(""), ARR_SIZE(g_szRemoteId) - 1);
  1340. }
  1341. }
  1342. }
  1343. VOID
  1344. StatusUpdate(PFAX_JOB_STATUS pStatus)
  1345. /*++
  1346. Routine description:
  1347. Handle "status update" fax event
  1348. Arguments:
  1349. pStatus - job status data
  1350. Return Value:
  1351. none
  1352. --*/
  1353. {
  1354. DBG_ENTER(TEXT("StatusUpdate"));
  1355. DWORD dwRes;
  1356. if(!pStatus)
  1357. {
  1358. return;
  1359. }
  1360. VERBOSE (DBG_MSG,
  1361. TEXT("Job status event - Type=%x, QueueStatus=%x, ExtendedStatus=%x"),
  1362. pStatus->dwJobType,
  1363. pStatus->dwQueueStatus,
  1364. pStatus->dwExtendedStatus);
  1365. if(JT_RECEIVE != pStatus->dwJobType && JT_SEND != pStatus->dwJobType)
  1366. {
  1367. VERBOSE (DBG_MSG, TEXT("Job type (%d) is not JT_RECEIVE or JT_SEND. Ignoring."), pStatus->dwJobType);
  1368. return;
  1369. }
  1370. eIconType eIcon = LIST_IMAGE_NONE; // New icon to set
  1371. DWORD dwStatusId = 0; // string resource ID
  1372. TCHAR tszStatus[MAX_PATH] = {0}; // String to show in status monitor
  1373. BOOL bStatus = FALSE; // TRUE if tszStatus has valid string
  1374. if(pStatus->dwQueueStatus & JS_PAUSED)
  1375. {
  1376. //
  1377. // The job has been paused in the outbox queue after a failure
  1378. //
  1379. g_dwlCurrentMsgID = 0;
  1380. return;
  1381. }
  1382. if(pStatus->dwQueueStatus & JS_COMPLETED || pStatus->dwQueueStatus & JS_ROUTING)
  1383. {
  1384. //
  1385. // Incoming job sends JS_ROUTING status by completion
  1386. //
  1387. if(JS_EX_PARTIALLY_RECEIVED == pStatus->dwExtendedStatus)
  1388. {
  1389. bStatus = GetStatusEx(pStatus, &eIcon, tszStatus, ARR_SIZE(tszStatus) - 1);
  1390. }
  1391. else
  1392. {
  1393. eIcon = LIST_IMAGE_SUCCESS;
  1394. dwStatusId = (JT_SEND == pStatus->dwJobType) ? IDS_FAX_SNT_COMPLETED : IDS_FAX_RCV_COMPLETED;
  1395. }
  1396. }
  1397. else if(pStatus->dwQueueStatus & JS_CANCELING)
  1398. {
  1399. dwStatusId = IDS_FAX_CANCELING;
  1400. }
  1401. else if(pStatus->dwQueueStatus & JS_CANCELED)
  1402. {
  1403. dwStatusId = IDS_FAX_CANCELED;
  1404. }
  1405. else if(pStatus->dwQueueStatus & JS_INPROGRESS)
  1406. {
  1407. GetRemoteId(pStatus);
  1408. bStatus = GetStatusEx(pStatus, &eIcon, tszStatus, ARR_SIZE(tszStatus) - 1);
  1409. g_dwCurrentJobID = pStatus->dwJobID;
  1410. SetIconState((JT_SEND == pStatus->dwJobType) ? ICON_SENDING : ICON_RECEIVING, TRUE, tszStatus);
  1411. SetStatusMonitorDeviceState((JT_SEND == pStatus->dwJobType) ? FAX_SENDING : FAX_RECEIVING);
  1412. }
  1413. else if(pStatus->dwQueueStatus & JS_FAILED)
  1414. {
  1415. if(!(bStatus = GetStatusEx(pStatus, &eIcon, tszStatus, ARR_SIZE(tszStatus) - 1)))
  1416. {
  1417. eIcon = LIST_IMAGE_ERROR;
  1418. dwStatusId = (JT_SEND == pStatus->dwJobType) ? IDS_FAX_FATAL_ERROR_SND : IDS_FAX_FATAL_ERROR_RCV;
  1419. }
  1420. }
  1421. else if(pStatus->dwQueueStatus & JS_RETRIES_EXCEEDED)
  1422. {
  1423. //
  1424. // Add two strings to the log.
  1425. // The first is extended status.
  1426. // The second is "Retries exceeded"
  1427. //
  1428. if(bStatus = GetStatusEx(pStatus, &eIcon, tszStatus, ARR_SIZE(tszStatus) - 1))
  1429. {
  1430. AddStatusMonitorLogEvent(eIcon, tszStatus);
  1431. bStatus = FALSE;
  1432. }
  1433. eIcon = LIST_IMAGE_ERROR;
  1434. dwStatusId = IDS_FAX_RETRIES_EXCEEDED;
  1435. }
  1436. else if(pStatus->dwQueueStatus & JS_RETRYING)
  1437. {
  1438. if(!(bStatus = GetStatusEx(pStatus, &eIcon, tszStatus, ARR_SIZE(tszStatus) - 1)))
  1439. {
  1440. eIcon = LIST_IMAGE_ERROR;
  1441. dwStatusId = IDS_FAX_FATAL_ERROR_SND;
  1442. }
  1443. }
  1444. if(!bStatus && dwStatusId)
  1445. {
  1446. if (ERROR_SUCCESS != (dwRes = LoadAndFormatString (dwStatusId, tszStatus, ARR_SIZE(tszStatus))))
  1447. {
  1448. bStatus = FALSE;
  1449. }
  1450. else
  1451. {
  1452. bStatus = TRUE;
  1453. }
  1454. }
  1455. if(bStatus)
  1456. {
  1457. AddStatusMonitorLogEvent (eIcon, tszStatus);
  1458. }
  1459. if(!(pStatus->dwQueueStatus & JS_INPROGRESS))
  1460. {
  1461. g_dwCurrentJobID = 0;
  1462. SetStatusMonitorDeviceState(FAX_IDLE);
  1463. SetIconState(ICON_SENDING, FALSE);
  1464. SetIconState(ICON_RECEIVING, FALSE);
  1465. }
  1466. if(pStatus->dwQueueStatus & (JS_FAILED | JS_RETRIES_EXCEEDED | JS_RETRYING))
  1467. {
  1468. if(JT_SEND == pStatus->dwJobType)
  1469. {
  1470. g_dwlSendFailedMsgId = g_dwlCurrentMsgID;
  1471. }
  1472. SetIconState((JT_SEND == pStatus->dwJobType) ? ICON_SEND_FAILED : ICON_RECEIVE_FAILED, TRUE, tszStatus);
  1473. }
  1474. if((JT_SEND == pStatus->dwJobType) && (pStatus->dwQueueStatus & JS_COMPLETED))
  1475. {
  1476. SetIconState(ICON_SEND_SUCCESS, TRUE, tszStatus);
  1477. g_dwlSendSuccessMsgId = g_dwlCurrentMsgID;
  1478. }
  1479. } // StatusUpdate
  1480. /*
  1481. Unhandled Job Statuses:
  1482. JS_NOLINE
  1483. JS_PAUSED
  1484. JS_PENDING
  1485. JS_DELETING
  1486. Unhandled Extneded Job Statuses:
  1487. JS_EX_HANDLED
  1488. */
  1489. BOOL
  1490. GetStatusEx(
  1491. PFAX_JOB_STATUS pStatus,
  1492. eIconType* peIcon,
  1493. TCHAR* ptsStatusEx,
  1494. DWORD dwSize
  1495. )
  1496. /*++
  1497. Routine description:
  1498. Find string description and icon type for a job
  1499. according to its extended status
  1500. Arguments:
  1501. pStatus - [in] job status data
  1502. peIcon - [out] job icon type
  1503. ptsStatusEx - [out] job status string
  1504. dwSize - [in] status string size
  1505. Return Value:
  1506. TRUE if success
  1507. FALSE otherwise
  1508. --*/
  1509. {
  1510. BOOL bRes = FALSE;
  1511. DBG_ENTER(TEXT("GetStatusEx"), bRes);
  1512. ASSERTION (pStatus && peIcon && ptsStatusEx);
  1513. TCHAR tszFormat[MAX_PATH]={0};
  1514. if (pStatus->lpctstrExtendedStatus)
  1515. {
  1516. //
  1517. // FSP provided proprietary status string - use it as is.
  1518. //
  1519. *peIcon = LIST_IMAGE_WARNING;
  1520. _tcsncpy(ptsStatusEx, pStatus->lpctstrExtendedStatus, dwSize);
  1521. ptsStatusEx[dwSize-1] = TEXT('\0');
  1522. bRes = TRUE;
  1523. return bRes;
  1524. }
  1525. //
  1526. // No extended status string, check for well known status code
  1527. //
  1528. if(!(pStatus->dwValidityMask & FAX_JOB_FIELD_STATUS_EX) ||
  1529. !pStatus->dwExtendedStatus)
  1530. {
  1531. return FALSE;
  1532. }
  1533. *peIcon = LIST_IMAGE_NONE;
  1534. for(DWORD dw=0; g_StatusEx[dw].dwExtStatus != 0; ++dw)
  1535. {
  1536. if(g_StatusEx[dw].dwExtStatus == pStatus->dwExtendedStatus)
  1537. {
  1538. DWORD dwRes;
  1539. *peIcon = g_StatusEx[dw].eIcon;
  1540. if (ERROR_SUCCESS != (dwRes = LoadAndFormatString (g_StatusEx[dw].uResourceId, tszFormat, ARR_SIZE(tszFormat))))
  1541. {
  1542. return bRes;
  1543. }
  1544. break;
  1545. }
  1546. }
  1547. switch(pStatus->dwExtendedStatus)
  1548. {
  1549. case JS_EX_DIALING:
  1550. //
  1551. // For outgoing fax FAX_JOB_STATUS.lpctstrCallerID field
  1552. // contains a recipient fax number.
  1553. //
  1554. CopyLTRString(g_szAddress, pStatus->lpctstrCallerID, ARR_SIZE(g_szAddress) - 1);
  1555. _sntprintf(ptsStatusEx, dwSize -1, tszFormat, g_szAddress);
  1556. ptsStatusEx[dwSize-1] = TEXT('\0');
  1557. break;
  1558. case JS_EX_TRANSMITTING:
  1559. _sntprintf(ptsStatusEx, dwSize -1, tszFormat, pStatus->dwCurrentPage, pStatus->dwPageCount);
  1560. ptsStatusEx[dwSize-1] = TEXT('\0');
  1561. break;
  1562. case JS_EX_RECEIVING:
  1563. _sntprintf(ptsStatusEx, dwSize -1, tszFormat, pStatus->dwCurrentPage);
  1564. ptsStatusEx[dwSize-1] = TEXT('\0');
  1565. break;
  1566. case JS_EX_FATAL_ERROR:
  1567. {
  1568. DWORD dwRes;
  1569. if (ERROR_SUCCESS != (dwRes = LoadAndFormatString (
  1570. (JT_SEND == pStatus->dwJobType) ?
  1571. IDS_FAX_FATAL_ERROR_SND :
  1572. IDS_FAX_FATAL_ERROR_RCV,
  1573. ptsStatusEx,
  1574. dwSize)))
  1575. {
  1576. return bRes;
  1577. }
  1578. }
  1579. break;
  1580. default:
  1581. _tcsncpy(ptsStatusEx, tszFormat, dwSize);
  1582. break;
  1583. }
  1584. bRes = TRUE;
  1585. return bRes;
  1586. } // GetStatusEx
  1587. BOOL
  1588. IsNotifyEnable(
  1589. eIconState state
  1590. )
  1591. /*++
  1592. Routine description:
  1593. Check if the UI notification is enabled for a specific icon state
  1594. Arguments:
  1595. state [in] - icon state
  1596. Return Value:
  1597. TRUE if the notification is enabled
  1598. FASLE otherwise
  1599. --*/
  1600. {
  1601. BOOL bEnable = TRUE;
  1602. switch(state)
  1603. {
  1604. case ICON_SENDING:
  1605. case ICON_RECEIVING:
  1606. bEnable = g_ConfigOptions.bNotifyProgress;
  1607. break;
  1608. case ICON_NEW_FAX:
  1609. case ICON_RECEIVE_FAILED:
  1610. bEnable = g_ConfigOptions.bNotifyInCompletion;
  1611. break;
  1612. case ICON_SEND_SUCCESS:
  1613. case ICON_SEND_FAILED:
  1614. bEnable = g_ConfigOptions.bNotifyOutCompletion;
  1615. break;
  1616. };
  1617. return bEnable;
  1618. } // IsNotifyEnable
  1619. eIconState
  1620. GetVisibleIconType ()
  1621. /*++
  1622. Routine name : GetVisibleIconType
  1623. Routine description:
  1624. Return the index (type) of the currently visible icon
  1625. Author:
  1626. Eran Yariv (EranY), May, 2001
  1627. Arguments:
  1628. Return Value:
  1629. Icon type
  1630. --*/
  1631. {
  1632. for(int index = ICON_RINGING; index < ICONS_COUNT; ++index)
  1633. {
  1634. if(!IsNotifyEnable(eIconState(index)))
  1635. {
  1636. continue;
  1637. }
  1638. if(g_Icons[index].bEnable)
  1639. {
  1640. return eIconState(index);
  1641. }
  1642. }
  1643. return ICONS_COUNT;
  1644. } // GetVisibleIconType
  1645. void
  1646. EvaluateIcon()
  1647. /*++
  1648. Routine description:
  1649. Show notification icon, tooltip and balloon
  1650. according to the current icon state
  1651. Arguments:
  1652. Return Value:
  1653. none
  1654. --*/
  1655. {
  1656. DBG_ENTER(TEXT("EvaluateIcon"));
  1657. ASSERTION (g_hWndFaxNotify);
  1658. NOTIFYICONDATA iconData = {0};
  1659. iconData.cbSize = sizeof(iconData);
  1660. iconData.hWnd = g_hWndFaxNotify;
  1661. iconData.uID = TRAY_ICON_ID;
  1662. iconData.uFlags = NIF_MESSAGE | NIF_TIP;
  1663. iconData.uCallbackMessage = WM_TRAYCALLBACK;
  1664. g_CurrentIcon = GetVisibleIconType();
  1665. if(ICONS_COUNT == g_CurrentIcon)
  1666. {
  1667. //
  1668. // No visible icon
  1669. //
  1670. if(g_bIconAdded)
  1671. {
  1672. Shell_NotifyIcon(NIM_DELETE, &iconData);
  1673. g_bIconAdded = FALSE;
  1674. }
  1675. //
  1676. // No icon - no balloon
  1677. //
  1678. g_BalloonInfo.bDelete = FALSE;
  1679. g_BalloonInfo.bEnable = FALSE;
  1680. return;
  1681. }
  1682. iconData.uFlags = iconData.uFlags | NIF_ICON;
  1683. iconData.hIcon = g_Icons[g_CurrentIcon].hIcon;
  1684. _tcscpy(iconData.szTip, g_Icons[g_CurrentIcon].tszToolTip);
  1685. if(g_BalloonInfo.bEnable)
  1686. {
  1687. if(IsNotifyEnable(g_BalloonInfo.eState))
  1688. {
  1689. //
  1690. // Show balloon tooltip
  1691. //
  1692. iconData.uTimeout = g_Icons[g_BalloonInfo.eState].dwBalloonTimeout;
  1693. iconData.uFlags = iconData.uFlags | NIF_INFO;
  1694. iconData.dwInfoFlags = g_Icons[g_BalloonInfo.eState].dwBalloonIcon | NIIF_NOSOUND;
  1695. _tcscpy(iconData.szInfo, g_BalloonInfo.szInfo);
  1696. _tcscpy(iconData.szInfoTitle, g_BalloonInfo.szInfoTitle);
  1697. }
  1698. g_BalloonInfo.bEnable = FALSE;
  1699. }
  1700. if(g_BalloonInfo.bDelete)
  1701. {
  1702. //
  1703. // Destroy currently open balloon tooltip
  1704. //
  1705. iconData.uFlags = iconData.uFlags | NIF_INFO;
  1706. _tcscpy(iconData.szInfo, TEXT(""));
  1707. _tcscpy(iconData.szInfoTitle, TEXT(""));
  1708. g_BalloonInfo.bDelete = FALSE;
  1709. }
  1710. Shell_NotifyIcon(g_bIconAdded ? NIM_MODIFY : NIM_ADD, &iconData);
  1711. g_bIconAdded = TRUE;
  1712. } // EvaluateIcon
  1713. void
  1714. SetIconState(
  1715. eIconState eIcon,
  1716. BOOL bEnable,
  1717. TCHAR* ptsStatus /* = NULL */
  1718. )
  1719. /*++
  1720. Routine description:
  1721. Change notification bar icon state.
  1722. Arguments:
  1723. eIcon - icon type
  1724. bEnable - icon state (enable/disable)
  1725. ptsStatus - status string (optional)
  1726. Return Value:
  1727. none
  1728. --*/
  1729. {
  1730. DWORD dwRes;
  1731. DBG_ENTER(TEXT("SetIconState"),
  1732. TEXT("Icon id=%d, Enable=%d, Status=%s"),
  1733. eIcon,
  1734. bEnable,
  1735. ptsStatus);
  1736. ASSERTION (eIcon < ICONS_COUNT);
  1737. if(!bEnable && eIcon != ICON_RINGING)
  1738. {
  1739. //
  1740. // We're turning off a state - nothing special to do
  1741. //
  1742. goto exit;
  1743. }
  1744. TCHAR tsFormat[MAX_PATH]= {0};
  1745. LPCTSTR strParam = NULL;
  1746. DWORD dwStringResId = 0;
  1747. switch(eIcon)
  1748. {
  1749. case ICON_RINGING:
  1750. if(bEnable)
  1751. {
  1752. //
  1753. // Sound, Balloon, and Animation
  1754. //
  1755. SetIconState(ICON_SENDING, FALSE);
  1756. SetIconState(ICON_RECEIVING, FALSE);
  1757. g_BalloonInfo.bEnable = TRUE;
  1758. g_BalloonInfo.eState = eIcon;
  1759. //
  1760. // Compose the balloon tooltip
  1761. //
  1762. strParam = NULL;
  1763. dwStringResId = IDS_INCOMING_CALL;
  1764. if(_tcslen(g_szAddress))
  1765. {
  1766. //
  1767. // Caller id is known - use it in formatted string
  1768. //
  1769. strParam = g_szAddress;
  1770. dwStringResId = IDS_INCOMING_CALL_FROM;
  1771. }
  1772. if (ERROR_SUCCESS != LoadAndFormatString(dwStringResId, tsFormat, ARR_SIZE(tsFormat), strParam))
  1773. {
  1774. return;
  1775. }
  1776. _tcsncpy(g_BalloonInfo.szInfoTitle, tsFormat, MAX_BALLOON_TITLE_LEN-1);
  1777. if (ERROR_SUCCESS != LoadAndFormatString(IDS_CLICK_TO_ANSWER,
  1778. g_BalloonInfo.szInfo,
  1779. ARR_SIZE(g_BalloonInfo.szInfo)))
  1780. {
  1781. return;
  1782. }
  1783. //
  1784. // Set tooltip
  1785. //
  1786. _sntprintf(g_Icons[eIcon].tszToolTip,
  1787. TOOLTIP_SIZE-1,
  1788. TEXT("%s\n%s"),
  1789. tsFormat,
  1790. g_BalloonInfo.szInfo);
  1791. if(!g_uRingTimerID)
  1792. {
  1793. //
  1794. // Set animation timer
  1795. //
  1796. g_uRingTimerID = SetTimer(NULL, 0, RING_ANIMATION_FRAME_DELAY, RingTimerProc);
  1797. if(!g_uRingTimerID)
  1798. {
  1799. dwRes = GetLastError();
  1800. CALL_FAIL (GENERAL_ERR, TEXT ("SetTimer"), dwRes);
  1801. }
  1802. else
  1803. {
  1804. g_dwRingAnimationStartTick = GetTickCount();
  1805. }
  1806. }
  1807. }
  1808. else // disable ringing
  1809. {
  1810. if(g_Icons[eIcon].bEnable)
  1811. {
  1812. //
  1813. // Remove ringing balloon
  1814. //
  1815. g_BalloonInfo.bDelete = TRUE;
  1816. }
  1817. if(g_uRingTimerID)
  1818. {
  1819. //
  1820. // kill animation timer
  1821. //
  1822. if(!KillTimer(NULL, g_uRingTimerID))
  1823. {
  1824. dwRes = GetLastError();
  1825. CALL_FAIL (GENERAL_ERR, TEXT ("KillTimer"), dwRes);
  1826. }
  1827. g_uRingTimerID = 0;
  1828. g_dwRingAnimationStartTick = 0;
  1829. }
  1830. }
  1831. break;
  1832. case ICON_SENDING:
  1833. //
  1834. // Compose tooltip
  1835. //
  1836. if (ERROR_SUCCESS != LoadAndFormatString (IDS_SENDING_TO, tsFormat, ARR_SIZE(tsFormat), g_szRemoteId))
  1837. {
  1838. return;
  1839. }
  1840. _sntprintf(g_Icons[eIcon].tszToolTip,
  1841. TOOLTIP_SIZE-1,
  1842. TEXT("%s\n%s"),
  1843. tsFormat,
  1844. ptsStatus ? ptsStatus : TEXT(""));
  1845. if(!g_Icons[eIcon].bEnable)
  1846. {
  1847. //
  1848. // Turn the icon on
  1849. //
  1850. SetIconState(ICON_RINGING, FALSE);
  1851. SetIconState(ICON_RECEIVING, FALSE);
  1852. //
  1853. // Open fax monitor
  1854. //
  1855. if(g_ConfigOptions.bMonitorOnSend)
  1856. {
  1857. dwRes = OpenFaxMonitor();
  1858. if(ERROR_SUCCESS != dwRes)
  1859. {
  1860. CALL_FAIL (GENERAL_ERR, TEXT ("OpenFaxMonitor"), dwRes);
  1861. }
  1862. }
  1863. }
  1864. break;
  1865. case ICON_RECEIVING:
  1866. //
  1867. // Compose tooltip
  1868. //
  1869. strParam = NULL;
  1870. dwStringResId = IDS_RECEIVING;
  1871. if(_tcslen(g_szRemoteId))
  1872. {
  1873. strParam = g_szRemoteId;
  1874. dwStringResId = IDS_RECEIVING_FROM;
  1875. }
  1876. if (ERROR_SUCCESS != LoadAndFormatString (dwStringResId, tsFormat, ARR_SIZE(tsFormat), strParam))
  1877. {
  1878. return;
  1879. }
  1880. _sntprintf(g_Icons[eIcon].tszToolTip,
  1881. TOOLTIP_SIZE-1,
  1882. TEXT("%s\n%s"),
  1883. tsFormat,
  1884. ptsStatus ? ptsStatus : TEXT(""));
  1885. if(!g_Icons[eIcon].bEnable)
  1886. {
  1887. //
  1888. // Turn the icon on
  1889. //
  1890. SetIconState(ICON_RINGING, FALSE);
  1891. SetIconState(ICON_SENDING, FALSE);
  1892. //
  1893. // open fax monitor
  1894. //
  1895. if(g_ConfigOptions.bMonitorOnReceive)
  1896. {
  1897. dwRes = OpenFaxMonitor();
  1898. if(ERROR_SUCCESS != dwRes)
  1899. {
  1900. CALL_FAIL (GENERAL_ERR, TEXT ("OpenFaxMonitor"), dwRes);
  1901. }
  1902. }
  1903. }
  1904. break;
  1905. case ICON_SEND_FAILED:
  1906. //
  1907. // Compose tooltip
  1908. //
  1909. if (ERROR_SUCCESS != LoadAndFormatString (IDS_SEND_ERROR_BALLOON, tsFormat, ARR_SIZE(tsFormat), g_szRemoteId))
  1910. {
  1911. return;
  1912. }
  1913. _sntprintf(g_Icons[eIcon].tszToolTip,
  1914. TOOLTIP_SIZE-1,
  1915. TEXT("%s\n%s"),
  1916. tsFormat,
  1917. ptsStatus ? ptsStatus : TEXT(""));
  1918. if(!g_Icons[eIcon].bEnable)
  1919. {
  1920. //
  1921. // Turn the icon on
  1922. //
  1923. if(g_ConfigOptions.bSoundOnError)
  1924. {
  1925. if(!PlaySound(g_Icons[eIcon].pctsSound, NULL, SND_ASYNC | SND_APPLICATION | SND_NODEFAULT))
  1926. {
  1927. CALL_FAIL (WINDOW_ERR, TEXT ("PlaySound"), 0);
  1928. }
  1929. }
  1930. g_BalloonInfo.bEnable = TRUE;
  1931. g_BalloonInfo.eState = eIcon;
  1932. //
  1933. // Compose the balloon
  1934. //
  1935. _tcsncpy(g_BalloonInfo.szInfoTitle, tsFormat, MAX_BALLOON_TITLE_LEN-1);
  1936. _tcsncpy(g_BalloonInfo.szInfo, ptsStatus ? ptsStatus : TEXT(""), MAX_BALLOON_TEXT_LEN-1);
  1937. }
  1938. break;
  1939. case ICON_RECEIVE_FAILED:
  1940. //
  1941. // Compose tooltip
  1942. //
  1943. strParam = NULL;
  1944. dwStringResId = IDS_RCV_ERROR_BALLOON;
  1945. if(_tcslen(g_szRemoteId))
  1946. {
  1947. strParam = g_szRemoteId;
  1948. dwStringResId = IDS_RCV_FROM_ERROR_BALLOON;
  1949. }
  1950. if (ERROR_SUCCESS != LoadAndFormatString (dwStringResId, tsFormat, ARR_SIZE(tsFormat), strParam))
  1951. {
  1952. return;
  1953. }
  1954. _sntprintf(g_Icons[eIcon].tszToolTip,
  1955. TOOLTIP_SIZE-1,
  1956. TEXT("%s\n%s"),
  1957. tsFormat,
  1958. ptsStatus ? ptsStatus : TEXT(""));
  1959. if(!g_Icons[eIcon].bEnable)
  1960. {
  1961. //
  1962. // Turn the icon on
  1963. //
  1964. if(g_ConfigOptions.bSoundOnError)
  1965. {
  1966. if(!PlaySound(g_Icons[eIcon].pctsSound, NULL, SND_ASYNC | SND_APPLICATION | SND_NODEFAULT))
  1967. {
  1968. CALL_FAIL (WINDOW_ERR, TEXT ("PlaySound"), 0);
  1969. }
  1970. }
  1971. g_BalloonInfo.bEnable = TRUE;
  1972. g_BalloonInfo.eState = eIcon;
  1973. //
  1974. // Compose the balloon
  1975. //
  1976. _tcsncpy(g_BalloonInfo.szInfoTitle, tsFormat, MAX_BALLOON_TITLE_LEN-1);
  1977. _tcsncpy(g_BalloonInfo.szInfo, ptsStatus ? ptsStatus : TEXT(""), MAX_BALLOON_TEXT_LEN-1);
  1978. }
  1979. break;
  1980. case ICON_NEW_FAX:
  1981. //
  1982. // Compose tooltip
  1983. //
  1984. strParam = NULL;
  1985. dwStringResId = IDS_NEW_FAX_BALLOON;
  1986. if(_tcslen(g_szRemoteId))
  1987. {
  1988. strParam = g_szRemoteId;
  1989. dwStringResId = IDS_NEW_FAX_FROM_BALLOON;
  1990. }
  1991. if (ERROR_SUCCESS != LoadAndFormatString (dwStringResId, tsFormat, ARR_SIZE(tsFormat), strParam))
  1992. {
  1993. return;
  1994. }
  1995. if (ERROR_SUCCESS != LoadAndFormatString (IDS_CLICK_TO_VIEW,
  1996. g_BalloonInfo.szInfo,
  1997. ARR_SIZE(g_BalloonInfo.szInfo)))
  1998. {
  1999. return;
  2000. }
  2001. _sntprintf(g_Icons[eIcon].tszToolTip,
  2002. TOOLTIP_SIZE-1,
  2003. TEXT("%s\n%s"),
  2004. tsFormat,
  2005. g_BalloonInfo.szInfo);
  2006. if(!g_Icons[eIcon].bEnable)
  2007. {
  2008. //
  2009. // Turn the icon on
  2010. //
  2011. if (g_ConfigOptions.bSoundOnReceive)
  2012. {
  2013. if(!PlaySound(g_Icons[eIcon].pctsSound, NULL, SND_ASYNC | SND_APPLICATION | SND_NODEFAULT))
  2014. {
  2015. CALL_FAIL (WINDOW_ERR, TEXT ("PlaySound"), 0);
  2016. }
  2017. }
  2018. g_BalloonInfo.bEnable = TRUE;
  2019. g_BalloonInfo.eState = eIcon;
  2020. //
  2021. // Compose the balloon
  2022. //
  2023. _tcsncpy(g_BalloonInfo.szInfoTitle, tsFormat, MAX_BALLOON_TITLE_LEN-1);
  2024. }
  2025. break;
  2026. case ICON_SEND_SUCCESS:
  2027. if(!g_Icons[eIcon].bEnable)
  2028. {
  2029. //
  2030. // Turn the icon on
  2031. //
  2032. if(g_ConfigOptions.bSoundOnSent)
  2033. {
  2034. if(!PlaySound(g_Icons[eIcon].pctsSound, NULL, SND_ASYNC | SND_APPLICATION | SND_NODEFAULT))
  2035. {
  2036. CALL_FAIL (WINDOW_ERR, TEXT ("PlaySound"), 0);
  2037. }
  2038. }
  2039. g_BalloonInfo.bEnable = TRUE;
  2040. g_BalloonInfo.eState = eIcon;
  2041. //
  2042. // Compose the balloon
  2043. //
  2044. if (ERROR_SUCCESS != LoadAndFormatString (IDS_SEND_OK, tsFormat, ARR_SIZE(tsFormat)))
  2045. {
  2046. return;
  2047. }
  2048. _tcsncpy(g_BalloonInfo.szInfoTitle, tsFormat, MAX_BALLOON_TITLE_LEN-1);
  2049. if (ERROR_SUCCESS != LoadAndFormatString (IDS_SEND_OK_BALLOON,
  2050. g_BalloonInfo.szInfo,
  2051. ARR_SIZE(g_BalloonInfo.szInfo),
  2052. g_szRemoteId))
  2053. {
  2054. return;
  2055. }
  2056. }
  2057. break;
  2058. default:
  2059. break;
  2060. }
  2061. exit:
  2062. g_Icons[eIcon].bEnable = bEnable;
  2063. g_Icons[eIcon].tszToolTip[TOOLTIP_SIZE -1] = _T('\0');
  2064. EvaluateIcon();
  2065. } // SetIconState
  2066. VOID
  2067. CALLBACK
  2068. RingTimerProc(
  2069. HWND hwnd, // handle to window
  2070. UINT uMsg, // WM_TIMER message
  2071. UINT_PTR idEvent, // timer identifier
  2072. DWORD dwTime // current system time
  2073. )
  2074. /*++
  2075. Routine description:
  2076. Animate ringing icon
  2077. Arguments:
  2078. hwnd - handle to window
  2079. uMsg - WM_TIMER message
  2080. idEvent - timer identifier
  2081. dwTime - current system time
  2082. Return Value:
  2083. none
  2084. --*/
  2085. {
  2086. DBG_ENTER(TEXT("RingTimerProc"));
  2087. if ((GetTickCount() - g_dwRingAnimationStartTick) > RING_ANIMATION_TIMEOUT)
  2088. {
  2089. //
  2090. // Animation has expired - keep static icon
  2091. //
  2092. g_Icons[ICON_RINGING].hIcon = g_RingIcons[0].hIcon;
  2093. if(!KillTimer(NULL, g_uRingTimerID))
  2094. {
  2095. CALL_FAIL (GENERAL_ERR, TEXT ("KillTimer"), GetLastError());
  2096. }
  2097. g_uRingTimerID = 0;
  2098. g_dwRingAnimationStartTick = 0;
  2099. }
  2100. else
  2101. {
  2102. g_dwCurrRingIconIndex = (g_dwCurrRingIconIndex + 1) % RING_ICONS_NUM;
  2103. g_Icons[ICON_RINGING].hIcon = g_RingIcons[g_dwCurrRingIconIndex].hIcon;
  2104. }
  2105. EvaluateIcon();
  2106. } // RingTimerProc
  2107. VOID
  2108. InvokeClientConsole ()
  2109. /*++
  2110. Routine description:
  2111. Invoke Client Console
  2112. Arguments:
  2113. none
  2114. Return Value:
  2115. none
  2116. --*/
  2117. {
  2118. DBG_ENTER(TEXT("InvokeClientConsole"));
  2119. TCHAR szCmdLine[MAX_PATH];
  2120. static TCHAR szFmtMsg[] = TEXT(" -folder %s -MessageId %I64x");
  2121. static TCHAR szFmtNoMsg[] = TEXT(" -folder %s");
  2122. DWORDLONG dwlMsgId = 0;
  2123. LPCWSTR lpcwstrFolder = TEXT("");
  2124. switch (g_CurrentIcon)
  2125. {
  2126. case ICON_RINGING: // Line is ringing - nothing special to do
  2127. case ICON_RECEIVE_FAILED: // Receive operation failed - nothing special to do
  2128. default: // Any other icon state - nothing special to do
  2129. break;
  2130. case ICON_SENDING:
  2131. //
  2132. // Device is sending - open fax console in Outbox folder
  2133. //
  2134. dwlMsgId = g_dwlCurrentMsgID;
  2135. lpcwstrFolder = CONSOLE_CMD_PRM_STR_OUTBOX;
  2136. break;
  2137. case ICON_SEND_FAILED:
  2138. //
  2139. // Send operation failed - open fax console in Outbox folder
  2140. //
  2141. dwlMsgId = g_dwlSendFailedMsgId;
  2142. lpcwstrFolder = CONSOLE_CMD_PRM_STR_OUTBOX;
  2143. break;
  2144. case ICON_RECEIVING:
  2145. //
  2146. // Device is receiving - open fax console in Incoming folder
  2147. //
  2148. dwlMsgId = g_dwlCurrentMsgID;
  2149. lpcwstrFolder = CONSOLE_CMD_PRM_STR_INCOMING;
  2150. break;
  2151. break;
  2152. case ICON_NEW_FAX:
  2153. //
  2154. // New unread fax - open fax console in Inbox folder
  2155. //
  2156. dwlMsgId = g_dwlNewMsgId;
  2157. lpcwstrFolder = CONSOLE_CMD_PRM_STR_INBOX;
  2158. break;
  2159. case ICON_SEND_SUCCESS:
  2160. //
  2161. // Send was successful - open fax console in Sent Items folder
  2162. //
  2163. dwlMsgId = g_dwlSendSuccessMsgId;
  2164. lpcwstrFolder = CONSOLE_CMD_PRM_STR_SENT_ITEMS;
  2165. break;
  2166. }
  2167. if (dwlMsgId)
  2168. {
  2169. wsprintf (szCmdLine, szFmtMsg, lpcwstrFolder, dwlMsgId);
  2170. }
  2171. else
  2172. {
  2173. wsprintf (szCmdLine, szFmtNoMsg, lpcwstrFolder);
  2174. }
  2175. HINSTANCE hRes;
  2176. hRes = ShellExecute(g_hWndFaxNotify,
  2177. NULL,
  2178. FAX_CLIENT_CONSOLE_IMAGE_NAME,
  2179. szCmdLine,
  2180. NULL,
  2181. SW_SHOW);
  2182. if((DWORD_PTR)hRes <= 32)
  2183. {
  2184. //
  2185. // error
  2186. //
  2187. CALL_FAIL (GENERAL_ERR, TEXT("ShellExecute"), PtrToUlong(hRes));
  2188. }
  2189. } // InvokeClientConsole
  2190. VOID
  2191. AnswerTheCall ()
  2192. /*++
  2193. Routine description:
  2194. Answer the current incoming call
  2195. Arguments:
  2196. none
  2197. Return Value:
  2198. none
  2199. --*/
  2200. {
  2201. DBG_ENTER(TEXT("AnswerTheCall"));
  2202. DWORD dwDeviceId;
  2203. //
  2204. // Check for 'Answer now' capabilities and auto-detect the device id.
  2205. //
  2206. DWORD dwRes = CheckAnswerNowCapability (TRUE, // Start service if necessary
  2207. &dwDeviceId); // Get device id for FaxAnswerCall
  2208. if (ERROR_SUCCESS != dwRes)
  2209. {
  2210. //
  2211. // Can't 'Answer Now' - dwRes has the string resource id for the message to show to the user.
  2212. //
  2213. FaxMessageBox (g_hMonitorDlg, dwRes, MB_OK | MB_ICONEXCLAMATION);
  2214. return;
  2215. }
  2216. //
  2217. // Reset remote ID
  2218. //
  2219. _tcscpy(g_szRemoteId, TEXT(""));
  2220. //
  2221. // Looks like we have a chance of FaxAnswerCall succeeding - let's try it.
  2222. // First, open the monitor (or make sure it's already open).
  2223. //
  2224. OpenFaxMonitor ();
  2225. //
  2226. // Start by disabling the 'Answer Now' button on the monitor dialog
  2227. //
  2228. if (g_hMonitorDlg)
  2229. {
  2230. //
  2231. // Monitor dialog is there
  2232. //
  2233. HWND hWndAnswerNow = GetDlgItem(g_hMonitorDlg, IDC_DISCONNECT);
  2234. if(hWndAnswerNow)
  2235. {
  2236. EnableWindow(hWndAnswerNow, FALSE);
  2237. }
  2238. }
  2239. //
  2240. // Call is gone
  2241. //
  2242. g_hCall = NULL;
  2243. SetIconState(ICON_RINGING, FALSE, TEXT(""));
  2244. if(!FaxAnswerCall(g_hFaxSvcHandle, dwDeviceId))
  2245. {
  2246. CALL_FAIL (RPC_ERR, TEXT ("FaxAnswerCall"), GetLastError());
  2247. FaxMessageBox(g_hWndFaxNotify, IDS_CANNOT_ANSWER, MB_OK | MB_ICONEXCLAMATION);
  2248. SetStatusMonitorDeviceState (FAX_IDLE);
  2249. }
  2250. else
  2251. {
  2252. g_tszLastEvent[0] = TEXT('\0');
  2253. SetStatusMonitorDeviceState(FAX_RECEIVING);
  2254. }
  2255. } // AnswerTheCall
  2256. VOID
  2257. FaxPrinterProperties(DWORD dwPage)
  2258. /*++
  2259. Routine description:
  2260. Open Fax Printer Property Sheet
  2261. Arguments:
  2262. dwPage - page number
  2263. Return Value:
  2264. none
  2265. --*/
  2266. {
  2267. DBG_ENTER(TEXT("FaxPrinterProperties"));
  2268. //
  2269. // open fax printer properties on the Tracking page
  2270. //
  2271. TCHAR tsPrinter[MAX_PATH];
  2272. typedef VOID (*PRINTER_PROP_PAGES_PROC)(HWND, LPCTSTR, INT, LPARAM);
  2273. HMODULE hPrintUI = NULL;
  2274. PRINTER_PROP_PAGES_PROC fpPrnPropPages = NULL;
  2275. if(!GetFirstLocalFaxPrinterName(tsPrinter, MAX_PATH))
  2276. {
  2277. CALL_FAIL (GENERAL_ERR, TEXT ("GetFirstLocalFaxPrinterName"), GetLastError());
  2278. return;
  2279. }
  2280. hPrintUI = LoadLibrary(TEXT("printui.dll"));
  2281. if(!hPrintUI)
  2282. {
  2283. CALL_FAIL (GENERAL_ERR, TEXT ("LoadLibrary(printui.dll)"), GetLastError());
  2284. return;
  2285. }
  2286. fpPrnPropPages = (PRINTER_PROP_PAGES_PROC)GetProcAddress(hPrintUI, "vPrinterPropPages");
  2287. if(fpPrnPropPages)
  2288. {
  2289. fpPrnPropPages(g_hWndFaxNotify, tsPrinter, SW_SHOWNORMAL, dwPage);
  2290. }
  2291. else
  2292. {
  2293. CALL_FAIL (GENERAL_ERR, TEXT ("GetProcAddress(vPrinterPropPages)"), GetLastError());
  2294. }
  2295. FreeLibrary(hPrintUI);
  2296. } // FaxPrinterProperties
  2297. VOID
  2298. DoFaxContextMenu (HWND hwnd)
  2299. /*++
  2300. Routine description:
  2301. Popup and handle context menu
  2302. Arguments:
  2303. hwnd - notification window handle
  2304. Return Value:
  2305. none
  2306. --*/
  2307. {
  2308. DBG_ENTER(TEXT("DoFaxContextMenu"));
  2309. POINT pt;
  2310. HMENU hm = LoadMenu (g_hResource, MAKEINTRESOURCE (IDM_FAX_MENU));
  2311. HMENU hmPopup = GetSubMenu(hm, 0);
  2312. if (!g_Icons[ICON_RINGING].bEnable)
  2313. {
  2314. RemoveMenu (hmPopup, ID_ANSWER_CALL, MF_BYCOMMAND);
  2315. }
  2316. if(g_dwCurrentJobID == 0)
  2317. {
  2318. RemoveMenu (hmPopup, ID_DISCONNECT_CALL, MF_BYCOMMAND);
  2319. }
  2320. if(!g_Icons[ICON_RINGING].bEnable && g_dwCurrentJobID == 0)
  2321. {
  2322. //
  2323. // delete the menu separator
  2324. //
  2325. DeleteMenu(hmPopup, 0, MF_BYPOSITION);
  2326. }
  2327. SetMenuDefaultItem(hmPopup, ID_FAX_QUEUE, FALSE);
  2328. GetCursorPos (&pt);
  2329. SetForegroundWindow(hwnd);
  2330. INT idCmd = TrackPopupMenu (GetSubMenu(hm, 0),
  2331. TPM_RETURNCMD | TPM_NONOTIFY,
  2332. pt.x, pt.y,
  2333. 0, hwnd, NULL);
  2334. switch (idCmd)
  2335. {
  2336. case ID_ICON_PROPERTIES:
  2337. FaxPrinterProperties(IsSimpleUI() ? 3 : 5);
  2338. break;
  2339. case ID_FAX_QUEUE:
  2340. InvokeClientConsole ();
  2341. break;
  2342. case ID_ANSWER_CALL:
  2343. AnswerTheCall ();
  2344. break;
  2345. case ID_FAX_MONITOR:
  2346. OpenFaxMonitor ();
  2347. break;
  2348. case ID_DISCONNECT_CALL:
  2349. OnDisconnect();
  2350. break;
  2351. }
  2352. if (hm)
  2353. {
  2354. DestroyMenu (hm);
  2355. }
  2356. } // DoFaxContextMenu
  2357. VOID
  2358. OnTrayCallback (HWND hwnd, WPARAM wp, LPARAM lp)
  2359. /*++
  2360. Routine description:
  2361. Handle messages from the notification icon
  2362. Arguments:
  2363. hwnd - notification window handle
  2364. wp - message parameter
  2365. lp - message parameter
  2366. Return Value:
  2367. none
  2368. --*/
  2369. {
  2370. DBG_ENTER(TEXT("OnTrayCallback"), TEXT("hWnd=%08x, wParam=%08x, lParam=%08x"), hwnd, wp, lp);
  2371. switch (lp)
  2372. {
  2373. case NIN_BALLOONUSERCLICK: // User clicked balloon or (WM_USER + 5 = 1029)
  2374. case WM_LBUTTONDOWN: // User pressed icon (513)
  2375. {
  2376. //
  2377. // Our behavior depends on the icon currently being displyed
  2378. //
  2379. switch (g_CurrentIcon)
  2380. {
  2381. case ICON_RINGING:
  2382. //
  2383. // Device is ringing - answer the call
  2384. //
  2385. AnswerTheCall ();
  2386. break;
  2387. case ICON_NEW_FAX: // New unread fax - open fax console in Inbox folder
  2388. case ICON_SEND_SUCCESS: // Send was successful - open fax console in Sent Items folder
  2389. case ICON_SEND_FAILED: // Send operation failed - open fax console in Outbox folder
  2390. //
  2391. // Turn off the current icon state
  2392. //
  2393. InvokeClientConsole ();
  2394. SetIconState(g_CurrentIcon, FALSE);
  2395. break;
  2396. case ICON_SENDING: // Device is sending - open fax console in Outbox folder
  2397. case ICON_RECEIVING: // Device is receiving - open fax console in Incoming folder
  2398. InvokeClientConsole ();
  2399. break;
  2400. case ICON_RECEIVE_FAILED:
  2401. //
  2402. // Receive operation failed
  2403. //
  2404. SetIconState(g_CurrentIcon, FALSE);
  2405. break;
  2406. default:
  2407. //
  2408. // When balloon is opened and the user clicks on the icon we get two notifications
  2409. // NIN_BALLOONUSERCLICK and WM_LBUTTONDOWN. The first one reset the icon state and the second do nothing.
  2410. //
  2411. break;
  2412. }
  2413. }
  2414. //
  2415. // no break ==> fall-through
  2416. //
  2417. case NIN_BALLOONTIMEOUT:
  2418. if (g_BalloonInfo.eState == ICON_RECEIVE_FAILED ||
  2419. g_BalloonInfo.eState == ICON_SEND_SUCCESS)
  2420. {
  2421. SetIconState(g_BalloonInfo.eState, FALSE);
  2422. }
  2423. g_BalloonInfo.eState = ICON_IDLE;
  2424. break;
  2425. case WM_RBUTTONDOWN:
  2426. DoFaxContextMenu (hwnd);
  2427. break;
  2428. }
  2429. } // OnTrayCallback
  2430. BOOL
  2431. IsUserGrantedAccess(
  2432. DWORD dwAccess
  2433. )
  2434. {
  2435. BOOL bRes = FALSE;
  2436. DBG_ENTER(TEXT("IsUserGrantedAccess"), bRes, TEXT("%d"), dwAccess);
  2437. if (!g_hFaxSvcHandle)
  2438. {
  2439. //
  2440. // Not connected - no rights
  2441. //
  2442. return bRes;
  2443. }
  2444. if (dwAccess == (g_ConfigOptions.dwAccessRights & dwAccess))
  2445. {
  2446. bRes = TRUE;
  2447. }
  2448. return bRes;
  2449. } // IsUserGrantedAccess
  2450. DWORD
  2451. CheckAnswerNowCapability (
  2452. BOOL bForceReconnect,
  2453. LPDWORD lpdwDeviceId /* = NULL */
  2454. )
  2455. /*++
  2456. Routine name : CheckAnswerNowCapability
  2457. Routine description:
  2458. Checks if the 'Answer Now' option can be used
  2459. Author:
  2460. Eran Yariv (EranY), Mar, 2001
  2461. Arguments:
  2462. bForceReconnect [in] - If the service is down, should we bring it up now?
  2463. lpdwDeviceId [out] - The device id to use when calling FaxAnswerCall.
  2464. If the Manual-Answer-Device is ringing, we use the Manual-Answer-Device id.
  2465. Otherwise, it's the monitored device id. (Optional)
  2466. Return Value:
  2467. ERROR_SUCCESS if the 'Answer Now' can be used.
  2468. Othewise, returns a string resource id that can be used in a message box to tell the user
  2469. why 'Answer Now' is not available.
  2470. --*/
  2471. {
  2472. DWORD dwRes = ERROR_SUCCESS;
  2473. DBG_ENTER(TEXT("CheckAnswerNowCapability"), dwRes);
  2474. //
  2475. // First, let's see if we're connected to the local server
  2476. //
  2477. if (NULL == g_hFaxSvcHandle)
  2478. {
  2479. //
  2480. // Service is down
  2481. //
  2482. if (!bForceReconnect)
  2483. {
  2484. //
  2485. // We assume the user can 'Answer now'
  2486. //
  2487. ASSERTION (NULL == lpdwDeviceId);
  2488. return dwRes;
  2489. }
  2490. //
  2491. // Try to start up the local fax service
  2492. //
  2493. if (!Connect())
  2494. {
  2495. //
  2496. // Couldn't start up the service
  2497. //
  2498. dwRes = GetLastError ();
  2499. CALL_FAIL (GENERAL_ERR, TEXT("Connect"), dwRes);
  2500. dwRes = IDS_ERR_CANT_TALK_TO_SERVICE;
  2501. return dwRes;
  2502. }
  2503. //
  2504. // Now that the service is up - we need to connect.
  2505. // Send a message to the main window to bring up the connection.
  2506. //
  2507. if (!SendMessage (g_hWndFaxNotify, WM_FAX_STARTED, 0, 0))
  2508. {
  2509. //
  2510. // Failed to connect
  2511. //
  2512. dwRes = IDS_ERR_CANT_TALK_TO_SERVICE;
  2513. return dwRes;
  2514. }
  2515. //
  2516. // Now we're connected !!!
  2517. //
  2518. }
  2519. if (!IsUserGrantedAccess (FAX_ACCESS_QUERY_IN_ARCHIVE))
  2520. {
  2521. //
  2522. // User can't receive-now
  2523. //
  2524. dwRes = IDS_ERR_ANSWER_ACCESS_DENIED;
  2525. return dwRes;
  2526. }
  2527. if (0 == g_ConfigOptions.dwMonitorDeviceId)
  2528. {
  2529. //
  2530. // No devices
  2531. //
  2532. dwRes = IDS_ERR_NO_DEVICES;
  2533. return dwRes;
  2534. }
  2535. if (g_hCall)
  2536. {
  2537. //
  2538. // The Manual-Answer-Device is ringing, we use the Manual-Answer-Device id.
  2539. //
  2540. ASSERTION (g_ConfigOptions.dwManualAnswerDeviceId);
  2541. if (lpdwDeviceId)
  2542. {
  2543. *lpdwDeviceId = g_ConfigOptions.dwManualAnswerDeviceId;
  2544. }
  2545. return dwRes;
  2546. }
  2547. //
  2548. // The Manual-Answer-Device is NOT ringing; we should receive on the monitored device
  2549. //
  2550. if ((0 != g_dwCurrentJobID) || (FAX_IDLE != g_devState))
  2551. {
  2552. //
  2553. // There's a job on the monitored device
  2554. //
  2555. dwRes = IDS_ERR_DEVICE_BUSY;
  2556. return dwRes;
  2557. }
  2558. //
  2559. // One last check - is the monitored device virtual?
  2560. //
  2561. BOOL bVirtual;
  2562. dwRes = IsDeviceVirtual (g_hFaxSvcHandle, g_ConfigOptions.dwMonitorDeviceId, &bVirtual);
  2563. if (ERROR_SUCCESS != dwRes)
  2564. {
  2565. //
  2566. // Can't tell - assume virtual
  2567. //
  2568. bVirtual = TRUE;
  2569. }
  2570. if (bVirtual)
  2571. {
  2572. //
  2573. // Sorry, manual answering on virtual devices is NOT supported
  2574. //
  2575. dwRes = IDS_ERROR_VIRTUAL_DEVICE;
  2576. return dwRes;
  2577. }
  2578. //
  2579. // It's ok to call FaxAnswerCall on the monitored device
  2580. //
  2581. if (lpdwDeviceId)
  2582. {
  2583. *lpdwDeviceId = g_ConfigOptions.dwMonitorDeviceId;
  2584. }
  2585. return dwRes;
  2586. } // CheckAnswerNowCapability
  2587. LRESULT
  2588. CALLBACK
  2589. NotifyWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
  2590. /*++
  2591. Routine description:
  2592. Notification window procedure
  2593. Arguments:
  2594. hwnd - notification window handle
  2595. msg - message ID
  2596. wp - message parameter
  2597. lp - message parameter
  2598. Return Value:
  2599. result
  2600. --*/
  2601. {
  2602. switch (msg)
  2603. {
  2604. case WM_CREATE:
  2605. break;
  2606. case WM_FAX_STARTED:
  2607. //
  2608. // We get this message after service startup event
  2609. //
  2610. return RegisterForServerEvents();
  2611. case WM_TRAYCALLBACK:
  2612. OnTrayCallback (hwnd, wp, lp);
  2613. break;
  2614. case WM_FAX_EVENT:
  2615. #ifndef DEBUG
  2616. try
  2617. {
  2618. #endif
  2619. OnFaxEvent ((FAX_EVENT_EX*)lp);
  2620. #ifndef DEBUG
  2621. }
  2622. catch(...)
  2623. {
  2624. //
  2625. // Do not handle the exception for the debug version
  2626. //
  2627. DBG_ENTER(TEXT("NotifyWndProc"));
  2628. CALL_FAIL (GENERAL_ERR, TEXT("OnFaxEvent"), 0);
  2629. return 0;
  2630. }
  2631. #endif
  2632. return 0;
  2633. case WM_FAXSTAT_CONTROLPANEL:
  2634. //
  2635. // configuration has been changed
  2636. //
  2637. GetConfiguration ();
  2638. EvaluateIcon();
  2639. UpdateMonitorData(g_hMonitorDlg);
  2640. return 0;
  2641. case WM_FAXSTAT_OPEN_MONITOR:
  2642. OpenFaxMonitor ();
  2643. return 0;
  2644. case WM_FAXSTAT_INBOX_VIEWED:
  2645. //
  2646. // Client Console Inbox has been viewed
  2647. //
  2648. SetIconState(ICON_NEW_FAX, FALSE);
  2649. return 0;
  2650. case WM_FAXSTAT_OUTBOX_VIEWED:
  2651. //
  2652. // Client Console Outbox has been viewed
  2653. //
  2654. SetIconState(ICON_SEND_FAILED, FALSE);
  2655. return 0;
  2656. case WM_FAXSTAT_RECEIVE_NOW:
  2657. //
  2658. // Start receiving now
  2659. //
  2660. AnswerTheCall ();
  2661. return 0;
  2662. case WM_FAXSTAT_PRINTER_PROPERTY:
  2663. //
  2664. // Open Fax Printer Property Sheet
  2665. //
  2666. FaxPrinterProperties((DWORD)(wp));
  2667. return 0;
  2668. default:
  2669. break;
  2670. }
  2671. return CallWindowProc (DefWindowProc, hwnd, msg, wp, lp);
  2672. } // NotifyWndProc
  2673. VOID
  2674. CopyLTRString(
  2675. TCHAR* szDest,
  2676. LPCTSTR szSource,
  2677. DWORD dwSize)
  2678. /*++
  2679. Routine description:
  2680. Copy the string and add left-to-right Unicode control characters if needed
  2681. Arguments:
  2682. szDest - destination string
  2683. szSource - source string
  2684. dwSize - destination string maximum size in characters
  2685. Return Value:
  2686. none
  2687. --*/
  2688. {
  2689. DBG_ENTER(TEXT("CopyLTRString"));
  2690. if(!szDest)
  2691. {
  2692. ASSERTION_FAILURE;
  2693. return;
  2694. }
  2695. if(IsRTLUILanguage() && szSource && _tcslen(szSource))
  2696. {
  2697. //
  2698. // The string always should be LTR
  2699. // Add LEFT-TO-RIGHT OVERRIDE (LRO)
  2700. //
  2701. _sntprintf(szDest,
  2702. dwSize -1,
  2703. TEXT("%c%s%c"),
  2704. UNICODE_LRO,
  2705. szSource,
  2706. UNICODE_PDF);
  2707. szDest[dwSize -1] = _T('\0');
  2708. }
  2709. else
  2710. {
  2711. _tcsncpy(szDest,
  2712. szSource ? szSource : TEXT(""),
  2713. dwSize);
  2714. }
  2715. } // CopyLTRString