|
|
/**
Copyright (c) Microsoft Corporation 1999-2000
Module Name:
fxsst.cpp
Abstract:
This module implements the tray icon for fax. The purpose of the tray icon is to provide status and feedback to the fax user.
**/ #include <windows.h>
#include <faxreg.h>
#include <fxsapip.h>
#include <faxutil.h>
#include <shellapi.h>
#include <winspool.h>
#include <shlobj.h>
#include <Mmsystem.h>
#include <tchar.h>
#include <DebugEx.h>
#include <FaxRes.h>
#include "monitor.h"
#include "resource.h"
////////////////////////////////////////////////////////////
// Global data
//
//
// The following message ids are used for internal custom messages.
//
#define WM_FAX_STARTED (WM_USER + 204) // Message indicating the loca fax service is up and running
#define WM_TRAYCALLBACK (WM_USER + 205) // Notification bar icon callback message
#define WM_FAX_EVENT (WM_USER + 300) // Fax extended event message
#define TRAY_ICON_ID 12345 // Unique enough
HINSTANCE g_hModule = NULL; // DLL Global instance
HINSTANCE g_hResource = NULL; // Resource DLL handle
HANDLE g_hFaxSvcHandle = NULL; // Handle to the fax service (from FaxConnectFaxServer)
DWORDLONG g_dwlCurrentMsgID = 0; // ID of current message being monitored
DWORD g_dwCurrentJobID = 0; // ID of current queue job being monitored
HANDLE g_hServerStartupThread = NULL; // Handle of thread which waits for the server startup event
HANDLE g_hStopStartupThreadEvent = NULL; // Event for stop Server Startup Thread
BOOL g_bShuttingDown = FALSE; // Are we shutting down now?
HWND g_hWndFaxNotify = NULL; // Local (hidden) window handle
HANDLE g_hNotification = NULL; // Fax extended notification handle
HCALL g_hCall = NULL; // Handle to call (from FAX_EVENT_TYPE_NEW_CALL)
DWORDLONG g_dwlNewMsgId; // ID of the last incoming fax
DWORDLONG g_dwlSendFailedMsgId; // ID of the last outgoing failed fax
DWORDLONG g_dwlSendSuccessMsgId; // ID of the last successfully sent fax
TCHAR g_szAddress[MAX_PATH] = {0}; // Current caller ID or recipient number
TCHAR g_szRemoteId[MAX_PATH] = {0}; // Sender ID or Recipient ID
//
// Sender ID (receive):
// TSID or
// Caller ID or
// "unknown caller"
//
// Recipient ID (send):
// Recipient name or
// CSID or
// Recipient phone number.
//
BOOL g_bRecipientNameValid = FALSE; // TRUE if the g_szRecipientName has valid data
TCHAR g_szRecipientName[MAX_PATH] = {0}; // Keep the recipient name during sending
//
// Configuration options - read from the registry / Service
// Default values are set here.
//
CONFIG_OPTIONS g_ConfigOptions = {0};
//
// Notification bar icon states
//
typedef enum { ICON_RINGING=0, // Device is ringing
ICON_SENDING, // Device is sending
ICON_RECEIVING, // Device is receiving
ICON_SEND_FAILED, // Send operation failed
ICON_RECEIVE_FAILED, // Receive operation failed
ICON_NEW_FAX, // New unread fax
ICON_SEND_SUCCESS, // Send was successful
ICON_IDLE, // Don't display an icon
ICONS_COUNT // Number of icons we support
} eIconState;
eIconState g_CurrentIcon = ICONS_COUNT; // The index of the currently displayed icon
#define TOOLTIP_SIZE 128 // Number of characters in the tooltip
struct SIconState { BOOL bEnable; // Is the state active? (e.g. are there any new unread faxes?)
DWORD dwIconResId; // Resource id of the icon to use
HICON hIcon; // Handle to icon to use
LPCTSTR pctsSound; // Name of sound event
TCHAR tszToolTip[TOOLTIP_SIZE]; // Text to display in icon tooltip
DWORD dwBalloonTimeout; // Timeout of balloon (millisecs)
DWORD dwBalloonIcon; // The icon to display in the balloon. (see NIIF_* constants)
};
//
// Fax notification icon state array.
// Several states may have the bEnable flag on.
// The array is sorted by priority and EvaluateIcon() scans it looking
// for the first active state.
//
SIconState g_Icons[ICONS_COUNT] = { {FALSE, IDI_RINGING_1, NULL, TEXT("FaxLineRings"), TEXT(""), 30000, NIIF_INFO}, // ICON_RINGING
{FALSE, IDI_SENDING, NULL, TEXT(""), TEXT(""), 0, NIIF_INFO}, // ICON_SENDING
{FALSE, IDI_RECEIVING, NULL, TEXT(""), TEXT(""), 0, NIIF_INFO}, // ICON_RECEIVING
{FALSE, IDI_SEND_FAILED, NULL, TEXT("FaxError"), TEXT(""), 15000, NIIF_WARNING}, // ICON_SEND_FAILED
{FALSE, IDI_RECEIVE_FAILED, NULL, TEXT("FaxError"), TEXT(""), 15000, NIIF_WARNING}, // ICON_RECEIVE_FAILED
{FALSE, IDI_NEW_FAX, NULL, TEXT("FaxNew"), TEXT(""), 15000, NIIF_INFO}, // ICON_NEW_FAX
{FALSE, IDI_SEND_SUCCESS, NULL, TEXT("FaxSent"), TEXT(""), 10000, NIIF_INFO}, // ICON_SEND_SUCCESS
{FALSE, IDI_FAX_NORMAL, NULL, TEXT(""), TEXT(""), 0, NIIF_NONE} // ICON_IDLE
};
//
// Icons array for ringing animation
//
struct SRingIcon { HICON hIcon; // Handle to loaded icon
DWORD dwIconResId; // Resource ID of icon
};
#define RING_ICONS_NUM 4 // Number of frames (different icons) in ringing animation
#define RING_ANIMATION_FRAME_DELAY 300 // Delay (millisecs) between ring animation frames
#define RING_ANIMATION_TIMEOUT 10000 // Timeout (millisecs) of ring animation. When the timeout expires, the animation
// stops and the icon becomes static.
SRingIcon g_RingIcons[RING_ICONS_NUM] = { NULL, IDI_RINGING_1, NULL, IDI_RINGING_2, NULL, IDI_RINGING_3, NULL, IDI_RINGING_4 };
UINT_PTR g_uRingTimerID = 0; // Timer of ringing animation
DWORD g_dwCurrRingIconIndex = 0; // Index of current frame (into g_RingIcons)
DWORD g_dwRingAnimationStartTick; // Tick count (time) of animation start
#define MAX_BALLOON_TEXT_LEN 256 // Max number of character in balloon text
#define MAX_BALLOON_TITLE_LEN 64 // Max number of character in balloon title
struct SBalloonInfo { BOOL bEnable; // This flag is set when there's a need to display some balloon.
// EvaluateIcon() detects this bit, asks for a balloon and turns the bit off.
BOOL bDelete; // This flag is set when there's a need to destroy some balloon.
eIconState eState; // The current state of the icon
TCHAR szInfo[MAX_BALLOON_TEXT_LEN]; // The text to display on the balloon
TCHAR szInfoTitle[MAX_BALLOON_TITLE_LEN]; // The title to display on the balloon
};
BOOL g_bIconAdded = FALSE; // Do we have an icon on the status bar?
SBalloonInfo g_BalloonInfo = {0}; // The current icon + ballon state
struct EVENT_INFO { DWORD dwExtStatus; // Extended status code
UINT uResourceId; // String for display
eIconType eIcon; };
static const EVENT_INFO g_StatusEx[] = { JS_EX_DISCONNECTED, IDS_FAX_DISCONNECTED, LIST_IMAGE_ERROR, JS_EX_INITIALIZING, IDS_FAX_INITIALIZING, LIST_IMAGE_NONE, JS_EX_DIALING, IDS_FAX_DIALING, LIST_IMAGE_NONE, JS_EX_TRANSMITTING, IDS_FAX_SENDING, LIST_IMAGE_NONE, JS_EX_ANSWERED, IDS_FAX_ANSWERED, LIST_IMAGE_NONE, JS_EX_RECEIVING, IDS_FAX_RECEIVING, LIST_IMAGE_NONE, JS_EX_LINE_UNAVAILABLE, IDS_FAX_LINE_UNAVAILABLE, LIST_IMAGE_ERROR, JS_EX_BUSY, IDS_FAX_BUSY, LIST_IMAGE_WARNING, JS_EX_NO_ANSWER, IDS_FAX_NO_ANSWER, LIST_IMAGE_WARNING, JS_EX_BAD_ADDRESS, IDS_FAX_BAD_ADDRESS, LIST_IMAGE_ERROR, JS_EX_NO_DIAL_TONE, IDS_FAX_NO_DIAL_TONE, LIST_IMAGE_ERROR, JS_EX_FATAL_ERROR, IDS_FAX_FATAL_ERROR_SND, LIST_IMAGE_ERROR, JS_EX_CALL_DELAYED, IDS_FAX_CALL_DELAYED, LIST_IMAGE_ERROR, JS_EX_CALL_BLACKLISTED, IDS_FAX_CALL_BLACKLISTED, LIST_IMAGE_ERROR, JS_EX_NOT_FAX_CALL, IDS_FAX_NOT_FAX_CALL, LIST_IMAGE_ERROR, JS_EX_PARTIALLY_RECEIVED, IDS_FAX_PARTIALLY_RECEIVED, LIST_IMAGE_WARNING, JS_EX_CALL_COMPLETED, IDS_FAX_CALL_COMPLETED, LIST_IMAGE_NONE, JS_EX_CALL_ABORTED, IDS_FAX_CALL_ABORTED, LIST_IMAGE_NONE, 0, 0, LIST_IMAGE_NONE };
/////////////////////////////////////////////////////////////////////
// Function prototypes
//
BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, void* lpReserved);
void GetConfiguration();
DWORD WaitForRestartThread(LPVOID ThreadData); VOID WaitForFaxRestart(HWND hWnd);
LRESULT CALLBACK NotifyWndProc (HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
BOOL Connect();
BOOL RegisterForServerEvents(); VOID OnFaxEvent(FAX_EVENT_EX *pEvent);
VOID OnNewCall (const FAX_EVENT_NEW_CALL &NewCall); VOID StatusUpdate (PFAX_JOB_STATUS pStatus); BOOL GetStatusEx(PFAX_JOB_STATUS pStatus, eIconType* peIcon, TCHAR* ptsStatusEx, DWORD dwSize); BOOL IsUserGrantedAccess(DWORD);
void EvaluateIcon(); void SetIconState(eIconState eIcon, BOOL bEnable, TCHAR* ptsStatus = NULL);
VOID AnswerTheCall(); VOID InvokeClientConsole(); VOID DoFaxContextMenu(HWND hwnd); VOID OnTrayCallback (HWND hwnd, WPARAM wp, LPARAM lp); VOID CALLBACK RingTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime); VOID OnDeviceRing(DWORD dwDeviceID); VOID InitGlobals (); VOID GetRemoteId(PFAX_JOB_STATUS pStatus); BOOL InitModule (); BOOL DestroyModule (); DWORD CheckAnswerNowCapability (BOOL bForceReconnect, LPDWORD lpdwDeviceId /* = NULL */); VOID FaxPrinterProperties(DWORD dwPage); VOID CopyLTRString(TCHAR* szDest, LPCTSTR szSource, DWORD dwSize);
//////////////////////////////////////////////////////////////////////
// Implementation
//
extern "C" BOOL FaxMonitorShutdown() { g_bShuttingDown = TRUE; return DestroyModule(); } // FaxMonitorShutdown
extern "C" BOOL IsFaxMessage( PMSG pMsg ) /*++
Routine name : IsFaxMessage
Routine description:
Fax message handle
Arguments:
pMsg - pointer to a message
Return Value:
TRUE if the message was handled FALSE otherwise
--*/ { BOOL bRes = FALSE;
if(g_hMonitorDlg) { bRes = IsDialogMessage(g_hMonitorDlg, pMsg); } return bRes;
} // IsFaxMessage
VOID InitGlobals () /*++
Routine name : InitGlobals
Routine description:
Initializes all server connection related global variables
Author:
Eran Yariv (EranY), Dec, 2000
Arguments:
Return Value:
None.
--*/ { DBG_ENTER(TEXT("InitGlobals")); g_hFaxSvcHandle = NULL; g_dwlCurrentMsgID = 0; g_dwCurrentJobID = 0; g_hNotification = NULL; g_hCall = NULL; g_szAddress[0] = TEXT('\0'); g_szRemoteId[0] = TEXT('\0');
g_bRecipientNameValid = FALSE; g_szRecipientName[0] = TEXT('\0');
BOOL bDesktopSKU = IsDesktopSKU();
g_ConfigOptions.dwMonitorDeviceId = 0; g_ConfigOptions.bSend = FALSE; g_ConfigOptions.bReceive = FALSE; g_ConfigOptions.dwManualAnswerDeviceId = 0; g_ConfigOptions.dwAccessRights = 0; g_ConfigOptions.bNotifyProgress = bDesktopSKU; g_ConfigOptions.bNotifyInCompletion = bDesktopSKU; g_ConfigOptions.bNotifyOutCompletion = bDesktopSKU; g_ConfigOptions.bMonitorOnSend = bDesktopSKU; g_ConfigOptions.bMonitorOnReceive = bDesktopSKU; g_ConfigOptions.bSoundOnRing = bDesktopSKU; g_ConfigOptions.bSoundOnReceive = bDesktopSKU; g_ConfigOptions.bSoundOnSent = bDesktopSKU; g_ConfigOptions.bSoundOnError = bDesktopSKU;
for (DWORD dw = 0; dw < ICONS_COUNT; dw++) { g_Icons[dw].bEnable = FALSE; g_Icons[dw].tszToolTip[0] = TEXT('\0'); }
g_uRingTimerID = 0; g_dwCurrRingIconIndex = 0; g_dwRingAnimationStartTick = 0; g_BalloonInfo.bEnable = FALSE; g_BalloonInfo.bDelete = FALSE; g_BalloonInfo.szInfo[0] = TEXT('\0'); g_BalloonInfo.szInfoTitle[0] = TEXT('\0'); g_CurrentIcon = ICONS_COUNT; } // InitGlobals
BOOL InitModule () /*++
Routine name : InitModule
Routine description:
Initializes the DLL module. Call only once.
Author:
Eran Yariv (EranY), Mar, 2001
Arguments:
Return Value:
TRUE on success
--*/ { BOOL bRes = FALSE; DWORD dwRes; DBG_ENTER(TEXT("InitModule"), bRes);
InitGlobals (); //
// Don't have DllMain called for thread inits and shutdown.
//
DisableThreadLibraryCalls(g_hModule); //
// Load icons
//
for(DWORD dw=0; dw < ICONS_COUNT; ++dw) { g_Icons[dw].hIcon = LoadIcon(g_hModule, MAKEINTRESOURCE(g_Icons[dw].dwIconResId)); if(!g_Icons[dw].hIcon) { dwRes = GetLastError(); CALL_FAIL (RESOURCE_ERR, TEXT ("LoadIcon"), dwRes); bRes = FALSE; return bRes; } } //
// Load animation icons
//
for(dw=0; dw < RING_ICONS_NUM; ++dw) { g_RingIcons[dw].hIcon = LoadIcon(g_hModule, MAKEINTRESOURCE(g_RingIcons[dw].dwIconResId)); if(!g_RingIcons[dw].hIcon) { dwRes = GetLastError(); CALL_FAIL (RESOURCE_ERR, TEXT ("LoadIcon"), dwRes); bRes = FALSE; return bRes; } } //
// Load "new fax" tooltip
//
if (ERROR_SUCCESS != (dwRes = LoadAndFormatString (IDS_NEW_FAX, g_Icons[ICON_NEW_FAX].tszToolTip, TOOLTIP_SIZE))) { SetLastError (dwRes); bRes = FALSE; return bRes; } //
// Register our hidden window and create it
//
WNDCLASSEX wndclass = {0};
wndclass.cbSize = sizeof(wndclass); wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = NotifyWndProc; wndclass.hInstance = g_hModule; wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH) (COLOR_INACTIVEBORDER + 1); wndclass.lpszClassName = FAXSTAT_WINCLASS;
if(!RegisterClassEx(&wndclass)) { dwRes = GetLastError(); CALL_FAIL (WINDOW_ERR, TEXT ("RegisterClassEx"), dwRes); bRes = FALSE; return bRes; }
g_hWndFaxNotify = CreateWindow (FAXSTAT_WINCLASS, TEXT("HiddenFaxWindow"), 0, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, g_hModule, NULL); if(!g_hWndFaxNotify) { dwRes = GetLastError(); CALL_FAIL (WINDOW_ERR, TEXT ("CreateWindow"), dwRes); bRes = FALSE; return bRes; } //
// Create stop thread event
//
g_hStopStartupThreadEvent = CreateEvent (NULL, FALSE, FALSE, NULL); if(!g_hStopStartupThreadEvent) { dwRes = GetLastError(); CALL_FAIL (WINDOW_ERR, TEXT ("CreateEvent"), dwRes); bRes = FALSE; return bRes; } //
// Launch a thread which waits for the local fax service startup event.
// When the event is set, the thread posts WM_FAX_STARTED to our hidden window.
//
WaitForFaxRestart(g_hWndFaxNotify); bRes = TRUE; return bRes; } // InitModule
DWORD WaitForBackgroundThreadToDie () { DWORD dwRes = ERROR_SUCCESS; DBG_ENTER(TEXT("WaitForBackgroundThreadToDie"), dwRes);
ASSERTION (g_hServerStartupThread);
DWORD dwWaitRes = WaitForSingleObject (g_hServerStartupThread, INFINITE); switch (dwWaitRes) { case WAIT_OBJECT_0: //
// Thread terminated - hooray
//
VERBOSE (DBG_MSG, TEXT("Background thread terminated successfully")); CloseHandle (g_hServerStartupThread); g_hServerStartupThread = NULL; break;
case WAIT_FAILED: //
// Error waiting for thread to die
//
dwRes = GetLastError (); VERBOSE (DBG_MSG, TEXT("Can't wait for background thread: %ld"), dwRes); break;
default: //
// No other return value from WaitForSingleObject is valid
//
ASSERTION_FAILURE; dwRes = ERROR_GEN_FAILURE; break; } return dwRes; } // WaitForBackgroundThreadToDie
BOOL DestroyModule () /*++
Routine name : DestroyModule
Routine description:
Destroys the DLL module. Call only once.
Author:
Eran Yariv (EranY), Mar, 2001
Arguments:
Return Value:
TRUE on success
--*/ { BOOL bRes = FALSE; DBG_ENTER(TEXT("DestroyModule"), bRes);
//
// Prepare for shutdown - destroy all active windows
//
if (g_hMonitorDlg) { //
// Fake 'hide' key press on the monitor dialog
//
SendMessage (g_hMonitorDlg, WM_COMMAND, IDCANCEL, 0); } //
// Delete the system tray icon if existed
//
if (g_bIconAdded) { NOTIFYICONDATA iconData = {0};
iconData.cbSize = sizeof(iconData); iconData.hWnd = g_hWndFaxNotify; iconData.uID = TRAY_ICON_ID;
Shell_NotifyIcon(NIM_DELETE, &iconData); g_bIconAdded = FALSE; } //
// Destory this window
//
if (!DestroyWindow (g_hWndFaxNotify)) { CALL_FAIL (WINDOW_ERR, TEXT("DestroyWindow"), GetLastError ()); } g_hWndFaxNotify = NULL; //
// Signal the DLL shutdown event
//
ASSERTION (g_hStopStartupThreadEvent); if (SetEvent (g_hStopStartupThreadEvent)) { VERBOSE (DBG_MSG, TEXT("DLL shutdown event signaled")); if (g_hServerStartupThread) { //
// Wait for background thread to die
//
DWORD dwRes = WaitForBackgroundThreadToDie(); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("WaitForBackgroundThreadToDie"), dwRes); } } } else { CALL_FAIL (GENERAL_ERR, TEXT("SetEvent (g_hStopStartupThreadEvent)"), GetLastError ()); } //
// Release our DLL shutdown event
//
CloseHandle (g_hStopStartupThreadEvent); g_hStopStartupThreadEvent = NULL; //
// Free the data of the monitor module
//
FreeMonitorDialogData (TRUE); //
// Unregister window class
//
if (!UnregisterClass (FAXSTAT_WINCLASS, g_hModule)) { CALL_FAIL (WINDOW_ERR, TEXT("UnregisterClass"), GetLastError ()); } //
// Unregister from server notifications
//
if (g_hNotification) { if(!FaxUnregisterForServerEvents(g_hNotification)) { CALL_FAIL (RPC_ERR, TEXT("FaxUnregisterForServerEvents"), GetLastError()); } g_hNotification = NULL; } //
// Disconnect from the fax service
//
if (g_hFaxSvcHandle) { if (!FaxClose (g_hFaxSvcHandle)) { CALL_FAIL (GENERAL_ERR, TEXT("FaxClose"), GetLastError ()); } g_hFaxSvcHandle = NULL; } //
// Unload all icons
//
for (DWORD dw = 0; dw < ICONS_COUNT; dw++) { if (g_Icons[dw].hIcon) { if (!DestroyIcon (g_Icons[dw].hIcon)) { CALL_FAIL (WINDOW_ERR, TEXT("DestroyIcon"), GetLastError ()); } g_Icons[dw].hIcon = NULL; } } for (DWORD dw = 0; dw < RING_ICONS_NUM; dw++) { if (g_RingIcons[dw].hIcon) { if (!DestroyIcon (g_RingIcons[dw].hIcon)) { CALL_FAIL (WINDOW_ERR, TEXT("DestroyIcon"), GetLastError ()); } g_RingIcons[dw].hIcon = NULL; } } //
// Kill animation timer
//
if(g_uRingTimerID) { if (!KillTimer(NULL, g_uRingTimerID)) { CALL_FAIL (GENERAL_ERR, TEXT("KillTimer"), GetLastError ()); } g_uRingTimerID = NULL; } bRes = TRUE; return bRes; } // DestroyModule
BOOL WINAPI DllMain( HINSTANCE hModule, DWORD dwReason, void* lpReserved ) /*++
Routine description:
Fax notifications startup
Arguments:
hinstDLL - handle to the DLL module fdwReason - reason for calling function lpvReserved - reserved
Return Value:
TRUE if success FALSE otherwise
--*/ { BOOL bRes = TRUE; DBG_ENTER(TEXT("DllMain"), bRes, TEXT("Reason = %ld"), dwReason);
switch (dwReason) { case DLL_PROCESS_ATTACH: g_hModule = hModule; g_hResource = GetResInstance(hModule); if(!g_hResource) { return FALSE; }
bRes = InitModule (); return bRes;
case DLL_PROCESS_DETACH: //
// If g_bShuttingDown is not TRUE, someone (STOBJECT.DLL) forgot to call
// FaxMonitorShutdown() (our shutdown procedure) before doing FreeLibrary on us.
// This is not the way we're supposed to be used - a bug.
//
ASSERTION (g_bShuttingDown); HeapCleanup(); FreeResInstance(); return bRes;
default: return bRes; } } // DllMain
DWORD WaitForRestartThread( LPVOID ThreadData ) { //
// Wait for event to be signaled, indicating fax service started
//
DWORD dwRes = ERROR_SUCCESS; HKEY hKey = NULL; HANDLE hEvents[2] = {0}; DBG_ENTER(TEXT("WaitForRestartThread"), dwRes);
//
// NOTICE: Events order in the array matters - we want to detect DLL shutdown BEFORE we detect service startup
//
hEvents[0] = g_hStopStartupThreadEvent;
if (hEvents[1]) { CloseHandle (hEvents[1]); } if (hKey) { RegCloseKey (hKey); } //
// Obtain service startup event handle.
// We need to do this every time before calling WaitForMultipleObjects
// because the event returned from CreateSvcStartEvent is a single-shot event.
//
dwRes = CreateSvcStartEvent (&(hEvents[1]), &hKey); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("CreateSvcStartEvent"), dwRes); goto ExitThisThread; } //
// Wait for either the service startup event or the DLL shutdown event
//
DWORD dwWaitRes = WaitForMultipleObjects(ARR_SIZE(hEvents), hEvents, FALSE, INFINITE); switch (dwWaitRes) { case WAIT_OBJECT_0 + 1: //
// Service startup event
//
VERBOSE (DBG_MSG, TEXT("Service startup event received"));
PostMessage((HWND) ThreadData, WM_FAX_STARTED, 0, 0); break;
case WAIT_OBJECT_0: //
// Stop thread event - exit thread ASAP.
//
VERBOSE (DBG_MSG, TEXT("DLL shutdown event received")); break;
case WAIT_FAILED: dwRes = GetLastError (); CALL_FAIL (GENERAL_ERR, TEXT("WaitForMultipleObjects"), dwRes); break;
default: //
// No other return value from WaitForMultipleObjects is valid.
//
ASSERTION_FAILURE; break;
} // switch (dwWaitRes)
ExitThisThread:
if (hEvents[1]) { CloseHandle (hEvents[1]); } if (hKey) { RegCloseKey (hKey); } return dwRes;
} // WaitForRestartThread
VOID WaitForFaxRestart( HWND hWnd ) { DBG_ENTER(TEXT("WaitForFaxRestart"));
if (g_bShuttingDown) { //
// Shutting down - no thread creation allowed
//
return; } if (g_hServerStartupThread) { //
// Signal to Startup Thread to stop
//
if (!SetEvent (g_hStopStartupThreadEvent)) { CALL_FAIL (GENERAL_ERR, TEXT("SetEvent"), GetLastError()); return; }
//
// A Previous thead exists - wait for it to die
//
DWORD dwRes = WaitForBackgroundThreadToDie(); if (ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT("WaitForBackgroundThreadToDie"), dwRes); return; } }
if (!ResetEvent (g_hStopStartupThreadEvent)) { CALL_FAIL (GENERAL_ERR, TEXT("ResetEvent"), GetLastError()); return; }
ASSERTION (NULL == g_hServerStartupThread); g_hServerStartupThread = CreateThread(NULL, 0, WaitForRestartThread, (LPVOID) hWnd, 0, NULL); if (g_hServerStartupThread) { VERBOSE (DBG_MSG, TEXT("Background therad created successfully")); } else { CALL_FAIL (GENERAL_ERR, TEXT("CreateThread(WaitForRestartThread)"), GetLastError()); } } // WaitForFaxRestart
void GetConfiguration() /*++
Routine description:
Read notification configuration from the registry
Arguments:
none
Return Value:
none
--*/ { DWORD dwRes; DBG_ENTER(TEXT("GetConfiguration"));
HKEY hKey;
if(Connect()) { if (!FaxAccessCheckEx(g_hFaxSvcHandle, MAXIMUM_ALLOWED, &g_ConfigOptions.dwAccessRights)) { dwRes = GetLastError (); CALL_FAIL (RPC_ERR, TEXT("FaxAccessCheckEx"), dwRes); } } dwRes = RegOpenKeyEx(HKEY_CURRENT_USER, REGKEY_FAX_USERINFO, 0, KEY_READ, &hKey); if (dwRes != ERROR_SUCCESS) { //
// Can't open user information key - use defaults
//
CALL_FAIL (GENERAL_ERR, TEXT("RegOpenKeyEx(REGKEY_FAX_USERINFO)"), dwRes); BOOL bDesktopSKU = IsDesktopSKU();
g_ConfigOptions.dwMonitorDeviceId = 0; g_ConfigOptions.bNotifyProgress = bDesktopSKU; g_ConfigOptions.bNotifyInCompletion = bDesktopSKU; g_ConfigOptions.bNotifyOutCompletion = bDesktopSKU; g_ConfigOptions.bMonitorOnSend = bDesktopSKU; g_ConfigOptions.bMonitorOnReceive = bDesktopSKU; g_ConfigOptions.bSoundOnRing = bDesktopSKU; g_ConfigOptions.bSoundOnReceive = bDesktopSKU; g_ConfigOptions.bSoundOnSent = bDesktopSKU; g_ConfigOptions.bSoundOnError = bDesktopSKU; } else { GetRegistryDwordEx(hKey, REGVAL_NOTIFY_PROGRESS, &g_ConfigOptions.bNotifyProgress); GetRegistryDwordEx(hKey, REGVAL_NOTIFY_IN_COMPLETE, &g_ConfigOptions.bNotifyInCompletion); GetRegistryDwordEx(hKey, REGVAL_NOTIFY_OUT_COMPLETE, &g_ConfigOptions.bNotifyOutCompletion); GetRegistryDwordEx(hKey, REGVAL_MONITOR_ON_SEND, &g_ConfigOptions.bMonitorOnSend); GetRegistryDwordEx(hKey, REGVAL_MONITOR_ON_RECEIVE, &g_ConfigOptions.bMonitorOnReceive); GetRegistryDwordEx(hKey, REGVAL_SOUND_ON_RING, &g_ConfigOptions.bSoundOnRing); GetRegistryDwordEx(hKey, REGVAL_SOUND_ON_RECEIVE, &g_ConfigOptions.bSoundOnReceive); GetRegistryDwordEx(hKey, REGVAL_SOUND_ON_SENT, &g_ConfigOptions.bSoundOnSent); GetRegistryDwordEx(hKey, REGVAL_SOUND_ON_ERROR, &g_ConfigOptions.bSoundOnError); GetRegistryDwordEx(hKey, REGVAL_DEVICE_TO_MONITOR, &g_ConfigOptions.dwMonitorDeviceId); RegCloseKey( hKey ); }
g_ConfigOptions.dwManualAnswerDeviceId = 0;
if(Connect() && IsUserGrantedAccess(FAX_ACCESS_QUERY_CONFIG)) { PFAX_PORT_INFO_EX pPortsInfo = NULL; DWORD dwPorts = 0;
if(!FaxEnumPortsEx(g_hFaxSvcHandle, &pPortsInfo, &dwPorts)) { dwRes = GetLastError (); CALL_FAIL (RPC_ERR, TEXT("FaxEnumPortsEx"), dwRes); } else { if (dwPorts) { DWORD dwDevIndex = 0; for(DWORD dw=0; dw < dwPorts; ++dw) { //
// Iterate all fax devices
//
if ((g_ConfigOptions.dwMonitorDeviceId == pPortsInfo[dw].dwDeviceID) || // Found the monitored device or
(!g_ConfigOptions.dwMonitorDeviceId && // No monitored device and
(pPortsInfo[dw].bSend || // the device is send-enabled or
(FAX_DEVICE_RECEIVE_MODE_OFF != pPortsInfo[dw].ReceiveMode) // the device is receive-enabled
) ) ) { //
// Mark the index of the device we use for monitoring.
//
dwDevIndex = dw; } if (FAX_DEVICE_RECEIVE_MODE_MANUAL == pPortsInfo[dw].ReceiveMode) { //
// Mark the id of the device set for manual-answer
//
g_ConfigOptions.dwManualAnswerDeviceId = pPortsInfo[dw].dwDeviceID; } } //
// Update the device used for monitoring from the index we found
//
g_ConfigOptions.dwMonitorDeviceId = pPortsInfo[dwDevIndex].dwDeviceID; g_ConfigOptions.bSend = pPortsInfo[dwDevIndex].bSend; g_ConfigOptions.bReceive = FAX_DEVICE_RECEIVE_MODE_OFF != pPortsInfo[dwDevIndex].ReceiveMode; } else { //
// No devices
//
g_ConfigOptions.dwMonitorDeviceId = 0; g_ConfigOptions.bSend = FALSE; g_ConfigOptions.bReceive = FALSE; } FaxFreeBuffer(pPortsInfo); } } } // GetConfiguration
BOOL Connect( ) { BOOL bRes = FALSE; DBG_ENTER(TEXT("Connect"), bRes);
if (g_hFaxSvcHandle) { //
// Already connected
//
bRes = TRUE; return bRes; }
if (!FaxConnectFaxServer(NULL, &g_hFaxSvcHandle)) { CALL_FAIL (RPC_ERR, TEXT("FaxConnectFaxServer"), GetLastError()); return bRes; } bRes = TRUE; return bRes; } // Connect
VOID CALLBACK WaitForFaxRestartTimerProc( HWND hwnd, // handle to window
UINT uMsg, // WM_TIMER message
UINT_PTR idEvent, // timer identifier
DWORD dwTime // current system time
) /*++
Routine description:
Timer proc for restart waiting thread
Arguments:
hwnd - handle to window uMsg - WM_TIMER message idEvent - timer identifier dwTime - current system time
Return Value:
none
--*/ { DBG_ENTER(TEXT("WaitForFaxRestartTimerProc"));
if(!KillTimer(NULL, idEvent)) { CALL_FAIL (GENERAL_ERR, TEXT ("KillTimer"), GetLastError()); }
WaitForFaxRestart(g_hWndFaxNotify);
} // WaitForFaxRestartTimerProc
BOOL RegisterForServerEvents() /*++
Routine description:
Register for fax notifications
Arguments:
none
Return Value:
none
--*/ { BOOL bRes = FALSE; DWORD dwEventTypes;
DBG_ENTER(TEXT("RegisterForServerEvents"));
if (!Connect()) { goto exit; }
//
// Load configuration
//
GetConfiguration ();
if(g_hNotification) { if(!FaxUnregisterForServerEvents(g_hNotification)) { CALL_FAIL (RPC_ERR, TEXT("FaxUnregisterForServerEvents"), GetLastError()); } g_hNotification = NULL; }
//
// Register for the fax events
//
dwEventTypes = FAX_EVENT_TYPE_FXSSVC_ENDED;
VERBOSE (DBG_MSG, TEXT("User has the following rights: %x. Asking for FAX_EVENT_TYPE_FXSSVC_ENDED"), g_ConfigOptions.dwAccessRights);
if(IsUserGrantedAccess(FAX_ACCESS_SUBMIT) || IsUserGrantedAccess(FAX_ACCESS_SUBMIT_NORMAL) || IsUserGrantedAccess(FAX_ACCESS_SUBMIT_HIGH)) // User can submit new faxes (and view his own faxes)
{ dwEventTypes |= FAX_EVENT_TYPE_OUT_QUEUE; VERBOSE (DBG_MSG, TEXT("Also asking for FAX_EVENT_TYPE_OUT_QUEUE")); }
if(IsUserGrantedAccess(FAX_ACCESS_QUERY_JOBS)) // User can view all jobs (in and out)
{ dwEventTypes |= FAX_EVENT_TYPE_OUT_QUEUE | FAX_EVENT_TYPE_IN_QUEUE; VERBOSE (DBG_MSG, TEXT("Also asking for FAX_EVENT_TYPE_OUT_QUEUE & FAX_EVENT_TYPE_IN_QUEUE")); }
if(IsUserGrantedAccess(FAX_ACCESS_QUERY_CONFIG)) { dwEventTypes |= FAX_EVENT_TYPE_CONFIG | FAX_EVENT_TYPE_DEVICE_STATUS; VERBOSE (DBG_MSG, TEXT("Also asking for FAX_EVENT_TYPE_CONFIG & FAX_EVENT_TYPE_DEVICE_STATUS")); }
if(IsUserGrantedAccess(FAX_ACCESS_QUERY_IN_ARCHIVE)) { dwEventTypes |= FAX_EVENT_TYPE_IN_ARCHIVE | FAX_EVENT_TYPE_NEW_CALL; VERBOSE (DBG_MSG, TEXT("Also asking for FAX_EVENT_TYPE_IN_ARCHIVE")); }
if (!FaxRegisterForServerEvents (g_hFaxSvcHandle, dwEventTypes, // Types of events to receive
NULL, // Not using completion ports
0, // Not using completion ports
g_hWndFaxNotify, // Handle of window to receive notification messages
WM_FAX_EVENT, // Message id
&g_hNotification)) // Notification handle
{ DWORD dwRes = GetLastError (); CALL_FAIL (RPC_ERR, TEXT("FaxRegisterForServerEvents"), dwRes); g_hNotification = NULL; } else { bRes = TRUE; }
if(!FaxRelease(g_hFaxSvcHandle)) { CALL_FAIL (RPC_ERR, TEXT("FaxRelease"), GetLastError ()); }
exit:
if(!bRes) { //
// FaxRegisterForServerEvents failed, try again 1 minute later
//
if(!SetTimer(NULL, 0, 60000, WaitForFaxRestartTimerProc)) { CALL_FAIL (GENERAL_ERR, TEXT("SetTimer"), GetLastError ()); } }
return bRes;
} // RegisterForServerEvents
VOID OnFaxEvent(FAX_EVENT_EX* pEvent) /*++
Routine description:
Handle fax events
Arguments:
pEvent - fax event data
Return Value:
none
--*/ { DBG_ENTER(TEXT("OnFaxEvent"), TEXT("%x"), pEvent); if(!pEvent || pEvent->dwSizeOfStruct != sizeof(FAX_EVENT_EX)) { VERBOSE (DBG_MSG, TEXT("Either event is bad or it has bad size")); return; } switch (pEvent->EventType) { case FAX_EVENT_TYPE_NEW_CALL:
OnNewCall (pEvent->EventInfo.NewCall); break;
case FAX_EVENT_TYPE_IN_QUEUE: case FAX_EVENT_TYPE_OUT_QUEUE:
switch (pEvent->EventInfo.JobInfo.Type) { case FAX_JOB_EVENT_TYPE_ADDED: case FAX_JOB_EVENT_TYPE_REMOVED: break;
case FAX_JOB_EVENT_TYPE_STATUS: if(pEvent->EventInfo.JobInfo.pJobData && pEvent->EventInfo.JobInfo.pJobData->dwDeviceID && pEvent->EventInfo.JobInfo.pJobData->dwDeviceID == g_ConfigOptions.dwMonitorDeviceId) { if(g_dwlCurrentMsgID != pEvent->EventInfo.JobInfo.dwlMessageId) { g_bRecipientNameValid = FALSE; } g_dwlCurrentMsgID = pEvent->EventInfo.JobInfo.dwlMessageId; }
if(g_dwlCurrentMsgID == pEvent->EventInfo.JobInfo.dwlMessageId) { StatusUpdate(pEvent->EventInfo.JobInfo.pJobData); } break; } break;
case FAX_EVENT_TYPE_IN_ARCHIVE: if(FAX_JOB_EVENT_TYPE_ADDED == pEvent->EventInfo.JobInfo.Type) { g_dwlNewMsgId = pEvent->EventInfo.JobInfo.dwlMessageId;
SetIconState(ICON_NEW_FAX, TRUE); } break;
case FAX_EVENT_TYPE_CONFIG: if (FAX_CONFIG_TYPE_SECURITY == pEvent->EventInfo.ConfigType) { //
// Security has changed.
// We should re-register for events now.
// Also re-read the current user rights
//
RegisterForServerEvents(); } else if (FAX_CONFIG_TYPE_DEVICES == pEvent->EventInfo.ConfigType) { //
// Device configuration has changed.
// The only reason we need to know that is because the device we were listening on might be gone now.
// If that's true, we should pick the first available device as the monitoring device.
//
GetConfiguration(); UpdateMonitorData(g_hMonitorDlg); } else { //
// Non-interesting configuraton change - ignore.
//
} break;
case FAX_EVENT_TYPE_DEVICE_STATUS: if(pEvent->EventInfo.DeviceStatus.dwDeviceId == g_ConfigOptions.dwMonitorDeviceId || pEvent->EventInfo.DeviceStatus.dwDeviceId == g_ConfigOptions.dwManualAnswerDeviceId) { //
// we only care about the monitored / manual-answer devices
//
if ((pEvent->EventInfo.DeviceStatus.dwNewStatus) & FAX_DEVICE_STATUS_RINGING) { //
// Device is ringing
//
OnDeviceRing (pEvent->EventInfo.DeviceStatus.dwDeviceId); } else { if (FAX_RINGING == g_devState) { //
// Device is not ringing anymore but the monitor shows 'ringing'.
// Set the monitor to idle state.
//
SetStatusMonitorDeviceState(FAX_IDLE); } } } break;
case FAX_EVENT_TYPE_FXSSVC_ENDED: //
// Service was stopped
//
SetIconState(ICON_RINGING, FALSE); SetIconState(ICON_SENDING, FALSE); SetIconState(ICON_RECEIVING, FALSE);
SetStatusMonitorDeviceState(FAX_IDLE); //
// We just lost our RPC connection handle and our notification handle. Close and zero them.
//
if (g_hNotification) { FaxUnregisterForServerEvents (g_hNotification); g_hNotification = NULL; } if (g_hFaxSvcHandle) { FaxClose (g_hFaxSvcHandle); g_hFaxSvcHandle = NULL; }
WaitForFaxRestart(g_hWndFaxNotify); break; }
FaxFreeBuffer (pEvent); } // OnFaxEvent
VOID OnDeviceRing( DWORD dwDeviceID ) /*++
Routine description:
Called when a device is ringing
Arguments:
dwDeviceID - device ID
Return Value:
none
--*/ { DBG_ENTER(TEXT("OnDeviceRing"), TEXT("%d"), dwDeviceID);
//
// It can be monitored or manual answer device
//
SetStatusMonitorDeviceState(FAX_RINGING); AddStatusMonitorLogEvent(LIST_IMAGE_NONE, IDS_RINGING); if(g_ConfigOptions.bSoundOnRing) { if(!PlaySound(g_Icons[ICON_RINGING].pctsSound, NULL, SND_ASYNC | SND_APPLICATION | SND_NODEFAULT)) { CALL_FAIL (WINDOW_ERR, TEXT ("PlaySound"), 0); } } }
VOID OnNewCall ( const FAX_EVENT_NEW_CALL &NewCall ) /*++
Routine description:
Handle "new call" fax event
Arguments:
NewCall - fax event data
Return Value:
none
--*/ { DBG_ENTER(TEXT("OnNewCall"));
//
// It can be any manual answer device
//
g_hCall = NewCall.hCall;
if(NewCall.hCall) { LPCTSTR lpctstrParam = NULL; DWORD dwStringResId = IDS_INCOMING_CALL;
CopyLTRString(g_szAddress, NewCall.lptstrCallerId, ARR_SIZE(g_szAddress) - 1);
_tcscpy(g_szRemoteId, g_szAddress);
if(NewCall.lptstrCallerId && _tcslen(NewCall.lptstrCallerId)) { //
// We know the caller id.
// Use another string which formats the caller ID parameter
//
lpctstrParam = NewCall.lptstrCallerId; dwStringResId = IDS_INCOMING_CALL_FROM; } TCHAR tszEvent[MAX_PATH] = {0}; AddStatusMonitorLogEvent (LIST_IMAGE_NONE, dwStringResId, lpctstrParam, tszEvent, ARR_SIZE(tszEvent)); SetStatusMonitorDeviceState(FAX_RINGING); SetIconState(ICON_RINGING, TRUE, tszEvent); } else { //
// Call is gone
//
SetStatusMonitorDeviceState(FAX_IDLE); SetIconState(ICON_RINGING, FALSE, TEXT("")); } } // OnNewCall
VOID GetRemoteId( PFAX_JOB_STATUS pStatus ) /*++
Routine description:
Write Sender ID or Recipient ID into g_szRemoteId Sender ID (receive): TSID or Caller ID or "unknown caller" Recipient ID (send): Recipient name or CSID or Recipient phone number.
Arguments:
pStatus - job status data
Return Value:
none
--*/ { DBG_ENTER(TEXT("GetRemoteId"));
if(!pStatus) { return; }
if(JT_SEND == pStatus->dwJobType) { //
// Recipient ID (send)
//
if(!g_bRecipientNameValid) { //
// Store the recipient name into g_szRecipientName
//
PFAX_JOB_ENTRY_EX pJobEntry = NULL; if(!FaxGetJobEx(g_hFaxSvcHandle, g_dwlCurrentMsgID, &pJobEntry)) { CALL_FAIL (RPC_ERR, TEXT ("FaxGetJobEx"), GetLastError()); g_szRecipientName[0] = TEXT('\0'); } else { if(pJobEntry->lpctstrRecipientName && _tcslen(pJobEntry->lpctstrRecipientName)) { _tcsncpy(g_szRecipientName, pJobEntry->lpctstrRecipientName, ARR_SIZE(g_szRecipientName) - 1); } else { g_szRecipientName[0] = TEXT('\0'); } g_bRecipientNameValid = TRUE;
FaxFreeBuffer(pJobEntry); } }
if(_tcslen(g_szRecipientName)) { //
// Recipient name
//
_tcsncpy(g_szRemoteId, g_szRecipientName, ARR_SIZE(g_szRemoteId) - 1); } else if(pStatus->lpctstrCsid && _tcslen(pStatus->lpctstrCsid)) { //
// CSID
//
CopyLTRString(g_szRemoteId, pStatus->lpctstrCsid, ARR_SIZE(g_szRemoteId) - 1); } else if(pStatus->lpctstrCallerID && _tcslen(pStatus->lpctstrCallerID)) { //
// Recipient number
// For outgoing fax FAX_JOB_STATUS.lpctstrCallerID field
// contains a recipient fax number.
//
CopyLTRString(g_szRemoteId, pStatus->lpctstrCallerID, ARR_SIZE(g_szRemoteId) - 1); } } else if(JT_RECEIVE == pStatus->dwJobType) { //
// Sender ID (receive)
//
if(pStatus->lpctstrTsid && _tcslen(pStatus->lpctstrTsid) && pStatus->lpctstrCallerID && _tcslen(pStatus->lpctstrCallerID)) { //
// We have Caller ID and TSID
//
TCHAR szTmp[MAX_PATH] = {0}; _sntprintf(szTmp, ARR_SIZE(szTmp)-1, TEXT("%s (%s)"), pStatus->lpctstrCallerID, pStatus->lpctstrTsid); CopyLTRString(g_szRemoteId, szTmp, ARR_SIZE(g_szRemoteId) - 1); } else if(pStatus->lpctstrTsid && _tcslen(pStatus->lpctstrTsid)) { //
// TSID
//
CopyLTRString(g_szRemoteId, pStatus->lpctstrTsid, ARR_SIZE(g_szRemoteId) - 1); } else if(pStatus->lpctstrCallerID && _tcslen(pStatus->lpctstrCallerID)) { //
// Caller ID
//
CopyLTRString(g_szRemoteId, pStatus->lpctstrCallerID, ARR_SIZE(g_szRemoteId) - 1); } else { //
// unknown caller
//
_tcsncpy(g_szRemoteId, TEXT(""), ARR_SIZE(g_szRemoteId) - 1); } } }
VOID StatusUpdate(PFAX_JOB_STATUS pStatus) /*++
Routine description:
Handle "status update" fax event
Arguments:
pStatus - job status data
Return Value:
none
--*/ { DBG_ENTER(TEXT("StatusUpdate"));
DWORD dwRes;
if(!pStatus) { return; } VERBOSE (DBG_MSG, TEXT("Job status event - Type=%x, QueueStatus=%x, ExtendedStatus=%x"), pStatus->dwJobType, pStatus->dwQueueStatus, pStatus->dwExtendedStatus);
if(JT_RECEIVE != pStatus->dwJobType && JT_SEND != pStatus->dwJobType) { VERBOSE (DBG_MSG, TEXT("Job type (%d) is not JT_RECEIVE or JT_SEND. Ignoring."), pStatus->dwJobType); return; }
eIconType eIcon = LIST_IMAGE_NONE; // New icon to set
DWORD dwStatusId = 0; // string resource ID
TCHAR tszStatus[MAX_PATH] = {0}; // String to show in status monitor
BOOL bStatus = FALSE; // TRUE if tszStatus has valid string
if(pStatus->dwQueueStatus & JS_PAUSED) { //
// The job has been paused in the outbox queue after a failure
//
g_dwlCurrentMsgID = 0; return; }
if(pStatus->dwQueueStatus & JS_COMPLETED || pStatus->dwQueueStatus & JS_ROUTING) { //
// Incoming job sends JS_ROUTING status by completion
//
if(JS_EX_PARTIALLY_RECEIVED == pStatus->dwExtendedStatus) { bStatus = GetStatusEx(pStatus, &eIcon, tszStatus, ARR_SIZE(tszStatus) - 1); } else { eIcon = LIST_IMAGE_SUCCESS; dwStatusId = (JT_SEND == pStatus->dwJobType) ? IDS_FAX_SNT_COMPLETED : IDS_FAX_RCV_COMPLETED; } } else if(pStatus->dwQueueStatus & JS_CANCELING) { dwStatusId = IDS_FAX_CANCELING; } else if(pStatus->dwQueueStatus & JS_CANCELED) { dwStatusId = IDS_FAX_CANCELED; } else if(pStatus->dwQueueStatus & JS_INPROGRESS) { GetRemoteId(pStatus);
bStatus = GetStatusEx(pStatus, &eIcon, tszStatus, ARR_SIZE(tszStatus) - 1);
g_dwCurrentJobID = pStatus->dwJobID;
SetIconState((JT_SEND == pStatus->dwJobType) ? ICON_SENDING : ICON_RECEIVING, TRUE, tszStatus);
SetStatusMonitorDeviceState((JT_SEND == pStatus->dwJobType) ? FAX_SENDING : FAX_RECEIVING); } else if(pStatus->dwQueueStatus & JS_FAILED) { if(!(bStatus = GetStatusEx(pStatus, &eIcon, tszStatus, ARR_SIZE(tszStatus) - 1))) { eIcon = LIST_IMAGE_ERROR; dwStatusId = (JT_SEND == pStatus->dwJobType) ? IDS_FAX_FATAL_ERROR_SND : IDS_FAX_FATAL_ERROR_RCV; } } else if(pStatus->dwQueueStatus & JS_RETRIES_EXCEEDED) { //
// Add two strings to the log.
// The first is extended status.
// The second is "Retries exceeded"
//
if(bStatus = GetStatusEx(pStatus, &eIcon, tszStatus, ARR_SIZE(tszStatus) - 1)) { AddStatusMonitorLogEvent(eIcon, tszStatus); bStatus = FALSE; }
eIcon = LIST_IMAGE_ERROR; dwStatusId = IDS_FAX_RETRIES_EXCEEDED; } else if(pStatus->dwQueueStatus & JS_RETRYING) { if(!(bStatus = GetStatusEx(pStatus, &eIcon, tszStatus, ARR_SIZE(tszStatus) - 1))) { eIcon = LIST_IMAGE_ERROR; dwStatusId = IDS_FAX_FATAL_ERROR_SND; } }
if(!bStatus && dwStatusId) { if (ERROR_SUCCESS != (dwRes = LoadAndFormatString (dwStatusId, tszStatus, ARR_SIZE(tszStatus)))) { bStatus = FALSE; } else { bStatus = TRUE; } }
if(bStatus) { AddStatusMonitorLogEvent (eIcon, tszStatus); }
if(!(pStatus->dwQueueStatus & JS_INPROGRESS)) { g_dwCurrentJobID = 0;
SetStatusMonitorDeviceState(FAX_IDLE);
SetIconState(ICON_SENDING, FALSE); SetIconState(ICON_RECEIVING, FALSE); } if(pStatus->dwQueueStatus & (JS_FAILED | JS_RETRIES_EXCEEDED | JS_RETRYING)) { if(JT_SEND == pStatus->dwJobType) { g_dwlSendFailedMsgId = g_dwlCurrentMsgID; }
SetIconState((JT_SEND == pStatus->dwJobType) ? ICON_SEND_FAILED : ICON_RECEIVE_FAILED, TRUE, tszStatus); }
if((JT_SEND == pStatus->dwJobType) && (pStatus->dwQueueStatus & JS_COMPLETED)) { SetIconState(ICON_SEND_SUCCESS, TRUE, tszStatus); g_dwlSendSuccessMsgId = g_dwlCurrentMsgID; } } // StatusUpdate
/*
Unhandled Job Statuses: JS_NOLINE JS_PAUSED JS_PENDING JS_DELETING Unhandled Extneded Job Statuses: JS_EX_HANDLED */
BOOL GetStatusEx( PFAX_JOB_STATUS pStatus, eIconType* peIcon, TCHAR* ptsStatusEx, DWORD dwSize ) /*++
Routine description:
Find string description and icon type for a job according to its extended status
Arguments:
pStatus - [in] job status data peIcon - [out] job icon type ptsStatusEx - [out] job status string dwSize - [in] status string size
Return Value:
TRUE if success FALSE otherwise
--*/ { BOOL bRes = FALSE; DBG_ENTER(TEXT("GetStatusEx"), bRes);
ASSERTION (pStatus && peIcon && ptsStatusEx);
TCHAR tszFormat[MAX_PATH]={0}; if (pStatus->lpctstrExtendedStatus) { //
// FSP provided proprietary status string - use it as is.
//
*peIcon = LIST_IMAGE_WARNING; _tcsncpy(ptsStatusEx, pStatus->lpctstrExtendedStatus, dwSize); ptsStatusEx[dwSize-1] = TEXT('\0'); bRes = TRUE; return bRes; }
//
// No extended status string, check for well known status code
//
if(!(pStatus->dwValidityMask & FAX_JOB_FIELD_STATUS_EX) || !pStatus->dwExtendedStatus) { return FALSE; } *peIcon = LIST_IMAGE_NONE; for(DWORD dw=0; g_StatusEx[dw].dwExtStatus != 0; ++dw) { if(g_StatusEx[dw].dwExtStatus == pStatus->dwExtendedStatus) { DWORD dwRes;
*peIcon = g_StatusEx[dw].eIcon; if (ERROR_SUCCESS != (dwRes = LoadAndFormatString (g_StatusEx[dw].uResourceId, tszFormat, ARR_SIZE(tszFormat)))) { return bRes; } break; } }
switch(pStatus->dwExtendedStatus) { case JS_EX_DIALING: //
// For outgoing fax FAX_JOB_STATUS.lpctstrCallerID field
// contains a recipient fax number.
//
CopyLTRString(g_szAddress, pStatus->lpctstrCallerID, ARR_SIZE(g_szAddress) - 1);
_sntprintf(ptsStatusEx, dwSize -1, tszFormat, g_szAddress); ptsStatusEx[dwSize-1] = TEXT('\0'); break;
case JS_EX_TRANSMITTING: _sntprintf(ptsStatusEx, dwSize -1, tszFormat, pStatus->dwCurrentPage, pStatus->dwPageCount); ptsStatusEx[dwSize-1] = TEXT('\0'); break;
case JS_EX_RECEIVING: _sntprintf(ptsStatusEx, dwSize -1, tszFormat, pStatus->dwCurrentPage); ptsStatusEx[dwSize-1] = TEXT('\0'); break;
case JS_EX_FATAL_ERROR: { DWORD dwRes; if (ERROR_SUCCESS != (dwRes = LoadAndFormatString ( (JT_SEND == pStatus->dwJobType) ? IDS_FAX_FATAL_ERROR_SND : IDS_FAX_FATAL_ERROR_RCV, ptsStatusEx, dwSize))) { return bRes; } } break;
default: _tcsncpy(ptsStatusEx, tszFormat, dwSize); break; } bRes = TRUE; return bRes; } // GetStatusEx
BOOL IsNotifyEnable( eIconState state ) /*++
Routine description:
Check if the UI notification is enabled for a specific icon state
Arguments:
state [in] - icon state
Return Value:
TRUE if the notification is enabled FASLE otherwise
--*/ { BOOL bEnable = TRUE; switch(state) { case ICON_SENDING: case ICON_RECEIVING: bEnable = g_ConfigOptions.bNotifyProgress; break;
case ICON_NEW_FAX: case ICON_RECEIVE_FAILED: bEnable = g_ConfigOptions.bNotifyInCompletion; break;
case ICON_SEND_SUCCESS: case ICON_SEND_FAILED: bEnable = g_ConfigOptions.bNotifyOutCompletion; break;
};
return bEnable;
} // IsNotifyEnable
eIconState GetVisibleIconType () /*++
Routine name : GetVisibleIconType
Routine description:
Return the index (type) of the currently visible icon
Author:
Eran Yariv (EranY), May, 2001
Arguments:
Return Value:
Icon type
--*/ { for(int index = ICON_RINGING; index < ICONS_COUNT; ++index) { if(!IsNotifyEnable(eIconState(index))) { continue; }
if(g_Icons[index].bEnable) { return eIconState(index); } } return ICONS_COUNT; } // GetVisibleIconType
void EvaluateIcon() /*++
Routine description:
Show notification icon, tooltip and balloon according to the current icon state
Arguments:
Return Value:
none
--*/ { DBG_ENTER(TEXT("EvaluateIcon"));
ASSERTION (g_hWndFaxNotify);
NOTIFYICONDATA iconData = {0};
iconData.cbSize = sizeof(iconData); iconData.hWnd = g_hWndFaxNotify; iconData.uID = TRAY_ICON_ID; iconData.uFlags = NIF_MESSAGE | NIF_TIP; iconData.uCallbackMessage = WM_TRAYCALLBACK;
g_CurrentIcon = GetVisibleIconType(); if(ICONS_COUNT == g_CurrentIcon) { //
// No visible icon
//
if(g_bIconAdded) { Shell_NotifyIcon(NIM_DELETE, &iconData); g_bIconAdded = FALSE; }
//
// No icon - no balloon
//
g_BalloonInfo.bDelete = FALSE; g_BalloonInfo.bEnable = FALSE; return; } iconData.uFlags = iconData.uFlags | NIF_ICON; iconData.hIcon = g_Icons[g_CurrentIcon].hIcon; _tcscpy(iconData.szTip, g_Icons[g_CurrentIcon].tszToolTip);
if(g_BalloonInfo.bEnable) { if(IsNotifyEnable(g_BalloonInfo.eState)) { //
// Show balloon tooltip
//
iconData.uTimeout = g_Icons[g_BalloonInfo.eState].dwBalloonTimeout; iconData.uFlags = iconData.uFlags | NIF_INFO; iconData.dwInfoFlags = g_Icons[g_BalloonInfo.eState].dwBalloonIcon | NIIF_NOSOUND;
_tcscpy(iconData.szInfo, g_BalloonInfo.szInfo); _tcscpy(iconData.szInfoTitle, g_BalloonInfo.szInfoTitle); } g_BalloonInfo.bEnable = FALSE; }
if(g_BalloonInfo.bDelete) { //
// Destroy currently open balloon tooltip
//
iconData.uFlags = iconData.uFlags | NIF_INFO;
_tcscpy(iconData.szInfo, TEXT("")); _tcscpy(iconData.szInfoTitle, TEXT(""));
g_BalloonInfo.bDelete = FALSE; }
Shell_NotifyIcon(g_bIconAdded ? NIM_MODIFY : NIM_ADD, &iconData); g_bIconAdded = TRUE; } // EvaluateIcon
void SetIconState( eIconState eIcon, BOOL bEnable, TCHAR* ptsStatus /* = NULL */ ) /*++
Routine description:
Change notification bar icon state.
Arguments:
eIcon - icon type bEnable - icon state (enable/disable) ptsStatus - status string (optional)
Return Value:
none
--*/ { DWORD dwRes; DBG_ENTER(TEXT("SetIconState"), TEXT("Icon id=%d, Enable=%d, Status=%s"), eIcon, bEnable, ptsStatus);
ASSERTION (eIcon < ICONS_COUNT);
if(!bEnable && eIcon != ICON_RINGING) { //
// We're turning off a state - nothing special to do
//
goto exit; }
TCHAR tsFormat[MAX_PATH]= {0}; LPCTSTR strParam = NULL; DWORD dwStringResId = 0;
switch(eIcon) { case ICON_RINGING: if(bEnable) { //
// Sound, Balloon, and Animation
//
SetIconState(ICON_SENDING, FALSE); SetIconState(ICON_RECEIVING, FALSE);
g_BalloonInfo.bEnable = TRUE; g_BalloonInfo.eState = eIcon;
//
// Compose the balloon tooltip
//
strParam = NULL; dwStringResId = IDS_INCOMING_CALL; if(_tcslen(g_szAddress)) { //
// Caller id is known - use it in formatted string
//
strParam = g_szAddress; dwStringResId = IDS_INCOMING_CALL_FROM; } if (ERROR_SUCCESS != LoadAndFormatString(dwStringResId, tsFormat, ARR_SIZE(tsFormat), strParam)) { return; }
_tcsncpy(g_BalloonInfo.szInfoTitle, tsFormat, MAX_BALLOON_TITLE_LEN-1);
if (ERROR_SUCCESS != LoadAndFormatString(IDS_CLICK_TO_ANSWER, g_BalloonInfo.szInfo, ARR_SIZE(g_BalloonInfo.szInfo))) { return; }
//
// Set tooltip
//
_sntprintf(g_Icons[eIcon].tszToolTip, TOOLTIP_SIZE-1, TEXT("%s\n%s"), tsFormat, g_BalloonInfo.szInfo);
if(!g_uRingTimerID) { //
// Set animation timer
//
g_uRingTimerID = SetTimer(NULL, 0, RING_ANIMATION_FRAME_DELAY, RingTimerProc); if(!g_uRingTimerID) { dwRes = GetLastError(); CALL_FAIL (GENERAL_ERR, TEXT ("SetTimer"), dwRes); } else { g_dwRingAnimationStartTick = GetTickCount(); } } } else // disable ringing
{ if(g_Icons[eIcon].bEnable) { //
// Remove ringing balloon
//
g_BalloonInfo.bDelete = TRUE; }
if(g_uRingTimerID) { //
// kill animation timer
//
if(!KillTimer(NULL, g_uRingTimerID)) { dwRes = GetLastError(); CALL_FAIL (GENERAL_ERR, TEXT ("KillTimer"), dwRes); } g_uRingTimerID = 0; g_dwRingAnimationStartTick = 0; } } break;
case ICON_SENDING: //
// Compose tooltip
//
if (ERROR_SUCCESS != LoadAndFormatString (IDS_SENDING_TO, tsFormat, ARR_SIZE(tsFormat), g_szRemoteId)) { return; } _sntprintf(g_Icons[eIcon].tszToolTip, TOOLTIP_SIZE-1, TEXT("%s\n%s"), tsFormat, ptsStatus ? ptsStatus : TEXT(""));
if(!g_Icons[eIcon].bEnable) { //
// Turn the icon on
//
SetIconState(ICON_RINGING, FALSE); SetIconState(ICON_RECEIVING, FALSE);
//
// Open fax monitor
//
if(g_ConfigOptions.bMonitorOnSend) { dwRes = OpenFaxMonitor(); if(ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT ("OpenFaxMonitor"), dwRes); } } } break;
case ICON_RECEIVING:
//
// Compose tooltip
//
strParam = NULL; dwStringResId = IDS_RECEIVING; if(_tcslen(g_szRemoteId)) { strParam = g_szRemoteId; dwStringResId = IDS_RECEIVING_FROM; }
if (ERROR_SUCCESS != LoadAndFormatString (dwStringResId, tsFormat, ARR_SIZE(tsFormat), strParam)) { return; } _sntprintf(g_Icons[eIcon].tszToolTip, TOOLTIP_SIZE-1, TEXT("%s\n%s"), tsFormat, ptsStatus ? ptsStatus : TEXT(""));
if(!g_Icons[eIcon].bEnable) { //
// Turn the icon on
//
SetIconState(ICON_RINGING, FALSE); SetIconState(ICON_SENDING, FALSE);
//
// open fax monitor
//
if(g_ConfigOptions.bMonitorOnReceive) { dwRes = OpenFaxMonitor(); if(ERROR_SUCCESS != dwRes) { CALL_FAIL (GENERAL_ERR, TEXT ("OpenFaxMonitor"), dwRes); } } } break;
case ICON_SEND_FAILED: //
// Compose tooltip
//
if (ERROR_SUCCESS != LoadAndFormatString (IDS_SEND_ERROR_BALLOON, tsFormat, ARR_SIZE(tsFormat), g_szRemoteId)) { return; }
_sntprintf(g_Icons[eIcon].tszToolTip, TOOLTIP_SIZE-1, TEXT("%s\n%s"), tsFormat, ptsStatus ? ptsStatus : TEXT(""));
if(!g_Icons[eIcon].bEnable) { //
// Turn the icon on
//
if(g_ConfigOptions.bSoundOnError) { if(!PlaySound(g_Icons[eIcon].pctsSound, NULL, SND_ASYNC | SND_APPLICATION | SND_NODEFAULT)) { CALL_FAIL (WINDOW_ERR, TEXT ("PlaySound"), 0); } }
g_BalloonInfo.bEnable = TRUE; g_BalloonInfo.eState = eIcon;
//
// Compose the balloon
//
_tcsncpy(g_BalloonInfo.szInfoTitle, tsFormat, MAX_BALLOON_TITLE_LEN-1); _tcsncpy(g_BalloonInfo.szInfo, ptsStatus ? ptsStatus : TEXT(""), MAX_BALLOON_TEXT_LEN-1); } break;
case ICON_RECEIVE_FAILED: //
// Compose tooltip
//
strParam = NULL; dwStringResId = IDS_RCV_ERROR_BALLOON; if(_tcslen(g_szRemoteId)) { strParam = g_szRemoteId; dwStringResId = IDS_RCV_FROM_ERROR_BALLOON; }
if (ERROR_SUCCESS != LoadAndFormatString (dwStringResId, tsFormat, ARR_SIZE(tsFormat), strParam)) { return; }
_sntprintf(g_Icons[eIcon].tszToolTip, TOOLTIP_SIZE-1, TEXT("%s\n%s"), tsFormat, ptsStatus ? ptsStatus : TEXT(""));
if(!g_Icons[eIcon].bEnable) { //
// Turn the icon on
//
if(g_ConfigOptions.bSoundOnError) { if(!PlaySound(g_Icons[eIcon].pctsSound, NULL, SND_ASYNC | SND_APPLICATION | SND_NODEFAULT)) { CALL_FAIL (WINDOW_ERR, TEXT ("PlaySound"), 0); } }
g_BalloonInfo.bEnable = TRUE; g_BalloonInfo.eState = eIcon;
//
// Compose the balloon
//
_tcsncpy(g_BalloonInfo.szInfoTitle, tsFormat, MAX_BALLOON_TITLE_LEN-1); _tcsncpy(g_BalloonInfo.szInfo, ptsStatus ? ptsStatus : TEXT(""), MAX_BALLOON_TEXT_LEN-1); } break;
case ICON_NEW_FAX: //
// Compose tooltip
//
strParam = NULL; dwStringResId = IDS_NEW_FAX_BALLOON; if(_tcslen(g_szRemoteId)) { strParam = g_szRemoteId; dwStringResId = IDS_NEW_FAX_FROM_BALLOON; }
if (ERROR_SUCCESS != LoadAndFormatString (dwStringResId, tsFormat, ARR_SIZE(tsFormat), strParam)) { return; }
if (ERROR_SUCCESS != LoadAndFormatString (IDS_CLICK_TO_VIEW, g_BalloonInfo.szInfo, ARR_SIZE(g_BalloonInfo.szInfo))) { return; }
_sntprintf(g_Icons[eIcon].tszToolTip, TOOLTIP_SIZE-1, TEXT("%s\n%s"), tsFormat, g_BalloonInfo.szInfo);
if(!g_Icons[eIcon].bEnable) { //
// Turn the icon on
//
if (g_ConfigOptions.bSoundOnReceive) { if(!PlaySound(g_Icons[eIcon].pctsSound, NULL, SND_ASYNC | SND_APPLICATION | SND_NODEFAULT)) { CALL_FAIL (WINDOW_ERR, TEXT ("PlaySound"), 0); } }
g_BalloonInfo.bEnable = TRUE; g_BalloonInfo.eState = eIcon; //
// Compose the balloon
//
_tcsncpy(g_BalloonInfo.szInfoTitle, tsFormat, MAX_BALLOON_TITLE_LEN-1); } break;
case ICON_SEND_SUCCESS:
if(!g_Icons[eIcon].bEnable) { //
// Turn the icon on
//
if(g_ConfigOptions.bSoundOnSent) { if(!PlaySound(g_Icons[eIcon].pctsSound, NULL, SND_ASYNC | SND_APPLICATION | SND_NODEFAULT)) { CALL_FAIL (WINDOW_ERR, TEXT ("PlaySound"), 0); } }
g_BalloonInfo.bEnable = TRUE; g_BalloonInfo.eState = eIcon;
//
// Compose the balloon
//
if (ERROR_SUCCESS != LoadAndFormatString (IDS_SEND_OK, tsFormat, ARR_SIZE(tsFormat))) { return; } _tcsncpy(g_BalloonInfo.szInfoTitle, tsFormat, MAX_BALLOON_TITLE_LEN-1); if (ERROR_SUCCESS != LoadAndFormatString (IDS_SEND_OK_BALLOON, g_BalloonInfo.szInfo, ARR_SIZE(g_BalloonInfo.szInfo), g_szRemoteId)) { return; } } break;
default: break; }
exit: g_Icons[eIcon].bEnable = bEnable; g_Icons[eIcon].tszToolTip[TOOLTIP_SIZE -1] = _T('\0');
EvaluateIcon(); } // SetIconState
VOID CALLBACK RingTimerProc( HWND hwnd, // handle to window
UINT uMsg, // WM_TIMER message
UINT_PTR idEvent, // timer identifier
DWORD dwTime // current system time
) /*++
Routine description:
Animate ringing icon
Arguments:
hwnd - handle to window uMsg - WM_TIMER message idEvent - timer identifier dwTime - current system time
Return Value:
none
--*/ { DBG_ENTER(TEXT("RingTimerProc")); if ((GetTickCount() - g_dwRingAnimationStartTick) > RING_ANIMATION_TIMEOUT) { //
// Animation has expired - keep static icon
//
g_Icons[ICON_RINGING].hIcon = g_RingIcons[0].hIcon; if(!KillTimer(NULL, g_uRingTimerID)) { CALL_FAIL (GENERAL_ERR, TEXT ("KillTimer"), GetLastError()); } g_uRingTimerID = 0; g_dwRingAnimationStartTick = 0; } else { g_dwCurrRingIconIndex = (g_dwCurrRingIconIndex + 1) % RING_ICONS_NUM; g_Icons[ICON_RINGING].hIcon = g_RingIcons[g_dwCurrRingIconIndex].hIcon; } EvaluateIcon(); } // RingTimerProc
VOID InvokeClientConsole () /*++
Routine description:
Invoke Client Console
Arguments:
none
Return Value:
none
--*/ { DBG_ENTER(TEXT("InvokeClientConsole"));
TCHAR szCmdLine[MAX_PATH]; static TCHAR szFmtMsg[] = TEXT(" -folder %s -MessageId %I64x"); static TCHAR szFmtNoMsg[] = TEXT(" -folder %s");
DWORDLONG dwlMsgId = 0; LPCWSTR lpcwstrFolder = TEXT("");
switch (g_CurrentIcon) { case ICON_RINGING: // Line is ringing - nothing special to do
case ICON_RECEIVE_FAILED: // Receive operation failed - nothing special to do
default: // Any other icon state - nothing special to do
break;
case ICON_SENDING: //
// Device is sending - open fax console in Outbox folder
//
dwlMsgId = g_dwlCurrentMsgID; lpcwstrFolder = CONSOLE_CMD_PRM_STR_OUTBOX; break;
case ICON_SEND_FAILED: //
// Send operation failed - open fax console in Outbox folder
//
dwlMsgId = g_dwlSendFailedMsgId; lpcwstrFolder = CONSOLE_CMD_PRM_STR_OUTBOX; break;
case ICON_RECEIVING: //
// Device is receiving - open fax console in Incoming folder
//
dwlMsgId = g_dwlCurrentMsgID; lpcwstrFolder = CONSOLE_CMD_PRM_STR_INCOMING; break; break;
case ICON_NEW_FAX: //
// New unread fax - open fax console in Inbox folder
//
dwlMsgId = g_dwlNewMsgId; lpcwstrFolder = CONSOLE_CMD_PRM_STR_INBOX; break;
case ICON_SEND_SUCCESS: //
// Send was successful - open fax console in Sent Items folder
//
dwlMsgId = g_dwlSendSuccessMsgId; lpcwstrFolder = CONSOLE_CMD_PRM_STR_SENT_ITEMS; break; }
if (dwlMsgId) { wsprintf (szCmdLine, szFmtMsg, lpcwstrFolder, dwlMsgId); } else { wsprintf (szCmdLine, szFmtNoMsg, lpcwstrFolder); }
HINSTANCE hRes; hRes = ShellExecute(g_hWndFaxNotify, NULL, FAX_CLIENT_CONSOLE_IMAGE_NAME, szCmdLine, NULL, SW_SHOW); if((DWORD_PTR)hRes <= 32) { //
// error
//
CALL_FAIL (GENERAL_ERR, TEXT("ShellExecute"), PtrToUlong(hRes)); }
} // InvokeClientConsole
VOID AnswerTheCall () /*++
Routine description:
Answer the current incoming call
Arguments:
none
Return Value:
none
--*/ { DBG_ENTER(TEXT("AnswerTheCall")); DWORD dwDeviceId;
//
// Check for 'Answer now' capabilities and auto-detect the device id.
//
DWORD dwRes = CheckAnswerNowCapability (TRUE, // Start service if necessary
&dwDeviceId); // Get device id for FaxAnswerCall
if (ERROR_SUCCESS != dwRes) { //
// Can't 'Answer Now' - dwRes has the string resource id for the message to show to the user.
//
FaxMessageBox (g_hMonitorDlg, dwRes, MB_OK | MB_ICONEXCLAMATION); return; }
//
// Reset remote ID
//
_tcscpy(g_szRemoteId, TEXT(""));
//
// Looks like we have a chance of FaxAnswerCall succeeding - let's try it.
// First, open the monitor (or make sure it's already open).
//
OpenFaxMonitor (); //
// Start by disabling the 'Answer Now' button on the monitor dialog
//
if (g_hMonitorDlg) { //
// Monitor dialog is there
//
HWND hWndAnswerNow = GetDlgItem(g_hMonitorDlg, IDC_DISCONNECT); if(hWndAnswerNow) { EnableWindow(hWndAnswerNow, FALSE); } } //
// Call is gone
//
g_hCall = NULL; SetIconState(ICON_RINGING, FALSE, TEXT(""));
if(!FaxAnswerCall(g_hFaxSvcHandle, dwDeviceId)) { CALL_FAIL (RPC_ERR, TEXT ("FaxAnswerCall"), GetLastError()); FaxMessageBox(g_hWndFaxNotify, IDS_CANNOT_ANSWER, MB_OK | MB_ICONEXCLAMATION); SetStatusMonitorDeviceState (FAX_IDLE); } else { g_tszLastEvent[0] = TEXT('\0'); SetStatusMonitorDeviceState(FAX_RECEIVING); } } // AnswerTheCall
VOID FaxPrinterProperties(DWORD dwPage) /*++
Routine description:
Open Fax Printer Property Sheet
Arguments:
dwPage - page number
Return Value:
none
--*/ { DBG_ENTER(TEXT("FaxPrinterProperties"));
//
// open fax printer properties on the Tracking page
//
TCHAR tsPrinter[MAX_PATH];
typedef VOID (*PRINTER_PROP_PAGES_PROC)(HWND, LPCTSTR, INT, LPARAM);
HMODULE hPrintUI = NULL; PRINTER_PROP_PAGES_PROC fpPrnPropPages = NULL;
if(!GetFirstLocalFaxPrinterName(tsPrinter, MAX_PATH)) { CALL_FAIL (GENERAL_ERR, TEXT ("GetFirstLocalFaxPrinterName"), GetLastError()); return; } hPrintUI = LoadLibrary(TEXT("printui.dll")); if(!hPrintUI) { CALL_FAIL (GENERAL_ERR, TEXT ("LoadLibrary(printui.dll)"), GetLastError()); return; }
fpPrnPropPages = (PRINTER_PROP_PAGES_PROC)GetProcAddress(hPrintUI, "vPrinterPropPages"); if(fpPrnPropPages) { fpPrnPropPages(g_hWndFaxNotify, tsPrinter, SW_SHOWNORMAL, dwPage); } else { CALL_FAIL (GENERAL_ERR, TEXT ("GetProcAddress(vPrinterPropPages)"), GetLastError()); } FreeLibrary(hPrintUI);
} // FaxPrinterProperties
VOID DoFaxContextMenu (HWND hwnd) /*++
Routine description:
Popup and handle context menu
Arguments:
hwnd - notification window handle
Return Value:
none
--*/ { DBG_ENTER(TEXT("DoFaxContextMenu"));
POINT pt; HMENU hm = LoadMenu (g_hResource, MAKEINTRESOURCE (IDM_FAX_MENU)); HMENU hmPopup = GetSubMenu(hm, 0);
if (!g_Icons[ICON_RINGING].bEnable) { RemoveMenu (hmPopup, ID_ANSWER_CALL, MF_BYCOMMAND); }
if(g_dwCurrentJobID == 0) { RemoveMenu (hmPopup, ID_DISCONNECT_CALL, MF_BYCOMMAND); }
if(!g_Icons[ICON_RINGING].bEnable && g_dwCurrentJobID == 0) { //
// delete the menu separator
//
DeleteMenu(hmPopup, 0, MF_BYPOSITION); }
SetMenuDefaultItem(hmPopup, ID_FAX_QUEUE, FALSE);
GetCursorPos (&pt); SetForegroundWindow(hwnd);
INT idCmd = TrackPopupMenu (GetSubMenu(hm, 0), TPM_RETURNCMD | TPM_NONOTIFY, pt.x, pt.y, 0, hwnd, NULL); switch (idCmd) { case ID_ICON_PROPERTIES: FaxPrinterProperties(IsSimpleUI() ? 3 : 5); break;
case ID_FAX_QUEUE: InvokeClientConsole (); break;
case ID_ANSWER_CALL: AnswerTheCall (); break;
case ID_FAX_MONITOR: OpenFaxMonitor (); break;
case ID_DISCONNECT_CALL: OnDisconnect(); break; } if (hm) { DestroyMenu (hm); } } // DoFaxContextMenu
VOID OnTrayCallback (HWND hwnd, WPARAM wp, LPARAM lp) /*++
Routine description:
Handle messages from the notification icon
Arguments:
hwnd - notification window handle wp - message parameter lp - message parameter
Return Value:
none
--*/ { DBG_ENTER(TEXT("OnTrayCallback"), TEXT("hWnd=%08x, wParam=%08x, lParam=%08x"), hwnd, wp, lp);
switch (lp) { case NIN_BALLOONUSERCLICK: // User clicked balloon or (WM_USER + 5 = 1029)
case WM_LBUTTONDOWN: // User pressed icon (513)
{ //
// Our behavior depends on the icon currently being displyed
//
switch (g_CurrentIcon) { case ICON_RINGING: //
// Device is ringing - answer the call
//
AnswerTheCall (); break;
case ICON_NEW_FAX: // New unread fax - open fax console in Inbox folder
case ICON_SEND_SUCCESS: // Send was successful - open fax console in Sent Items folder
case ICON_SEND_FAILED: // Send operation failed - open fax console in Outbox folder
//
// Turn off the current icon state
//
InvokeClientConsole (); SetIconState(g_CurrentIcon, FALSE); break;
case ICON_SENDING: // Device is sending - open fax console in Outbox folder
case ICON_RECEIVING: // Device is receiving - open fax console in Incoming folder
InvokeClientConsole (); break;
case ICON_RECEIVE_FAILED: //
// Receive operation failed
//
SetIconState(g_CurrentIcon, FALSE); break;
default: //
// When balloon is opened and the user clicks on the icon we get two notifications
// NIN_BALLOONUSERCLICK and WM_LBUTTONDOWN. The first one reset the icon state and the second do nothing.
//
break; } } //
// no break ==> fall-through
//
case NIN_BALLOONTIMEOUT: if (g_BalloonInfo.eState == ICON_RECEIVE_FAILED || g_BalloonInfo.eState == ICON_SEND_SUCCESS) { SetIconState(g_BalloonInfo.eState, FALSE); } g_BalloonInfo.eState = ICON_IDLE; break;
case WM_RBUTTONDOWN: DoFaxContextMenu (hwnd); break;
} } // OnTrayCallback
BOOL IsUserGrantedAccess( DWORD dwAccess ) { BOOL bRes = FALSE; DBG_ENTER(TEXT("IsUserGrantedAccess"), bRes, TEXT("%d"), dwAccess); if (!g_hFaxSvcHandle) { //
// Not connected - no rights
//
return bRes; } if (dwAccess == (g_ConfigOptions.dwAccessRights & dwAccess)) { bRes = TRUE; } return bRes; } // IsUserGrantedAccess
DWORD CheckAnswerNowCapability ( BOOL bForceReconnect, LPDWORD lpdwDeviceId /* = NULL */ ) /*++
Routine name : CheckAnswerNowCapability
Routine description:
Checks if the 'Answer Now' option can be used
Author:
Eran Yariv (EranY), Mar, 2001
Arguments:
bForceReconnect [in] - If the service is down, should we bring it up now? lpdwDeviceId [out] - The device id to use when calling FaxAnswerCall. If the Manual-Answer-Device is ringing, we use the Manual-Answer-Device id. Otherwise, it's the monitored device id. (Optional)
Return Value:
ERROR_SUCCESS if the 'Answer Now' can be used. Othewise, returns a string resource id that can be used in a message box to tell the user why 'Answer Now' is not available.
--*/ { DWORD dwRes = ERROR_SUCCESS; DBG_ENTER(TEXT("CheckAnswerNowCapability"), dwRes); //
// First, let's see if we're connected to the local server
//
if (NULL == g_hFaxSvcHandle) { //
// Service is down
//
if (!bForceReconnect) { //
// We assume the user can 'Answer now'
//
ASSERTION (NULL == lpdwDeviceId); return dwRes; } //
// Try to start up the local fax service
//
if (!Connect()) { //
// Couldn't start up the service
//
dwRes = GetLastError (); CALL_FAIL (GENERAL_ERR, TEXT("Connect"), dwRes); dwRes = IDS_ERR_CANT_TALK_TO_SERVICE; return dwRes; } //
// Now that the service is up - we need to connect.
// Send a message to the main window to bring up the connection.
//
if (!SendMessage (g_hWndFaxNotify, WM_FAX_STARTED, 0, 0)) { //
// Failed to connect
//
dwRes = IDS_ERR_CANT_TALK_TO_SERVICE; return dwRes; } //
// Now we're connected !!!
//
} if (!IsUserGrantedAccess (FAX_ACCESS_QUERY_IN_ARCHIVE)) { //
// User can't receive-now
//
dwRes = IDS_ERR_ANSWER_ACCESS_DENIED; return dwRes; } if (0 == g_ConfigOptions.dwMonitorDeviceId) { //
// No devices
//
dwRes = IDS_ERR_NO_DEVICES; return dwRes; } if (g_hCall) { //
// The Manual-Answer-Device is ringing, we use the Manual-Answer-Device id.
//
ASSERTION (g_ConfigOptions.dwManualAnswerDeviceId); if (lpdwDeviceId) { *lpdwDeviceId = g_ConfigOptions.dwManualAnswerDeviceId; } return dwRes; } //
// The Manual-Answer-Device is NOT ringing; we should receive on the monitored device
//
if ((0 != g_dwCurrentJobID) || (FAX_IDLE != g_devState)) { //
// There's a job on the monitored device
//
dwRes = IDS_ERR_DEVICE_BUSY; return dwRes; } //
// One last check - is the monitored device virtual?
//
BOOL bVirtual; dwRes = IsDeviceVirtual (g_hFaxSvcHandle, g_ConfigOptions.dwMonitorDeviceId, &bVirtual); if (ERROR_SUCCESS != dwRes) { //
// Can't tell - assume virtual
//
bVirtual = TRUE; } if (bVirtual) { //
// Sorry, manual answering on virtual devices is NOT supported
//
dwRes = IDS_ERROR_VIRTUAL_DEVICE; return dwRes; } //
// It's ok to call FaxAnswerCall on the monitored device
//
if (lpdwDeviceId) { *lpdwDeviceId = g_ConfigOptions.dwMonitorDeviceId; } return dwRes; } // CheckAnswerNowCapability
LRESULT CALLBACK NotifyWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) /*++
Routine description:
Notification window procedure
Arguments:
hwnd - notification window handle msg - message ID wp - message parameter lp - message parameter
Return Value:
result
--*/ { switch (msg) { case WM_CREATE: break;
case WM_FAX_STARTED: //
// We get this message after service startup event
//
return RegisterForServerEvents();
case WM_TRAYCALLBACK: OnTrayCallback (hwnd, wp, lp); break;
case WM_FAX_EVENT:
#ifndef DEBUG
try { #endif
OnFaxEvent ((FAX_EVENT_EX*)lp); #ifndef DEBUG
} catch(...) { //
// Do not handle the exception for the debug version
//
DBG_ENTER(TEXT("NotifyWndProc")); CALL_FAIL (GENERAL_ERR, TEXT("OnFaxEvent"), 0); return 0; } #endif
return 0;
case WM_FAXSTAT_CONTROLPANEL: //
// configuration has been changed
//
GetConfiguration (); EvaluateIcon(); UpdateMonitorData(g_hMonitorDlg); return 0;
case WM_FAXSTAT_OPEN_MONITOR: OpenFaxMonitor (); return 0;
case WM_FAXSTAT_INBOX_VIEWED: //
// Client Console Inbox has been viewed
//
SetIconState(ICON_NEW_FAX, FALSE); return 0;
case WM_FAXSTAT_OUTBOX_VIEWED: //
// Client Console Outbox has been viewed
//
SetIconState(ICON_SEND_FAILED, FALSE); return 0;
case WM_FAXSTAT_RECEIVE_NOW: //
// Start receiving now
//
AnswerTheCall (); return 0;
case WM_FAXSTAT_PRINTER_PROPERTY: //
// Open Fax Printer Property Sheet
//
FaxPrinterProperties((DWORD)(wp)); return 0;
default: break; } return CallWindowProc (DefWindowProc, hwnd, msg, wp, lp); } // NotifyWndProc
VOID CopyLTRString( TCHAR* szDest, LPCTSTR szSource, DWORD dwSize) /*++
Routine description:
Copy the string and add left-to-right Unicode control characters if needed
Arguments:
szDest - destination string szSource - source string dwSize - destination string maximum size in characters
Return Value:
none
--*/ { DBG_ENTER(TEXT("CopyLTRString"));
if(!szDest) { ASSERTION_FAILURE; return; }
if(IsRTLUILanguage() && szSource && _tcslen(szSource)) { //
// The string always should be LTR
// Add LEFT-TO-RIGHT OVERRIDE (LRO)
//
_sntprintf(szDest, dwSize -1, TEXT("%c%s%c"), UNICODE_LRO, szSource, UNICODE_PDF); szDest[dwSize -1] = _T('\0');
} else { _tcsncpy(szDest, szSource ? szSource : TEXT(""), dwSize); }
} // CopyLTRString
|