/*++

Copyright (c) 1998 Microsoft Corporation

Module Name:

  faxqueue.cpp

Abstract:

  This module implements the fax queue viewer

Environment:

  WIN32 User Mode

Author:

  Wesley Witt (wesw) 9-june-1997
  Steven Kehrli (steveke) 30-oct-1998 - major rewrite

--*/

#include "faxqueue.h"

HINSTANCE  g_hInstance;            // g_hInstance is the handle to the instance
HWND       g_hWndMain;             // g_hWndMain is the handle to the parent window
HWND       g_hWndListView;         // g_hWndListView is the handle to the list view window

HWND       g_hWndToolbar;          // g_hWndToolbar is the handle to the toolbar

LPTSTR     g_szTitleConnected;     // g_szTitleConnected is the window title when connected
LPTSTR     g_szTitleNotConnected;  // g_szTitleNotConnected is the window title when not connected
LPTSTR     g_szTitleConnecting;    // g_szTitleConnecting is the window title when connecting
LPTSTR     g_szTitleRefreshing;    // g_szTitleRefreshing is the window title when refreshing
LPTSTR     g_szTitlePaused;        // g_szTitlePaused is the window title when paused

LPTSTR     g_szCurrentUserName;    // g_szCurrentUserName is the name of the current user

HANDLE     g_hStartEvent;          // g_hStartEvent is the handle to an event indicating the fax event queue exists
HANDLE     g_hExitEvent;           // g_hExitEvent is the handle to an event indicating the application is exiting

LPTSTR     g_szMachineName;        // g_szMachineName is the machine to connect to
HANDLE     g_hFaxSvcMutex;         // g_hFaxSvcMutex is an object to synchronize access to the fax service routines
HANDLE     g_hFaxSvcHandle;        // g_hFaxSvcHandle is the handle to the fax service
LONG       g_nNumConnections;      // g_nNumConnections is the number of connections to the fax service
HANDLE     g_hCompletionPort;      // g_hCompletionPort is the handle to the completion port

WINPOSINFO  WinPosInfo =
{
#ifdef DEBUG
    FALSE,
#endif // DEBUG
#ifdef TOOLBAR_ENABLED
    FALSE,  // toolbar
#endif // TOOLBAR_ENABLED
    TRUE,   // status bar
    200,    // eDocumentName
    80,     // eJobType
    80,     // eStatus
    80,     // eOwner
    60,     // ePages
    100,    // eSize
    140,    // eScheduledTime
    120,    // ePort
    {sizeof(WINDOWPLACEMENT), 0, SW_SHOWNORMAL, {0, 0}, {0, 0}, {50, 100, 695, 300}}
};

DWORD DocumentPropertiesHelpIDs[] =
{
    IDC_FAX_DOCUMENTNAME,         IDH_QUEUE_DOCUMENTNAME,
    IDC_FAX_RECIPIENTINFO,        IDH_QUEUE_RECIPIENTINFO,
    IDC_FAX_RECIPIENTNAME_TEXT,   IDH_QUEUE_RECIPIENTNAME,
    IDC_FAX_RECIPIENTNAME,        IDH_QUEUE_RECIPIENTNAME,
    IDC_FAX_RECIPIENTNUMBER_TEXT, IDH_QUEUE_RECIPIENTNUMBER,
    IDC_FAX_RECIPIENTNUMBER,      IDH_QUEUE_RECIPIENTNUMBER,
    IDC_FAX_SENDERINFO,           IDH_QUEUE_SENDERINFO,
    IDC_FAX_SENDERNAME_TEXT,      IDH_QUEUE_SENDERNAME,
    IDC_FAX_SENDERNAME,           IDH_QUEUE_SENDERNAME,
    IDC_FAX_SENDERCOMPANY_TEXT,   IDH_QUEUE_SENDERCOMPANY,
    IDC_FAX_SENDERCOMPANY,        IDH_QUEUE_SENDERCOMPANY,
    IDC_FAX_SENDERDEPT_TEXT,      IDH_QUEUE_SENDERDEPT,
    IDC_FAX_SENDERDEPT,           IDH_QUEUE_SENDERDEPT,
    IDC_FAX_BILLINGCODE_TEXT,     IDH_QUEUE_BILLINGCODE,
    IDC_FAX_BILLINGCODE,          IDH_QUEUE_BILLINGCODE,
    IDC_FAX_FAXINFO,              IDH_QUEUE_FAXINFO,
    IDC_FAX_JOBTYPE_TEXT,         IDH_QUEUE_JOBTYPE,
    IDC_FAX_JOBTYPE,              IDH_QUEUE_JOBTYPE,
    IDC_FAX_STATUS_TEXT,          IDH_QUEUE_STATUS,
    IDC_FAX_STATUS,               IDH_QUEUE_STATUS,
    IDC_FAX_PAGES_TEXT,           IDH_QUEUE_PAGES,
    IDC_FAX_PAGES,                IDH_QUEUE_PAGES,
    IDC_FAX_SIZE_TEXT,            IDH_QUEUE_SIZE,
    IDC_FAX_SIZE,                 IDH_QUEUE_SIZE,
    IDC_FAX_SCHEDULEDTIME_TEXT,   IDH_QUEUE_SCHEDULEDTIME,
    IDC_FAX_SCHEDULEDTIME,        IDH_QUEUE_SCHEDULEDTIME,
    0,                    0
};

// MainWndProc is the parent window procedure
LRESULT CALLBACK MainWndProc (HWND hWndMain, UINT iMsg, WPARAM wParam, LPARAM lParam);

// SelectFaxPrinterDlgProc is the select fax printer dialog procedure
INT_PTR CALLBACK SelectFaxPrinterDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam);

// DocumentPropertiesDlgProc is the select fax printer dialog procedure
INT_PTR CALLBACK DocumentPropertiesDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam);

// EnumThreadWndProc is the callback function for the EnumThreadWindows call
BOOL CALLBACK EnumThreadWndProc(HWND hWnd, LPARAM lParam);

// PostEventToCompletionPort posts an exit packet to a completion port
VOID PostEventToCompletionPort(HANDLE hCompletionPort, DWORD dwEventId, DWORD dwJobId);

// FaxEventThread is the thread to handle the fax events
DWORD FaxEventThread (LPVOID lpv);

extern "C"
int __cdecl
_tmain(
    int     argc,
    LPTSTR  argv[]
)
{
    WNDCLASSEX  wndclass;
    MSG         msg;

    // bStarted indicates if the application has started
    BOOL        bStarted;

    // szFormat is a format string
    TCHAR       szFormat[RESOURCE_STRING_LEN];

    // szComputerName is the local computer name
    LPTSTR      szComputerName;
    DWORD       dwComputerNameSize;

    // hThread is the handle to a thread
    HANDLE      hThread;
    // hAccel is the handle to the accelerators
    HACCEL      hAccel;

    DWORD       dwReturn;
    DWORD       cb;

    // Set g_hInstance
    g_hInstance = GetModuleHandle(NULL);

    // Set bStarted to FALSE to indicate the application has not started
    bStarted = FALSE;

    // Get the machine name
    g_szMachineName = NULL;
    if (argc == 2) {
        // Allocate the memory for the machine name
        g_szMachineName = (LPTSTR) MemAlloc((lstrlen(argv[1]) + 1) * sizeof(TCHAR));
        if (g_szMachineName) {
            // Copy the machine name
            lstrcpy(g_szMachineName, argv[1]);

            // Allocate the memory for the local computer name
            dwComputerNameSize = MAX_COMPUTERNAME_LENGTH + 1;
            szComputerName = (LPTSTR) MemAlloc((MAX_COMPUTERNAME_LENGTH + 1) * sizeof(TCHAR));
            if (szComputerName) {
                // Get the local computer name
                if (GetComputerName(szComputerName, &dwComputerNameSize)) {
                    // Compare the local computer name against the machine name
                    if (!lstrcmpi(g_szMachineName, szComputerName)) {
                        // Local computer name and machine name are the same, so set machine name to NULL
                        MemFree(g_szMachineName);
                        g_szMachineName = NULL;
                    }
                }

                MemFree(szComputerName);
            }
        }
    }

    // Set the window title when connected
    if (g_szMachineName) {
        // Convert the machine name to uppercase
        g_szMachineName = CharUpper(g_szMachineName);

        LoadString(g_hInstance, IDS_FAXQUEUE_REMOTE_CAPTION, szFormat, RESOURCE_STRING_LEN);
        // Allocate the memory for the window title
        g_szTitleConnected = (LPTSTR) MemAlloc((lstrlen(szFormat) + lstrlen(g_szMachineName) + 1) * sizeof(TCHAR));

        if (!g_szTitleConnected) {
            // Set the exit code
            dwReturn = GetLastError();

            goto ExitLevel0;
        }

        wsprintf(g_szTitleConnected, szFormat, g_szMachineName);
    }
    else {
        LoadString(g_hInstance, IDS_FAXQUEUE_LOCAL_CAPTION, szFormat, RESOURCE_STRING_LEN);
        // Allocate the memory for the window title
        g_szTitleConnected = (LPTSTR) MemAlloc((lstrlen(szFormat) + 1) * sizeof(TCHAR));

        if (!g_szTitleConnected) {
            // Set the exit code
            dwReturn = GetLastError();

            goto ExitLevel0;
        }

        lstrcpy(g_szTitleConnected, szFormat);
    }

    // Set the window title when not connected
    LoadString(g_hInstance, IDS_FAXQUEUE_NOT_CONNECTED, szFormat, RESOURCE_STRING_LEN);
    // Allocate the memory for the window title
    g_szTitleNotConnected = (LPTSTR) MemAlloc((lstrlen(g_szTitleConnected) + lstrlen(szFormat) + 1) * sizeof(TCHAR));
    if (!g_szTitleNotConnected) {
        // Set the exit code
        dwReturn = GetLastError();

        goto ExitLevel1;
    }
    lstrcpy(g_szTitleNotConnected, g_szTitleConnected);
    lstrcat(g_szTitleNotConnected, szFormat);

    // Set the window title when connecting
    LoadString(g_hInstance, IDS_FAXQUEUE_CONNECTING, szFormat, RESOURCE_STRING_LEN);
    // Allocate the memory for the window title
    g_szTitleConnecting = (LPTSTR) MemAlloc((lstrlen(g_szTitleConnected) + lstrlen(szFormat) + 1) * sizeof(TCHAR));
    if (!g_szTitleConnecting) {
        // Set the exit code
        dwReturn = GetLastError();

        goto ExitLevel2;
    }
    lstrcpy(g_szTitleConnecting, g_szTitleConnected);
    lstrcat(g_szTitleConnecting, szFormat);

    // Set the window title when refreshing
    LoadString(g_hInstance, IDS_FAXQUEUE_REFRESHING, szFormat, RESOURCE_STRING_LEN);
    // Allocate the memory for the window title
    g_szTitleRefreshing = (LPTSTR) MemAlloc((lstrlen(g_szTitleConnected) + lstrlen(szFormat) + 1) * sizeof(TCHAR));
    if (!g_szTitleRefreshing) {
        // Set the exit code
        dwReturn = GetLastError();

        goto ExitLevel3;
    }
    lstrcpy(g_szTitleRefreshing, g_szTitleConnected);
    lstrcat(g_szTitleRefreshing, szFormat);

    // Set the window title when paused
    LoadString(g_hInstance, IDS_FAXQUEUE_PAUSED, szFormat, RESOURCE_STRING_LEN);
    // Allocate the memory for the window title
    g_szTitlePaused = (LPTSTR) MemAlloc((lstrlen(g_szTitleConnected) + lstrlen(szFormat) + 1) * sizeof(TCHAR));
    if (!g_szTitlePaused) {
        // Set the exit code
        dwReturn = GetLastError();

        goto ExitLevel4;
    }
    lstrcpy(g_szTitlePaused, g_szTitleConnected);
    lstrcat(g_szTitlePaused, szFormat);

    g_hWndMain = NULL;

    // Find the window
    g_hWndMain = FindWindow(FAXQUEUE_WINCLASS, g_szTitlePaused);
    if (g_hWndMain) {
        goto ExitLevel5;
    }

    // Find the window
    g_hWndMain = FindWindow(FAXQUEUE_WINCLASS, g_szTitleRefreshing);
    if (g_hWndMain) {
        goto ExitLevel5;
    }

    // Find the window
    g_hWndMain = FindWindow(FAXQUEUE_WINCLASS, g_szTitleConnecting);
    if (g_hWndMain) {
        goto ExitLevel5;
    }

    // Find the window
    g_hWndMain = FindWindow(FAXQUEUE_WINCLASS, g_szTitleNotConnected);
    if (g_hWndMain) {
        goto ExitLevel5;
    }

    // Find the window
    g_hWndMain = FindWindow(FAXQUEUE_WINCLASS, g_szTitleConnected);
    if (g_hWndMain) {
        goto ExitLevel5;
    }

    // Create the g_hStartEvent
    g_hStartEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!g_hStartEvent) {
        // Set the exit code
        dwReturn = GetLastError();

        goto ExitLevel5;
    }

    // Create the g_hExitEvent
    g_hExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!g_hExitEvent) {
        // Set the exit code
        dwReturn = GetLastError();

        goto ExitLevel6;
    }

    // Initialize the fax service
    g_hFaxSvcMutex = CreateMutex(NULL, FALSE, NULL);
    if (!g_hFaxSvcMutex) {
        // Set the exit code
        dwReturn = GetLastError();

        goto ExitLevel7;
    }
    g_hFaxSvcHandle = NULL;
    g_nNumConnections = 0;

    hThread = CreateThread(NULL, 0, FaxEventThread, NULL, 0, NULL);
    if (!hThread) {
        // Set the exit code
        dwReturn = GetLastError();

        goto ExitLevel8;
    }
    CloseHandle(hThread);

    // Load the accelerators
    hAccel = LoadAccelerators(g_hInstance, MAKEINTRESOURCE(IDR_ACCEL));
    if (!hAccel) {
        // Set the exit code
        dwReturn = GetLastError();

        goto ExitLevel9;
    }

    // Get the name of the current user
    cb = 0;
    GetUserName(NULL, &cb);
    g_szCurrentUserName = (LPTSTR) MemAlloc((cb + 1) * sizeof(TCHAR));
    if (!GetUserName(g_szCurrentUserName, &cb)) {
        MemFree(g_szCurrentUserName);
        g_szCurrentUserName = NULL;
    }

    // Retrieve the persistent data
    GetFaxQueueRegistryData(&WinPosInfo);

    // Initialize the common controls
    InitCommonControls();

    // Set bStarted to TRUE to indicate the application has started
    bStarted = TRUE;

    // Initialize the wndclass
    wndclass.cbSize = sizeof(wndclass);
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = MainWndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = g_hInstance;
    wndclass.hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_FAXQUEUE_ICON));
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    wndclass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU);
    wndclass.lpszClassName = FAXQUEUE_WINCLASS;
    wndclass.hIconSm = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_FAXQUEUE_ICON));

    RegisterClassEx(&wndclass);

    g_hWndMain = CreateWindow(FAXQUEUE_WINCLASS, g_szTitleConnecting, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, g_hInstance, NULL);

    // Move and show the window
    SetWindowPlacement(g_hWndMain, &WinPosInfo.WindowPlacement);
    ShowWindow(g_hWndMain, SW_SHOWNORMAL);
    UpdateWindow(g_hWndMain);

    // Set the start event
    SetEvent(g_hStartEvent);

    while (GetMessage(&msg, NULL, 0, 0)) {
        if (!TranslateAccelerator(g_hWndMain, hAccel, &msg)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    if (g_szCurrentUserName) {
        MemFree(g_szCurrentUserName);
    }

ExitLevel9:
    SetEvent(g_hExitEvent);

ExitLevel8:
    CloseHandle(g_hFaxSvcMutex);

ExitLevel7:
    CloseHandle(g_hExitEvent);

ExitLevel6:
    CloseHandle(g_hStartEvent);

ExitLevel5:
    if ((g_hWndMain) && (!bStarted)) {
        // g_hWndMain is valid but the application has not started, so it must already be running
        // Switch to that window
        ShowWindow(g_hWndMain, SW_RESTORE);
        SetForegroundWindow(g_hWndMain);

        // Set the exit code
        dwReturn = 0;
        // Set bStarted to TRUE to indicate the application has started
        bStarted = TRUE;
    }

    MemFree(g_szTitlePaused);

ExitLevel4:
    MemFree(g_szTitleRefreshing);

ExitLevel3:
    MemFree(g_szTitleConnecting);

ExitLevel2:
    MemFree(g_szTitleNotConnected);

ExitLevel1:
    MemFree(g_szTitleConnected);

ExitLevel0:
    if (g_szMachineName) {
        MemFree(g_szMachineName);
    }

    if (!bStarted) {
        // szErrorCaption is the error caption if the application cannot start
        TCHAR       szErrorCaption[RESOURCE_STRING_LEN];
        // szErrorFormat is the format of the error message if the application cannot start
        TCHAR       szErrorFormat[RESOURCE_STRING_LEN];
        // szErrorReason is the error reason if the application cannot start
        LPTSTR      szErrorReason;
        // szErrorMessage is the error message if the application cannot start
        LPTSTR      szErrorMessage;

        // The application did not start so display an error message
        // Load the error caption
        LoadString(g_hInstance, IDS_ERROR_CAPTION, szErrorCaption, RESOURCE_STRING_LEN);

        // Try to get the error message from the system message table
        szErrorMessage = NULL;
        if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwReturn, 0, (LPTSTR) &szErrorReason, 0, NULL)) {
            // Load the error format
            LoadString(g_hInstance, IDS_ERROR_APP_FAILED_FORMAT, szErrorFormat, RESOURCE_STRING_LEN);

            // Allocate the memory for the error message
            szErrorMessage = (LPTSTR) MemAlloc((lstrlen(szErrorReason) + RESOURCE_STRING_LEN + 1) * sizeof(TCHAR));
            if (szErrorMessage) {
                // Set the error message
                wsprintf(szErrorMessage, szErrorFormat, szErrorReason);
            }
            LocalFree(szErrorReason);
        }

        if (!szErrorMessage) {
            // Allocate the memory for the error message
            szErrorMessage = (LPTSTR) MemAlloc((RESOURCE_STRING_LEN) * sizeof(TCHAR));
            if (szErrorMessage) {
                // Load the error message
                LoadString(g_hInstance, IDS_ERROR_APP_FAILED, szErrorMessage, RESOURCE_STRING_LEN);
            }
        }

        if (szErrorMessage) {
            // Display the error message
            MessageBox(NULL, szErrorMessage, szErrorCaption, MB_OK | MB_ICONERROR | MB_APPLMODAL);
            MemFree(szErrorMessage);
        }
    }

    return dwReturn;
}

LRESULT CALLBACK MainWndProc (HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
    // rcClient is the rectangle of the client area
    RECT                rcClient;

    // hWndToolTips is the handle to the tool tips window
    static HWND         hWndToolTips;
    // rcToolbar is the rectangle of the toolbar
    static RECT         rcToolbar;

    // hWndStatusBar is the handle to the status bar
    static HWND         hWndStatusBar;
    // rcStatusBar is the rectangle of the status bar
    static RECT         rcStatusBar;

    // hFaxMenu is the handle to the fax menu
    static HMENU        hFaxMenu;
    // hFaxSetAsDefaultMenu is the handle to the set as default printer menu
    static HMENU        hFaxSetAsDefaultMenu;
    // hFaxSharingMenu is the handle to the sharing menu
    static HMENU        hFaxSharingMenu;
    // hFaxPropertiesMenu is the handle to the properties menu
    static HMENU        hFaxPropertiesMenu;

    // hDocumentMenu is the handle to the document menu
    static HMENU        hDocumentMenu;

    // hViewMenu is the handle to the view menu
    static HMENU        hViewMenu;

    // uCurrentMenu indicates the current menu selection
    static UINT         uCurrentMenu;

    // pProcessInfoList is a pointer to the process info list
    static PLIST_ENTRY  pProcessInfoList;

    switch (iMsg) {
        case WM_CREATE:
            // lvc specifies the attributes of a particular column of the list view
            LV_COLUMN  lvc;
            // nColumnIndex is used to enumerate each column of the list view
            INT        nColumnIndex;
            // szColumnHeader is the column header text
            TCHAR      szColumnHeader[RESOURCE_STRING_LEN];

            // Set pProcessInfoList to NULL
            pProcessInfoList = NULL;

            // Get the handle to the fax menu
            hFaxMenu = GetSubMenu(GetMenu(hWnd), 0);
            // Initialize the set as default printer menu
            hFaxSetAsDefaultMenu = NULL;
            // Initialize the sharing menu
            hFaxSharingMenu = NULL;
            // Initialize the properties menu
            hFaxPropertiesMenu = NULL;

            // Disable the pause faxing menu item and the cancel all faxes menu item
            EnableMenuItem(hFaxMenu, IDM_FAX_PAUSE_FAXING, MF_BYCOMMAND | MF_GRAYED);
            EnableMenuItem(hFaxMenu, IDM_FAX_CANCEL_ALL_FAXES, MF_BYCOMMAND | MF_GRAYED);

#ifdef TOOLBAR_ENABLED
            // Disable the pause faxing toolbar menu item and the cancel all faxes toolbar menu item
            EnableToolbarMenuState(g_hWndToolbar, IDM_FAX_PAUSE_FAXING, FALSE);
            EnableToolbarMenuState(g_hWndToolbar, IDM_FAX_CANCEL_ALL_FAXES, FALSE);
#endif // TOOLBAR_ENABLED

#ifdef WIN95
            // Disable the sharing menu item
            EnableMenuItem(hFaxMenu, 5, MF_BYPOSITION | MF_GRAYED);
            // Disable the properties menu item
            EnableMenuItem(hFaxMenu, 7, MF_BYPOSITION | MF_GRAYED);
#endif // WIN95

            // Get the handle to the document menu
            hDocumentMenu = GetSubMenu(GetMenu(hWnd), 1);

            // Get the handle to the view menu
            hViewMenu = GetSubMenu(GetMenu(hWnd), 2);

#ifdef TOOLBAR_ENABLED
            // Set the toolbar
            if (WinPosInfo.bToolbarVisible) {
                // Show the toolbar
                hWndToolTips = CreateToolTips(hWnd);
                g_hWndToolbar = CreateToolbar(hWnd);
                // Get the rectangle of the status bar
                GetWindowRect(g_hWndToolbar, &rcToolbar);
            }
            else {
                ZeroMemory(&rcToolbar, sizeof(rcToolbar));
            }

            // Check the menu item
            CheckMenuItem(hViewMenu, IDM_VIEW_TOOLBAR, MF_BYCOMMAND | (WinPosInfo.bToolbarVisible ? MF_CHECKED : MF_UNCHECKED));
#else
            ZeroMemory(&rcToolbar, sizeof(rcToolbar));
#endif // TOOLBAR_ENABLED

            // Set the status bar
            if (WinPosInfo.bStatusBarVisible) {
                // Show the status bar
                hWndStatusBar = CreateStatusWindow(WS_CHILD | WS_CLIPSIBLINGS | WS_TABSTOP | WS_VISIBLE | SBARS_SIZEGRIP, NULL, hWnd, IDM_STATUS_BAR);
                // Get the rectangle of the status bar
                GetWindowRect(hWndStatusBar, &rcStatusBar);
            }
            else {
                ZeroMemory(&rcStatusBar, sizeof(rcStatusBar));
            }

            // Check the menu item
            CheckMenuItem(hViewMenu, IDM_VIEW_STATUS_BAR, MF_BYCOMMAND | (WinPosInfo.bStatusBarVisible ? MF_CHECKED : MF_UNCHECKED));

            // Get the rectangle of the client area
            GetClientRect(hWnd, &rcClient);
            // Create the list view control
            g_hWndListView = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_TABSTOP | WS_VISIBLE | LVS_REPORT, 0, (rcToolbar.bottom - rcToolbar.top), rcClient.right, rcClient.bottom - (rcStatusBar.bottom - rcStatusBar.top) - (rcToolbar.bottom - rcToolbar.top), hWnd, NULL, g_hInstance, NULL);
            if (g_hWndListView) {
                ListView_SetExtendedListViewStyle(g_hWndListView, LVS_EX_FULLROWSELECT);

                // Set common attributes for each column
                lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
                lvc.fmt = LVCFMT_LEFT;

                for (nColumnIndex = 0; nColumnIndex < (INT) eIllegalColumnIndex; nColumnIndex++) {
                    // Get the column header text
                    GetColumnHeaderText((eListViewColumnIndex) nColumnIndex, szColumnHeader);

                    // Set the column header text
                    lvc.pszText = szColumnHeader;
                    // Set the column width
                    lvc.cx = WinPosInfo.ColumnWidth[nColumnIndex];
                    // Set the column number
                    lvc.iSubItem = nColumnIndex;
                    // Insert column into list view
                    ListView_InsertColumn(g_hWndListView, lvc.iSubItem, &lvc);
                }

                SetFocus(g_hWndListView);
            }

            break;

        case WM_MENUSELECT:
            UINT  uBase;

            switch (LOWORD(wParam)) {
                case IDM_FAX_SET_AS_DEFAULT_PRINTER_1:
                case IDM_FAX_SET_AS_DEFAULT_PRINTER_2:
                case IDM_FAX_SET_AS_DEFAULT_PRINTER_3:
                case IDM_FAX_SET_AS_DEFAULT_PRINTER_4:
                    wParam = MAKELONG(LOWORD(IDM_FAX_SET_AS_DEFAULT_PRINTER), HIWORD(wParam));
                    break;

                case IDM_FAX_SHARING_1:
                case IDM_FAX_SHARING_2:
                case IDM_FAX_SHARING_3:
                case IDM_FAX_SHARING_4:
                    wParam = MAKELONG(LOWORD(IDM_FAX_SHARING), HIWORD(wParam));
                    break;

                case IDM_FAX_PROPERTIES_1:
                case IDM_FAX_PROPERTIES_2:
                case IDM_FAX_PROPERTIES_3:
                case IDM_FAX_PROPERTIES_4:
                    wParam = MAKELONG(LOWORD(IDM_FAX_PROPERTIES), HIWORD(wParam));
                    break;

                case IDM_FAX_SET_AS_DEFAULT_PRINTER_MORE:
                case IDM_FAX_SHARING_MORE:
                case IDM_FAX_PROPERTIES_MORE:
                    wParam = MAKELONG(LOWORD(IDM_FAX_SET_AS_DEFAULT_PRINTER_MORE), HIWORD(wParam));
                    break;
            }

            if (hWndStatusBar) {
                uBase = IDS_MENU_BASE;
                MenuHelp(iMsg, wParam, lParam, NULL, g_hInstance, hWndStatusBar, &uBase);
            }

            break;

        case WM_INITMENUPOPUP:
            if (((HMENU) wParam == hFaxMenu) && (LOWORD(lParam) == 0)) {
                // mii is the menu item info
                MENUITEMINFO      mii;
                // szMenuString is a menu string
                TCHAR             szMenuString[RESOURCE_STRING_LEN];
                // dwNumOldMenuItems is the old number of menu items
                DWORD             dwNumOldMenuItems;
                // dwNumNewMenuItems is the new number of menu items
                DWORD             dwNumNewMenuItems;

                // pFaxPrintersConfig is the pointer to the fax printers
                LPPRINTER_INFO_2  pFaxPrintersConfig;
                // dwNumFaxPrinters is the number of fax printers
                DWORD             dwNumFaxPrinters;
                // dwIndex is a counter to enumerate each menu and printer
                DWORD             dwIndex;
                // dwDefaultIndex is the index of the default fax printer
                DWORD             dwDefaultIndex;

                // szDefaultPrinterName is the default printer name
                LPTSTR            szDefaultPrinterName;

                if ((WaitForSingleObject(g_hStartEvent, 0) != WAIT_TIMEOUT) && (ListView_GetItemCount(g_hWndListView))) {
                    // Enable the cancel all faxes menu item
                    EnableMenuItem(hFaxMenu, IDM_FAX_CANCEL_ALL_FAXES, MF_BYCOMMAND | MF_ENABLED);
                }
                else {
                    // Disable the cancel all faxes menu item
                    EnableMenuItem(hFaxMenu, IDM_FAX_CANCEL_ALL_FAXES, MF_BYCOMMAND | MF_GRAYED);
                }

                // Get the default printer
                szDefaultPrinterName = GetDefaultPrinterName();

                // Get the fax printers
                pFaxPrintersConfig = (LPPRINTER_INFO_2) GetFaxPrinters(&dwNumFaxPrinters);
                if ((pFaxPrintersConfig) && (dwNumFaxPrinters > 1)) {
                    // Many fax printers, so set menu items as sub-menus
                    // Initialize the menu item info
                    mii.cbSize = sizeof(mii);
                    mii.fMask = MIIM_STATE | MIIM_SUBMENU;
                    mii.fState = MFS_ENABLED;

                    // Update the set as default printer menu item
                    if (!hFaxSetAsDefaultMenu) {
                        hFaxSetAsDefaultMenu = CreatePopupMenu();

                        mii.hSubMenu = hFaxSetAsDefaultMenu;
                        SetMenuItemInfo(hFaxMenu, IDM_FAX_SET_AS_DEFAULT_PRINTER, FALSE, &mii);
                    }

                    // Update the sharing menu item
                    if (!hFaxSharingMenu) {
                        hFaxSharingMenu = CreatePopupMenu();

                        mii.hSubMenu = hFaxSharingMenu;
                        SetMenuItemInfo(hFaxMenu, IDM_FAX_SHARING, FALSE, &mii);
                    }

                    // Update the properties menu item
                    if (!hFaxPropertiesMenu) {
                        hFaxPropertiesMenu = CreatePopupMenu();

                        mii.hSubMenu = hFaxPropertiesMenu;
                        SetMenuItemInfo(hFaxMenu, IDM_FAX_PROPERTIES, FALSE, &mii);
                    }

                    // Initialize the menu item info
                    mii.cbSize = sizeof(mii);
                    mii.fMask = MIIM_ID | MIIM_STATE | MIIM_TYPE;
                    mii.fType = MFT_STRING;

                    // Get the number of menu items
                    dwNumOldMenuItems = (DWORD) GetMenuItemCount(hFaxSetAsDefaultMenu);

                    // Insert the default fax printer first into the menus
                    for (dwDefaultIndex = dwNumFaxPrinters, dwNumNewMenuItems = 0; (szDefaultPrinterName) && (dwDefaultIndex > 0); dwDefaultIndex--) {
                        if (!lstrcmpi(szDefaultPrinterName, pFaxPrintersConfig[dwDefaultIndex - 1].pPrinterName)) {
                            // Set the menu string
                            wsprintf(szMenuString, TEXT("&%d %s"), dwNumNewMenuItems + 1, pFaxPrintersConfig[dwDefaultIndex - 1].pPrinterName);
                            mii.fState = MFS_CHECKED | MFS_ENABLED;
                            mii.dwTypeData = szMenuString;

                            // Insert the item into the set as default printer menu
                            mii.wID = IDM_FAX_SET_AS_DEFAULT_PRINTER_1;
                            InsertMenuItem(hFaxSetAsDefaultMenu, dwNumOldMenuItems, TRUE, &mii);

                            // Insert the item into the sharing menu
                            mii.wID = IDM_FAX_SHARING_1;
                            InsertMenuItem(hFaxSharingMenu, dwNumOldMenuItems, TRUE, &mii);

                            // Insert the item into the properties menu
                            mii.wID = IDM_FAX_PROPERTIES_1;
                            InsertMenuItem(hFaxPropertiesMenu, dwNumOldMenuItems, TRUE, &mii);

                            // Increment the number of new menu items
                            dwNumNewMenuItems++;

                            break;
                        }
                    }

                    // Propagate the menus with the list of fax printers
                    mii.fState = MFS_ENABLED;
                    for (dwIndex = 0; (dwIndex < dwNumFaxPrinters) && (dwNumNewMenuItems < (IDM_FAX_SET_AS_DEFAULT_PRINTER_MORE - IDM_FAX_SET_AS_DEFAULT_PRINTER_1)); dwIndex++) {
                        if (dwIndex != (dwDefaultIndex - 1)) {
                            // Set the menu string
                            wsprintf(szMenuString, TEXT("&%d %s"), dwNumNewMenuItems + 1, pFaxPrintersConfig[dwIndex].pPrinterName);
                            mii.dwTypeData = szMenuString;

                            // Insert the item into the set as default printer menu
                            mii.wID = (IDM_FAX_SET_AS_DEFAULT_PRINTER_1 + dwNumNewMenuItems);
                            InsertMenuItem(hFaxSetAsDefaultMenu, (dwNumOldMenuItems + dwNumNewMenuItems), TRUE, &mii);

                            // Insert the item into the sharing menu
                            mii.wID = (IDM_FAX_SHARING_1 + dwNumNewMenuItems);
                            InsertMenuItem(hFaxSharingMenu, (dwNumOldMenuItems + dwNumNewMenuItems), TRUE, &mii);

                            // Insert the item into the properties menu
                            mii.wID = (IDM_FAX_PROPERTIES_1 + dwNumNewMenuItems);
                            InsertMenuItem(hFaxPropertiesMenu, (dwNumOldMenuItems + dwNumNewMenuItems), TRUE, &mii);

                            // Increment the number of new menu items
                            dwNumNewMenuItems++;
                        }
                    }

                    if (dwNumFaxPrinters > (IDM_FAX_SET_AS_DEFAULT_PRINTER_MORE - IDM_FAX_SET_AS_DEFAULT_PRINTER_1)) {
                        // Finish the menus with the printers string
                        LoadString(g_hInstance, IDS_MENU_ITEM_FAX_PRINTERS, szMenuString, RESOURCE_STRING_LEN);
                        mii.dwTypeData = szMenuString;

                        // Insert the item into the set as default printer menu
                        mii.wID = IDM_FAX_SET_AS_DEFAULT_PRINTER_MORE;
                        InsertMenuItem(hFaxSetAsDefaultMenu, (dwNumOldMenuItems + dwNumNewMenuItems), TRUE, &mii);

                        // Insert the item into the sharing menu
                        mii.wID = IDM_FAX_SHARING_MORE;
                        InsertMenuItem(hFaxSharingMenu, (dwNumOldMenuItems + dwNumNewMenuItems), TRUE, &mii);

                        // Insert the item into the properties menu
                        mii.wID = IDM_FAX_PROPERTIES_MORE;
                        InsertMenuItem(hFaxPropertiesMenu, (dwNumOldMenuItems + dwNumNewMenuItems), TRUE, &mii);
                    }

                    // Remove the old items from the set as default printer menu
                    for (dwIndex = 0; dwIndex < dwNumOldMenuItems; dwIndex++) {
                        DeleteMenu(hFaxSetAsDefaultMenu, 0, MF_BYPOSITION);
                    }

                    // Remove the old items from the sharing menu
                    for (dwIndex = 0; dwIndex < dwNumOldMenuItems; dwIndex++) {
                        DeleteMenu(hFaxSharingMenu, 0, MF_BYPOSITION);
                    }

                    // Remove the old items from the properties menu
                    for (dwIndex = 0; dwIndex < dwNumOldMenuItems; dwIndex++) {
                        DeleteMenu(hFaxPropertiesMenu, 0, MF_BYPOSITION);
                    }
                }
                else {
                    // One or zero fax printers, so set sub-menus as menu items
                    // Initialize the menu item info
                    mii.cbSize = sizeof(mii);
                    mii.fMask = MIIM_STATE | MIIM_SUBMENU;
                    mii.hSubMenu = NULL;

                    // Update the set as default printer menu item
                    if (hFaxSetAsDefaultMenu) {
                        DestroyMenu(hFaxSetAsDefaultMenu);
                        hFaxSetAsDefaultMenu = NULL;
                    }
                    if ((pFaxPrintersConfig) && (dwNumFaxPrinters == 1) && (szDefaultPrinterName) && (!lstrcmpi(szDefaultPrinterName, pFaxPrintersConfig[0].pPrinterName))) {
                        mii.fState = MFS_CHECKED | MFS_ENABLED;
                    }
                    else {
                        mii.fState = (dwNumFaxPrinters == 1) ? MFS_ENABLED : MFS_GRAYED;
                    }
                    SetMenuItemInfo(hFaxMenu, IDM_FAX_SET_AS_DEFAULT_PRINTER, FALSE, &mii);

                    // Update the sharing menu item
                    if (hFaxSharingMenu) {
                        DestroyMenu(hFaxSharingMenu);
                        hFaxSharingMenu = NULL;
                    }
                    mii.fState = (dwNumFaxPrinters == 1) ? MFS_ENABLED : MFS_GRAYED;
                    SetMenuItemInfo(hFaxMenu, IDM_FAX_SHARING, FALSE, &mii);

                    // Update the properties menu item
                    if (hFaxPropertiesMenu) {
                        DestroyMenu(hFaxPropertiesMenu);
                        hFaxPropertiesMenu = NULL;
                    }
                    mii.fState = (dwNumFaxPrinters == 1) ? MFS_ENABLED : MFS_GRAYED;
                    SetMenuItemInfo(hFaxMenu, IDM_FAX_PROPERTIES, FALSE, &mii);
                }

                if (pFaxPrintersConfig) {
                    MemFree(pFaxPrintersConfig);
                }

                if (szDefaultPrinterName) {
                    MemFree(szDefaultPrinterName);
                }
            }
            else if (((HMENU) wParam == hDocumentMenu) && (LOWORD(lParam) == 1)) {
                if ((WaitForSingleObject(g_hStartEvent, 0) == WAIT_TIMEOUT) || (!ListView_GetSelectedCount(g_hWndListView))) {
                    // Disconnected or no items selected, so disable all menu items
                    // Disable the pause menu item
                    EnableMenuItem(hDocumentMenu, IDM_DOCUMENT_PAUSE, MF_BYCOMMAND | MF_GRAYED);
                    // Disable the resume menu item
                    EnableMenuItem(hDocumentMenu, IDM_DOCUMENT_RESUME, MF_BYCOMMAND | MF_GRAYED);
                    // Disable the restart menu item
                    EnableMenuItem(hDocumentMenu, IDM_DOCUMENT_RESTART, MF_BYCOMMAND | MF_GRAYED);
                    // Disable the cancel menu item
                    EnableMenuItem(hDocumentMenu, IDM_DOCUMENT_CANCEL, MF_BYCOMMAND | MF_GRAYED);
                    // Disable the properties menu item
                    EnableMenuItem(hDocumentMenu, IDM_DOCUMENT_PROPERTIES, MF_BYCOMMAND | MF_GRAYED);
                }
                else {
                    // uAndMask is the mask indicating the item attributes ANDed together
                    UINT   uAndMask;
                    // uOrMask is the mask indicating the item attributes ORed together
                    UINT   uOrMask;
                    // uState is the item's state
                    UINT   uState;
                    // dwListIndex is the index of a particular item in the list view
                    DWORD  dwListIndex;
                    // bUserHasAccess indicates the user has job manage access
                    BOOL   bUserHasAccess;

                    // Initialize uAndMask
                    uAndMask = LVIS_OVERLAYMASK;
                    // Initialize uOrMask
                    uOrMask = 0;

                    // Enumerate each selected item in the list view
                    dwListIndex = ListView_GetNextItem(g_hWndListView, -1, LVNI_ALL | LVNI_SELECTED);
                    while (dwListIndex != -1) {
                        // Get the item's attributes
                        uState = ListView_GetItemState(g_hWndListView, dwListIndex, LVIS_OVERLAYMASK);
                        // AND the item's attributes with uAndMask
                        uAndMask &= uState;
                        // OR the item's attributes with uOrMask
                        uOrMask |= uState;
                        dwListIndex = ListView_GetNextItem(g_hWndListView, dwListIndex, LVNI_ALL | LVNI_SELECTED);
                    }

                    if (uAndMask & ITEM_USEROWNSJOB_MASK) {
                        // User owns all of the jobs
                        bUserHasAccess = TRUE;
                    }
                    else {
                        // User does not own all of the jobs, so determine if user has job manage permission
                        // Initialize bUserHasAccess to FALSE
                        bUserHasAccess = FALSE;

                        // Connect to the fax service
                        if (Connect()) {
                            // Get the user's job manage access
                            bUserHasAccess = FaxAccessCheck(g_hFaxSvcHandle, FAX_JOB_MANAGE);
                            // Disconnect from the fax service
                            Disconnect();
                        }
                    }

                    if (((uAndMask & ITEM_SEND_MASK) == 0) || ((uAndMask & ITEM_IDLE_MASK) == 0)) {
                        // Not a send job or a send job in progress, so disable the pause, resume and restart menu items
                        // Disable the pause menu item
                        EnableMenuItem(hDocumentMenu, IDM_DOCUMENT_PAUSE, MF_BYCOMMAND | MF_GRAYED);
                        // Disable the resume menu item
                        EnableMenuItem(hDocumentMenu, IDM_DOCUMENT_RESUME, MF_BYCOMMAND | MF_GRAYED);
                        // Disable the restart menu item
                        EnableMenuItem(hDocumentMenu, IDM_DOCUMENT_RESTART, MF_BYCOMMAND | MF_GRAYED);
                    }
                    else if (uAndMask & ITEM_PAUSED_MASK) {
                        // Idle send job that is paused, so enable the resume and restart menu items and disable the pause menu item
                        // Disable the pause menu item
                        EnableMenuItem(hDocumentMenu, IDM_DOCUMENT_PAUSE, MF_BYCOMMAND | MF_GRAYED);
                        // Enable the resume menu item
                        EnableMenuItem(hDocumentMenu, IDM_DOCUMENT_RESUME, MF_BYCOMMAND | (bUserHasAccess ? MF_ENABLED : MF_GRAYED));
                        // Enable the restart menu item
                        EnableMenuItem(hDocumentMenu, IDM_DOCUMENT_RESTART, MF_BYCOMMAND | (bUserHasAccess ? MF_ENABLED : MF_GRAYED));
                    }
                    else if ((uOrMask & ITEM_PAUSED_MASK) == 0) {
                        // Idle send job that is not paused, so enable the pause and restart menu items and disable the resume menu item
                        // Enable the pause menu item
                        EnableMenuItem(hDocumentMenu, IDM_DOCUMENT_PAUSE, MF_BYCOMMAND | (bUserHasAccess ? MF_ENABLED : MF_GRAYED));
                        // Disable the resume menu item
                        EnableMenuItem(hDocumentMenu, IDM_DOCUMENT_RESUME, MF_BYCOMMAND | MF_GRAYED);
                        // Enable the restart menu item
                        EnableMenuItem(hDocumentMenu, IDM_DOCUMENT_RESTART, MF_BYCOMMAND | (bUserHasAccess ? MF_ENABLED : MF_GRAYED));
                    }
                    else {
                        // Idle send job, so enable the restart menu item and disable the pause and resume menu items
                        // Enable the pause menu item
                        EnableMenuItem(hDocumentMenu, IDM_DOCUMENT_PAUSE, MF_BYCOMMAND | MF_GRAYED);
                        // Disable the resume menu item
                        EnableMenuItem(hDocumentMenu, IDM_DOCUMENT_RESUME, MF_BYCOMMAND | MF_GRAYED);
                        // Enable the restart menu item
                        EnableMenuItem(hDocumentMenu, IDM_DOCUMENT_RESTART, MF_BYCOMMAND | (bUserHasAccess ? MF_ENABLED : MF_GRAYED));
                    }

                    // Enable the cancel menu item
                    EnableMenuItem(hDocumentMenu, IDM_DOCUMENT_CANCEL, MF_BYCOMMAND | (bUserHasAccess ? MF_ENABLED : MF_GRAYED));

                    if (ListView_GetSelectedCount(g_hWndListView) == 1) {
                        // Only one item selected, so enable the properties menu item
                        EnableMenuItem(hDocumentMenu, IDM_DOCUMENT_PROPERTIES, MF_BYCOMMAND | MF_ENABLED);
                    }
                    else {
                        // Multiple items selected, so disable the properties menu item
                        EnableMenuItem(hDocumentMenu, IDM_DOCUMENT_PROPERTIES, MF_BYCOMMAND | MF_GRAYED);
                    }
                }
            }

            break;

        case WM_NOTIFY:
            LPNMHDR  pnmhdr;
            // dwMessagePos is the cursor position for the message
            DWORD    dwMessagePos;

            pnmhdr = (LPNMHDR) lParam;
            if ((pnmhdr->hwndFrom == g_hWndListView) && (pnmhdr->code == NM_RCLICK)) {
                // User has right-clicked in the list view, so display the document context menu
                // Initialize the document menu
                SendMessage(g_hWndMain, WM_INITMENUPOPUP, (WPARAM) hDocumentMenu, MAKELPARAM(1, FALSE));
                // Get the cursor position
                dwMessagePos = GetMessagePos();
                // Display the document context menu
                TrackPopupMenu(hDocumentMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON, LOWORD(dwMessagePos), HIWORD(dwMessagePos), 0, g_hWndMain, NULL);
            }
            else if ((pnmhdr->hwndFrom == g_hWndListView) && (pnmhdr->code == NM_DBLCLK)) {
                // rcItem is the rectangle of the item
                RECT  rcItem;

                // User has double-clicked in the list view, so display the job properties
                if (ListView_GetSelectedCount(g_hWndListView) != 1) {
                    break;
                }

                // Get the item's bounding rectangle
                if (ListView_GetItemRect(g_hWndListView, ListView_GetNextItem(g_hWndListView, -1, LVNI_ALL | LVNI_SELECTED), &rcItem, LVIR_BOUNDS)) {
                    // Get the cursor position
                    dwMessagePos = GetMessagePos();
                    // Get the window rectangle of the list view
                    GetWindowRect(g_hWndListView, &rcClient);
                    // Adjust dwMessagePos to indicate the cursor position within the list view
                    dwMessagePos = MAKELONG(LOWORD(dwMessagePos) - rcClient.left, HIWORD(dwMessagePos) - rcClient.top);

                    if ((LOWORD(dwMessagePos) >= rcItem.left) && (LOWORD(dwMessagePos) <= rcItem.right) && (HIWORD(dwMessagePos) >= rcItem.top) && (HIWORD(dwMessagePos) <= rcItem.bottom)) {
                        // Display the job properties
                        SendMessage(g_hWndMain, WM_COMMAND, MAKEWPARAM(IDM_DOCUMENT_PROPERTIES, 0), 0);
                    }
                }
            }
#ifdef TOOLBAR_ENABLED
            else if (pnmhdr->code == TTN_NEEDTEXT) {
                // pToolTipText is the pointer to the tool tip text structure
                LPTOOLTIPTEXT  pToolTipText;
                // szToolTip is the tool tip text
                TCHAR          szToolTip[RESOURCE_STRING_LEN];

                pToolTipText = (LPTOOLTIPTEXT) lParam;
                switch (pToolTipText->hdr.idFrom) {
                    case IDM_FAX_PAUSE_FAXING:
                        LoadString(g_hInstance, IDS_MENU_FAX_PAUSE_FAXING, szToolTip, RESOURCE_STRING_LEN);
                        break;

                    case IDM_FAX_CANCEL_ALL_FAXES:
                        LoadString(g_hInstance, IDS_MENU_FAX_CANCEL_ALL_FAXES, szToolTip, RESOURCE_STRING_LEN);
                        break;

                    case IDM_DOCUMENT_PAUSE:
                        LoadString(g_hInstance, IDS_MENU_DOCUMENT_PAUSE, szToolTip, RESOURCE_STRING_LEN);
                        break;

                    case IDM_DOCUMENT_RESUME:
                        LoadString(g_hInstance, IDS_MENU_DOCUMENT_PAUSE, szToolTip, RESOURCE_STRING_LEN);
                        break;

                    case IDM_DOCUMENT_RESTART:
                        LoadString(g_hInstance, IDS_MENU_DOCUMENT_RESTART, szToolTip, RESOURCE_STRING_LEN);
                        break;

                    case IDM_DOCUMENT_CANCEL:
                        LoadString(g_hInstance, IDS_MENU_DOCUMENT_CANCEL, szToolTip, RESOURCE_STRING_LEN);
                        break;

                    case IDM_DOCUMENT_PROPERTIES:
                        LoadString(g_hInstance, IDS_MENU_DOCUMENT_PROPERTIES, szToolTip, RESOURCE_STRING_LEN);
                        break;

                    case IDM_VIEW_REFRESH:
                        LoadString(g_hInstance, IDS_MENU_VIEW_REFRESH, szToolTip, RESOURCE_STRING_LEN);
                        break;

                    case IDM_HELP_TOPICS:
                        LoadString(g_hInstance, IDS_MENU_HELP_TOPICS, szToolTip, RESOURCE_STRING_LEN);
                        break;

                    default:
                        ZeroMemory(szToolTip, sizeof(szToolTip));
                        break;

                }

                pToolTipText->lpszText = szToolTip;
            }
#endif // TOOLBAR_ENABLED
            break;

        case WM_SETFOCUS:
            SetFocus(g_hWndListView);
            break;

        case WM_SIZE:
#ifdef TOOLBAR_ENABLED
            // Resize the toolbar
            if (WinPosInfo.bToolbarVisible) {
                SendMessage(g_hWndToolbar, iMsg, wParam, lParam);
            }
#endif // TOOLBAR_ENABLED

            // Resize the status bar
            if (WinPosInfo.bStatusBarVisible) {
                SendMessage(hWndStatusBar, iMsg, wParam, lParam);
            }

            // Get the rectangle of the client area
            GetClientRect(hWnd, &rcClient);
            // Resize the list view
            MoveWindow(g_hWndListView, 0, (rcToolbar.bottom - rcToolbar.top), rcClient.right, rcClient.bottom - (rcStatusBar.bottom - rcStatusBar.top) - (rcToolbar.bottom - rcToolbar.top), TRUE);
            break;

        case UM_SELECT_FAX_PRINTER:
            // szCommandLine is the command line
            TCHAR                szCommandLine[MAX_PATH];
            // si is the startup info for the print ui
            STARTUPINFO          si;
            // pi is the process info for the print ui
            PROCESS_INFORMATION  pi;
            // hWndPrintUI is the handle to the print ui window
            HWND                 hWndPrintUI;
            // pProcessInfoItem is a pointer to a PROCESS_INFO_ITEM structure
            PPROCESS_INFO_ITEM   pProcessInfoItem;
            // szErrorCaption is the error caption if CreateProcess() fails
            TCHAR                szErrorCaption[RESOURCE_STRING_LEN];
            // szErrorMessage is the error message if CreateProcess() fails
            LPTSTR               szErrorMessage;

            if (uCurrentMenu == IDM_FAX_SET_AS_DEFAULT_PRINTER_MORE) {
                // Set the default printer
                SetDefaultPrinterName((LPTSTR) wParam);

                MemFree((LPBYTE) wParam);
                uCurrentMenu = 0;
                return 0;
            }

            if (pProcessInfoList) {
                // See if print ui is already open
                pProcessInfoItem = (PPROCESS_INFO_ITEM) pProcessInfoList;

                while (TRUE) {
                    if (!lstrcmpi((LPTSTR) wParam, pProcessInfoItem->szPrinterName)) {
                        // Printer name matches, so print ui may still be open
                        if (WaitForSingleObject(pProcessInfoItem->hProcess, 0) != WAIT_OBJECT_0) {
                            // Print ui is still open
                            ShowWindow(pProcessInfoItem->hWnd, SW_SHOWNORMAL);
                            SetForegroundWindow(pProcessInfoItem->hWnd);
                            return 0;
                        }

                        if (pProcessInfoItem == (PPROCESS_INFO_ITEM) pProcessInfoList) {
                            pProcessInfoList = pProcessInfoItem->ListEntry.Blink;
                        }

                        if (IsListEmpty(pProcessInfoList)) {
                            // This is the last item in the list, so set the list head to NULL
                            pProcessInfoList = NULL;
                        }
                        else {
                            // Remove the process info item from the list
                            RemoveEntryList(&pProcessInfoItem->ListEntry);
                        }
                        // Free the process info item
                        MemFree(pProcessInfoItem);
                        break;
                    }

                    // Step to the next process info item
                    pProcessInfoItem = (PPROCESS_INFO_ITEM) pProcessInfoItem->ListEntry.Blink;

                    if (pProcessInfoItem == (PPROCESS_INFO_ITEM) pProcessInfoList) {
                        // The list has been traversed
                        break;
                    }
                }
            }

            switch (uCurrentMenu) {
                case IDM_FAX_SHARING_MORE:
                    // Set the parameters
                    wsprintf(szCommandLine, TEXT("rundll32 printui.dll,PrintUIEntry /p /t1 /n \"%s\""), (LPTSTR) wParam);
                    break;

                case IDM_FAX_PROPERTIES_MORE:
                    // Set the parameters
                    wsprintf(szCommandLine, TEXT("rundll32 printui.dll,PrintUIEntry /p /t0 /n \"%s\""), (LPTSTR) wParam);
                    break;
            }

            // Initialize si
            ZeroMemory(&si, sizeof(si));
            GetStartupInfo(&si);
            si.dwFlags = STARTF_USESHOWWINDOW;
            si.wShowWindow = SW_NORMAL;

            // Initialize pi
            ZeroMemory(&pi, sizeof(pi));

            // Launch the print ui
            hWndPrintUI = NULL;
            SetCursor(LoadCursor(NULL, IDC_WAIT));
            if (CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi)) {
                // Find the print ui window
                do {
                    Sleep(250);
                    EnumThreadWindows(pi.dwThreadId, EnumThreadWndProc, (LPARAM) &hWndPrintUI);
                } while (hWndPrintUI == NULL);

                SetCursor(LoadCursor(NULL, IDC_ARROW));

                // Add the process info item to the list
                pProcessInfoItem = (PPROCESS_INFO_ITEM) MemAlloc(sizeof(PROCESS_INFO_ITEM) + (lstrlen((LPTSTR) wParam) + 1) * sizeof(TCHAR));
                if (pProcessInfoItem) {
                    // Set szPrinterName
                    pProcessInfoItem->szPrinterName = (LPTSTR) ((UINT_PTR) pProcessInfoItem + sizeof(PROCESS_INFO_ITEM));
                    // Copy szPrinterName
                    lstrcpy(pProcessInfoItem->szPrinterName, (LPTSTR) wParam);

                    // Copy hProcess
                    pProcessInfoItem->hProcess = pi.hProcess;

                    // Copy hWndPrintUI
                    pProcessInfoItem->hWnd = hWndPrintUI;

                    // Insert the process info item into the list
                    if (pProcessInfoList) {
                        InsertTailList(pProcessInfoList, &pProcessInfoItem->ListEntry);
                    }
                    else {
                        pProcessInfoList = &pProcessInfoItem->ListEntry;
                        InitializeListHead(pProcessInfoList);
                    }
                }
            }
            else {
                // CreateProcess() failed, so display an error message
                SetCursor(LoadCursor(NULL, IDC_ARROW));

                // Load the error caption
                LoadString(g_hInstance, IDS_ERROR_CAPTION, szErrorCaption, RESOURCE_STRING_LEN);

                // Try to get the error message from the system message table
                if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, (LPTSTR) &szErrorMessage, 0, NULL)) {
                    // Display the error message
                    MessageBox(hWnd, szErrorMessage, szErrorCaption, MB_OK | MB_ICONERROR | MB_APPLMODAL);
                    LocalFree(szErrorMessage);
                }
                else {
                    // Allocate the memory for the error message
                    szErrorMessage = (LPTSTR) MemAlloc((RESOURCE_STRING_LEN) * sizeof(TCHAR));
                    if (szErrorMessage) {
                        // Load the error message
                        LoadString(g_hInstance, IDS_ERROR_PRINTER_PROPERTIES, szErrorMessage, RESOURCE_STRING_LEN);
                        // Display the error message
                        MessageBox(hWnd, szErrorMessage, szErrorCaption, MB_OK | MB_ICONERROR | MB_APPLMODAL);
                        MemFree(szErrorMessage);
                    }
                }
            }

            MemFree((LPBYTE) wParam);
            uCurrentMenu = 0;

            break;

        case WM_COMMAND:
            // mii is the menu item info
            MENUITEMINFO    mii;
            // szPrinterName is the printer name
            LPTSTR          szPrinterName;
            // pFaxJobEntry is the pointer to the fax jobs
            PFAX_JOB_ENTRY  pFaxJobEntry;
            // lvi specifies the attributes of a particular item in the list view
            LV_ITEM         lvi;
            // dwListIndex is the index of a particular item in the list view
            DWORD           dwListIndex;

            switch (LOWORD(wParam)) {
                case IDM_FAX_PAUSE_FAXING:
                    // pFaxConfig is the pointer to the fax configuration
                    PFAX_CONFIGURATION  pFaxConfig;

                    // Initialize the menu item info
                    mii.cbSize = sizeof(mii);
                    mii.fMask = MIIM_STATE;
                    mii.fState = 0;

                    // Get the size of the menu item
                    if (!GetMenuItemInfo(hFaxMenu, IDM_FAX_PAUSE_FAXING, FALSE, &mii)) {
                        break;
                    }

                    SetCursor(LoadCursor(NULL, IDC_WAIT));
                    if (Connect()) {
                        // Get the fax configuration
                        if (FaxGetConfiguration(g_hFaxSvcHandle, &pFaxConfig)) {
                            // Toggle the pause faxing status
                            pFaxConfig->PauseServerQueue = mii.fState & MFS_CHECKED ? FALSE : TRUE;
                            // Set the fax configuration
                            if (FaxSetConfiguration(g_hFaxSvcHandle, pFaxConfig)) {
                                // Check the menu item
                                CheckMenuItem(hFaxMenu, IDM_FAX_PAUSE_FAXING, MF_BYCOMMAND | (pFaxConfig->PauseServerQueue ? MF_CHECKED : MF_UNCHECKED));
#ifdef TOOLBAR_ENABLED
                                // Enable the pause faxing toolbar menu item
                                EnableToolbarMenuState(g_hWndToolbar, IDM_FAX_PAUSE_FAXING, pFaxConfig->PauseServerQueue);
#endif // TOOLBAR_ENABLED
                            }

                            FaxFreeBuffer(pFaxConfig);
                        }

                        Disconnect();
                    }

                    // Set the window title to indicate connected or paused
                    SetWindowText(hWnd, mii.fState & MFS_CHECKED ? g_szTitleConnected : g_szTitlePaused);

                    SetCursor(LoadCursor(NULL, IDC_ARROW));

                    break;

                case IDM_FAX_CANCEL_ALL_FAXES:
                    // dwNumFaxJobs is the number of fax jobs
                    DWORD           dwNumFaxJobs;
                    // dwIndex is a counter to enumerate each fax job
                    DWORD           dwIndex;

                    SetCursor(LoadCursor(NULL, IDC_WAIT));
                    if (Connect()) {
                        // Enumerate the fax jobs
                        if (FaxEnumJobs(g_hFaxSvcHandle, &pFaxJobEntry, &dwNumFaxJobs)) {
                            // Enumerate and cancel each fax job
                            for (dwIndex = 0; dwIndex < dwNumFaxJobs; dwIndex++) {
                                FaxSetJob(g_hFaxSvcHandle, pFaxJobEntry[dwIndex].JobId, JC_DELETE, &pFaxJobEntry[dwIndex]);
                            }

                            FaxFreeBuffer(pFaxJobEntry);
                        }

                        Disconnect();
                    }

                    SetCursor(LoadCursor(NULL, IDC_ARROW));

                    break;

                case IDM_FAX_CLOSE:
                    SendMessage(hWnd, WM_CLOSE, 0, 0);
                    break;

                case IDM_DOCUMENT_PAUSE:
                case IDM_DOCUMENT_RESUME:
                case IDM_DOCUMENT_RESTART:
                case IDM_DOCUMENT_CANCEL:
                    // pJobIdList is a pointer to the job id list
                    PLIST_ENTRY    pJobIdList;
                    // JobIdItem is a JOB_ID_ITEM structure
                    PJOB_ID_ITEM   pJobIdItem;
                    // FaxJobEntry is the fax job
                    FAX_JOB_ENTRY  FaxJobEntry;
                    // dwCommand is the command to set the fax job entry
                    DWORD          dwCommand;

                    switch (LOWORD(wParam)) {
                        case IDM_DOCUMENT_PAUSE:
                            dwCommand = JC_PAUSE;
                            break;

                        case IDM_DOCUMENT_RESUME:
                            dwCommand = JC_RESUME;
                            break;

                        case IDM_DOCUMENT_RESTART:
                            dwCommand = JC_RESTART;
                            break;

                        case IDM_DOCUMENT_CANCEL:
                            dwCommand = JC_DELETE;
                            break;
                    }

                    // Set pJobIdList to NULL
                    pJobIdList = NULL;

                    SetCursor(LoadCursor(NULL, IDC_WAIT));
                    if (Connect()) {
                        // Initialize FaxJobEntry
                        ZeroMemory(&FaxJobEntry, sizeof(FaxJobEntry));
                        FaxJobEntry.SizeOfStruct = sizeof(FaxJobEntry);

                        // Enumerate each selected item in the list view
                        dwListIndex = ListView_GetNextItem(g_hWndListView, -1, LVNI_ALL | LVNI_SELECTED);
                        while (dwListIndex != -1) {
                            // Initialize lvi
                            lvi.mask = LVIF_PARAM;
                            // Set the item number
                            lvi.iItem = dwListIndex;
                            // Set the subitem number
                            lvi.iSubItem = 0;
                            // Set the lParam
                            lvi.lParam = 0;
                            // Get the selected item from the list view
                            if (ListView_GetItem(g_hWndListView, &lvi)) {
                                // Add the job id item to the list
                                pJobIdItem = (PJOB_ID_ITEM) MemAlloc(sizeof(JOB_ID_ITEM));
                                if (pJobIdItem) {
                                    // Copy dwJobId
                                    pJobIdItem->dwJobId = (DWORD) lvi.lParam;
                                    // Insert the job id item into the list
                                    if (pJobIdList) {
                                        InsertTailList(pJobIdList, &pJobIdItem->ListEntry);
                                    }
                                    else {
                                        pJobIdList = &pJobIdItem->ListEntry;
                                        InitializeListHead(pJobIdList);
                                    }
                                }
                            }

                            dwListIndex = ListView_GetNextItem(g_hWndListView, dwListIndex, LVNI_ALL | LVNI_SELECTED);
                        }

                        while (pJobIdList) {
                            // Get the job id item from the list
                            pJobIdItem = (PJOB_ID_ITEM) pJobIdList;
                            // Set FaxJobEntry
                            FaxJobEntry.JobId = pJobIdItem->dwJobId;
                            // Set the fax job entry
                            if (FaxSetJob(g_hFaxSvcHandle, FaxJobEntry.JobId, dwCommand, &FaxJobEntry)) {
                                if (LOWORD(wParam) != IDM_DOCUMENT_CANCEL) {
                                    // Post an event to the completion port to indicate thread is to refresh
                                    PostEventToCompletionPort(g_hCompletionPort, (DWORD) -1, FaxJobEntry.JobId);
                                }
                            }

                            // Set the head of the job id item list to the next job id item in the list
                            pJobIdList = pJobIdItem->ListEntry.Blink;

                            if (IsListEmpty(pJobIdList)) {
                                // This is the last item in the list, so set the list head to NULL
                                pJobIdList = NULL;
                            }
                            else {
                                // Remove the job id item from the list
                                RemoveEntryList(&pJobIdItem->ListEntry);
                            }
                            // Free the job id item
                            MemFree(pJobIdItem);
                        }

                        Disconnect();
                    }

                    SetCursor(LoadCursor(NULL, IDC_ARROW));

                    break;

                case IDM_DOCUMENT_PROPERTIES:
                    // PropSheetHeader is the property sheet header
                    PROPSHEETHEADER  PropSheetHeader;
                    // PropSheetPage is the property sheet page
                    PROPSHEETPAGE    PropSheetPage;

                    // Set pJobIdList to NULL
                    pJobIdList = NULL;

                    // Initialize PropSheetHeader
                    PropSheetHeader.dwSize = sizeof(PropSheetHeader);
                    // Set the property sheet header flags
                    PropSheetHeader.dwFlags = PSH_NOAPPLYNOW | PSH_PROPSHEETPAGE | PSH_PROPTITLE;
                    // Set the property sheet header owner window
                    PropSheetHeader.hwndParent = hWnd;
                    // Set the property sheet header hInstance
                    PropSheetHeader.hInstance = g_hInstance;
                    // Set the number of property sheet pages
                    PropSheetHeader.nPages = 1;
                    // Set the start property sheet page
                    PropSheetHeader.nStartPage = 0;
                    PropSheetHeader.pStartPage = NULL;
                    // Set the property sheet pages
                    PropSheetHeader.ppsp = &PropSheetPage;

                    // Initialize PropSheetPage
                    PropSheetPage.dwSize = sizeof(PropSheetPage);
                    // Set the property sheet page flags
                    PropSheetPage.dwFlags = 0;
                    // Set the property sheet page hInstance
                    PropSheetPage.hInstance = g_hInstance;
                    // Set the property sheet page dialog template
                    PropSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_DOCUMENT_PROPERTIES);
                    // Set the property sheet page dialog procedure
                    PropSheetPage.pfnDlgProc = DocumentPropertiesDlgProc;

                    SetCursor(LoadCursor(NULL, IDC_WAIT));
                    if (Connect()) {
                        // Enumerate each selected item in the list view
                        dwListIndex = ListView_GetNextItem(g_hWndListView, -1, LVNI_ALL | LVNI_SELECTED);
                        while (dwListIndex != -1) {
                            // Initialize lvi
                            lvi.mask = LVIF_PARAM;
                            // Set the item number
                            lvi.iItem = dwListIndex;
                            // Set the subitem number
                            lvi.iSubItem = 0;
                            // Set the lParam
                            lvi.lParam = 0;
                            // Get the selected item from the list view
                            if (ListView_GetItem(g_hWndListView, &lvi)) {
                                // Add the job id item to the list
                                pJobIdItem = (PJOB_ID_ITEM) MemAlloc(sizeof(JOB_ID_ITEM));
                                if (pJobIdItem) {
                                    // Copy dwJobId
                                    pJobIdItem->dwJobId = (DWORD) lvi.lParam;
                                    // Insert the job id item into the list
                                    if (pJobIdList) {
                                        InsertTailList(pJobIdList, &pJobIdItem->ListEntry);
                                    }
                                    else {
                                        pJobIdList = &pJobIdItem->ListEntry;
                                        InitializeListHead(pJobIdList);
                                    }
                                }
                            }

                            dwListIndex = ListView_GetNextItem(g_hWndListView, dwListIndex, LVNI_ALL | LVNI_SELECTED);
                        }

                        while (pJobIdList) {
                            // Get the job id item from the list
                            pJobIdItem = (PJOB_ID_ITEM) pJobIdList;
                            if (FaxGetJob(g_hFaxSvcHandle, pJobIdItem->dwJobId, &pFaxJobEntry)) {
                                // Set the property sheet header pszCaption
                                PropSheetHeader.pszCaption = pFaxJobEntry->DocumentName;
                                // Set the property sheet page lParam
                                PropSheetPage.lParam = (LPARAM) pFaxJobEntry;

                                // Create the property sheet
                                PropertySheet(&PropSheetHeader);
                            }

                            // Set the head of the job id item list to the next job id item in the list
                            pJobIdList = pJobIdItem->ListEntry.Blink;

                            if (IsListEmpty(pJobIdList)) {
                                // This is the last item in the list, so set the list head to NULL
                                pJobIdList = NULL;
                            }
                            else {
                                // Remove the job id item from the list
                                RemoveEntryList(&pJobIdItem->ListEntry);
                            }
                            // Free the job id item
                            MemFree(pJobIdItem);
                        }

                        Disconnect();
                    }

                    SetCursor(LoadCursor(NULL, IDC_ARROW));

                    break;

#ifdef TOOLBAR_ENABLED
                case IDM_VIEW_TOOLBAR:
                    if (WinPosInfo.bToolbarVisible) {
                        // Close the toolbar
                        DestroyWindow(g_hWndToolbar);
                        DestroyWindow(hWndToolTips);
                        ZeroMemory(&rcToolbar, sizeof(rcToolbar));
                    }
                    else {
                        // Show the toolbar
                        hWndToolTips = CreateToolTips(hWnd);
                        g_hWndToolbar = CreateToolbar(hWnd);
                        // Get the rectangle of the toolbar
                        GetWindowRect(g_hWndToolbar, &rcToolbar);
                    }

                    WinPosInfo.bToolbarVisible = !WinPosInfo.bToolbarVisible;
                    // Check the menu item
                    CheckMenuItem(hViewMenu, IDM_VIEW_TOOLBAR, MF_BYCOMMAND | (WinPosInfo.bToolbarVisible ? MF_CHECKED : MF_UNCHECKED));

                    // Get the rectangle of the client area
                    GetClientRect(hWnd, &rcClient);
                    // Resize the list view
                    MoveWindow(g_hWndListView, 0, (rcToolbar.bottom - rcToolbar.top), rcClient.right, rcClient.bottom - (rcStatusBar.bottom - rcStatusBar.top) - (rcToolbar.bottom - rcToolbar.top), TRUE);
                    break;
#endif // TOOLBAR_ENABLED

                case IDM_VIEW_STATUS_BAR:
                    if (WinPosInfo.bStatusBarVisible) {
                        // Close the status bar
                        DestroyWindow(hWndStatusBar);
                        ZeroMemory(&rcStatusBar, sizeof(rcStatusBar));
                    }
                    else {
                        // Show the status bar
                        hWndStatusBar = CreateStatusWindow(WS_CHILD | WS_CLIPSIBLINGS | WS_TABSTOP | WS_VISIBLE | SBARS_SIZEGRIP, NULL, hWnd, IDM_STATUS_BAR);
                        // Get the rectangle of the status bar
                        GetWindowRect(hWndStatusBar, &rcStatusBar);
                    }

                    WinPosInfo.bStatusBarVisible = !WinPosInfo.bStatusBarVisible;
                    // Check the menu item
                    CheckMenuItem(hViewMenu, IDM_VIEW_STATUS_BAR, MF_BYCOMMAND | (WinPosInfo.bStatusBarVisible ? MF_CHECKED : MF_UNCHECKED));

                    // Get the rectangle of the client area
                    GetClientRect(hWnd, &rcClient);
                    // Resize the list view
                    MoveWindow(g_hWndListView, 0, (rcToolbar.bottom - rcToolbar.top), rcClient.right, rcClient.bottom - (rcStatusBar.bottom - rcStatusBar.top) - (rcToolbar.bottom - rcToolbar.top), TRUE);
                    break;

                case IDM_VIEW_REFRESH:
                    if (WaitForSingleObject(g_hStartEvent, 0) == WAIT_OBJECT_0) {
                        // Post an event to the completion port to indicate thread is to refresh
                        PostEventToCompletionPort(g_hCompletionPort, FEI_FAXSVC_STARTED, (DWORD) -1);
                    }
                    else {
                        // Set the start event
                        SetEvent(g_hStartEvent);
                    }

                    break;

                case IDM_HELP_TOPICS:
                    HtmlHelp(GetDesktopWindow(), FAXQUEUE_HTMLHELP_FILENAME, HH_DISPLAY_TOPIC, 0L);
                    break;

                case IDM_HELP_ABOUT:
                    // szCaption is the caption for the shell about dialog box
                    TCHAR  szCaption[RESOURCE_STRING_LEN];

                    LoadString(g_hInstance, IDS_FAXQUEUE_LOCAL_CAPTION, szCaption, RESOURCE_STRING_LEN);
                    ShellAbout(hWnd, szCaption, NULL, NULL);
                    break;

                case IDM_FAX_SET_AS_DEFAULT_PRINTER:
                case IDM_FAX_SHARING:
                case IDM_FAX_PROPERTIES:
                    // pFaxPrintersConfig is the pointer to the fax printers
                    LPPRINTER_INFO_2  pFaxPrintersConfig;
                    // dwNumFaxPrinters is the number of fax printers
                    DWORD             dwNumFaxPrinters;

                    // Set the current menu selection
                    if (LOWORD(wParam) == IDM_FAX_SET_AS_DEFAULT_PRINTER) {
                        uCurrentMenu = IDM_FAX_SET_AS_DEFAULT_PRINTER_MORE;
                    }
                    else if (LOWORD(wParam) == IDM_FAX_SHARING) {
                        uCurrentMenu = IDM_FAX_SHARING_MORE;
                    }
                    else if (LOWORD(wParam) == IDM_FAX_PROPERTIES) {
                        uCurrentMenu = IDM_FAX_PROPERTIES_MORE;
                    }
                    else {
                        break;
                    }

                    // Get the fax printers
                    pFaxPrintersConfig = (LPPRINTER_INFO_2) GetFaxPrinters(&dwNumFaxPrinters);
                    if (pFaxPrintersConfig) {
                        // Allocate the memory for the printer name
                        szPrinterName = (LPTSTR) MemAlloc((lstrlen(pFaxPrintersConfig[0].pPrinterName) + 1) * sizeof(TCHAR));
                        if (szPrinterName) {
                            // Copy the printer name
                            lstrcpy(szPrinterName, pFaxPrintersConfig[0].pPrinterName);

                            // Post a message that a printer has been selected
                            PostMessage(hWnd, UM_SELECT_FAX_PRINTER, (UINT_PTR) szPrinterName, 0);
                        }

                        MemFree(pFaxPrintersConfig);
                    }

                    break;

                case IDM_FAX_SET_AS_DEFAULT_PRINTER_1:
                case IDM_FAX_SET_AS_DEFAULT_PRINTER_2:
                case IDM_FAX_SET_AS_DEFAULT_PRINTER_3:
                case IDM_FAX_SET_AS_DEFAULT_PRINTER_4:
                case IDM_FAX_SHARING_1:
                case IDM_FAX_SHARING_2:
                case IDM_FAX_SHARING_3:
                case IDM_FAX_SHARING_4:
                case IDM_FAX_PROPERTIES_1:
                case IDM_FAX_PROPERTIES_2:
                case IDM_FAX_PROPERTIES_3:
                case IDM_FAX_PROPERTIES_4:
                    // hMenu is the handle to the menu
                    HMENU   hMenu;
                    // szMenuString is a menu string
                    TCHAR   szMenuString[RESOURCE_STRING_LEN];
                    // szMenuItemName is the menu item name
                    LPTSTR  szMenuItemName;

                    // Get the handle to the menu, set the current menu selection, and set the menu string
                    if ((LOWORD(wParam) >= IDM_FAX_SET_AS_DEFAULT_PRINTER_1) && (LOWORD(wParam) < IDM_FAX_SET_AS_DEFAULT_PRINTER_MORE)) {
                        hMenu = hFaxSetAsDefaultMenu;
                        uCurrentMenu = IDM_FAX_SET_AS_DEFAULT_PRINTER_MORE;
                        wsprintf(szMenuString, TEXT("&%d "), LOWORD(wParam) - IDM_FAX_SET_AS_DEFAULT_PRINTER_1 + 1);
                    }
                    else if ((LOWORD(wParam) >= IDM_FAX_SHARING_1) && (LOWORD(wParam) < IDM_FAX_SHARING_MORE)) {
                        hMenu = hFaxSharingMenu;
                        uCurrentMenu = IDM_FAX_SHARING_MORE;
                        wsprintf(szMenuString, TEXT("&%d "), LOWORD(wParam) - IDM_FAX_SHARING_1 + 1);
                    }
                    else if ((LOWORD(wParam) >= IDM_FAX_PROPERTIES_1) && (LOWORD(wParam) < IDM_FAX_PROPERTIES_MORE)) {
                        hMenu = hFaxPropertiesMenu;
                        uCurrentMenu = IDM_FAX_PROPERTIES_MORE;
                        wsprintf(szMenuString, TEXT("&%d "), LOWORD(wParam) - IDM_FAX_PROPERTIES_1 + 1);
                    }
                    else {
                        break;
                    }

                    // Initialize the menu item info
                    mii.cbSize = sizeof(mii);
                    mii.fMask = MIIM_TYPE;
                    mii.fType = MFT_STRING;
                    mii.dwTypeData = NULL;
                    mii.cch = 0;

                    // Get the size of the menu item
                    if (GetMenuItemInfo(hMenu, LOWORD(wParam), FALSE, &mii)) {
                        mii.cch++;
                        // Allocate the memory for the menu item
                        szMenuItemName = (LPTSTR) MemAlloc(mii.cch * sizeof(TCHAR));
                        if (szMenuItemName) {
                            mii.dwTypeData = szMenuItemName;
                            // Get the menu item
                            if (GetMenuItemInfo(hMenu, LOWORD(wParam), FALSE, &mii)) {
                                // Allocate the memory for the printer name
                                szPrinterName = (LPTSTR) MemAlloc((lstrlen(szMenuItemName) - lstrlen(szMenuString) + 1) * sizeof(TCHAR));
                                if (szPrinterName) {
                                    // Copy the printer name
                                    lstrcpy(szPrinterName, (LPTSTR) ((UINT_PTR) szMenuItemName + lstrlen(szMenuString) * sizeof(TCHAR)));

                                    // Post a message that a printer has been selected
                                    PostMessage(hWnd, UM_SELECT_FAX_PRINTER, (UINT_PTR) szPrinterName, 0);
                                }
                            }

                            MemFree(szMenuItemName);
                        }
                    }

                    break;

                case IDM_FAX_SET_AS_DEFAULT_PRINTER_MORE:
                case IDM_FAX_SHARING_MORE:
                case IDM_FAX_PROPERTIES_MORE:
                    uCurrentMenu = LOWORD(wParam);
                    DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_SELECT_FAX_PRINTER), hWnd, SelectFaxPrinterDlgProc);
                    break;

            }

            break;

        case WM_CLOSE:
            // Set the exit event
            SetEvent(g_hExitEvent);
            if (WaitForSingleObject(g_hStartEvent, 0) == WAIT_OBJECT_0) {
                // Post an event to the completion port to indicate thread is to exit
                PostEventToCompletionPort(g_hCompletionPort, FEI_FAXSVC_ENDED, (DWORD) -1);
            }

#ifdef TOOLBAR_ENABLED
            // Set the persistent data
            SetFaxQueueRegistryData(WinPosInfo.bToolbarVisible, WinPosInfo.bStatusBarVisible, g_hWndListView, hWnd);
#else
            // Set the persistent data
            SetFaxQueueRegistryData(WinPosInfo.bStatusBarVisible, g_hWndListView, hWnd);
#endif // TOOLBAR_ENABLED

            // Free the process info list
            while (pProcessInfoList) {
                // Get the head of the process info list
                pProcessInfoItem = (PPROCESS_INFO_ITEM) pProcessInfoList;

                // Set the head of process info list to the next process info item in the list
                pProcessInfoList = pProcessInfoItem->ListEntry.Blink;

                if (IsListEmpty(pProcessInfoList)) {
                    // This is the last item in the list, so set the list head to NULL
                    pProcessInfoList = NULL;
                }
                else {
                    // Remove the process info item from the list
                    RemoveEntryList(&pProcessInfoItem->ListEntry);
                }
                // Free the process info item
                MemFree(pProcessInfoItem);
            }

            break;

        case WM_DESTROY:
#ifdef TOOLBAR_ENABLED
            if (WinPosInfo.bToolbarVisible) {
                // Close the toolbar
                DestroyWindow(g_hWndToolbar);
                DestroyWindow(hWndToolTips);
            }
#endif // TOOLBAR_ENABLED

            if (WinPosInfo.bStatusBarVisible) {
                // Close the status bar
                DestroyWindow(hWndStatusBar);
            }

            // Close the list view
            DestroyWindow(g_hWndListView);

            PostQuitMessage(0);
            break;
    }

    return DefWindowProc(hWnd, iMsg, wParam, lParam);
}

INT_PTR CALLBACK SelectFaxPrinterDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
    // hWndPrinterList is the handle to the fax printer list box
    static HWND   hWndPrinterList;

    switch(iMsg) {
        case WM_INITDIALOG:
            // pFaxPrintersConfig is the pointer to the fax printers
            LPPRINTER_INFO_2  pFaxPrintersConfig;
            // dwNumFaxPrinters is the number of fax printers
            DWORD             dwNumFaxPrinters;
            // dwIndex is a counter to enumerate each printer
            DWORD             dwIndex;

            // Get the handle to the fax printer list box
            hWndPrinterList = GetDlgItem(hDlg, IDC_FAX_PRINTER_LIST);

            // Get the fax printers
            pFaxPrintersConfig = (LPPRINTER_INFO_2) GetFaxPrinters(&dwNumFaxPrinters);
            if ((pFaxPrintersConfig) && (dwNumFaxPrinters)) {
                // Propagate the list box with the list of fax printers
                for (dwIndex = 0; dwIndex < dwNumFaxPrinters; dwIndex++) {
                    SendMessage(hWndPrinterList, LB_ADDSTRING, 0, (UINT_PTR) pFaxPrintersConfig[dwIndex].pPrinterName);
                }
            }

            if (pFaxPrintersConfig) {
                MemFree(pFaxPrintersConfig);
            }

            return TRUE;

        case WM_COMMAND:
            switch(HIWORD(wParam)) {
                case LBN_DBLCLK:
                    SendMessage(GetDlgItem(hDlg, IDOK), BM_CLICK, 0, 0);
                    break;
            }

            switch(LOWORD(wParam)) {
                case IDOK:
                    // szPrinterName is the printer name
                    LPTSTR     szPrinterName;
                    // ulpIndex is the index of the currently selected item in the list box
                    ULONG_PTR  ulpIndex;
                    DWORD      cb;

                    // Get the current selection of the list box
                    ulpIndex = SendMessage(hWndPrinterList, LB_GETCURSEL, 0, 0);
                    if (ulpIndex != LB_ERR) {
                        // Get the size of the current selection of the list box
                        cb = (DWORD) SendMessage(hWndPrinterList, LB_GETTEXTLEN, ulpIndex, NULL);
                        if (cb != LB_ERR) {
                            // Allocate the memory for the current selection
                            szPrinterName = (LPTSTR) MemAlloc((cb + 1) * sizeof(TCHAR));
                            if (szPrinterName) {
                                // Get the current selection of the list box
                                if (SendMessage(hWndPrinterList, LB_GETTEXT, ulpIndex, (UINT_PTR) szPrinterName) != LB_ERR) {
                                    // Post a message that a printer has been selected
                                    PostMessage(g_hWndMain, UM_SELECT_FAX_PRINTER, (UINT_PTR) szPrinterName, 0);
                                }
                            }
                        }
                    }

                case IDCANCEL:
                    EndDialog(hDlg, LOWORD(wParam));
                    return TRUE;
            }
            break;
    }

    return FALSE;
}

INT_PTR CALLBACK DocumentPropertiesDlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
    switch(iMsg) {
        case WM_INITDIALOG:
            // pFaxJobEntry is the pointer to the fax job
            PFAX_JOB_ENTRY  pFaxJobEntry;
            // szColumnItem is text of a column item for those items that are equivalent to a column item
            LPTSTR          szColumnItem;

            // Get the pointer to the fax job from the property sheet page
            pFaxJobEntry = (PFAX_JOB_ENTRY) ((LPPROPSHEETPAGE) lParam)->lParam;

            // Get the column item text for the document name
            szColumnItem = GetColumnItemText(eDocumentName, pFaxJobEntry, NULL);
            // Set the job type static text
            if (szColumnItem) {
                SetDlgItemText(hDlg, IDC_FAX_DOCUMENTNAME, szColumnItem);
                MemFree(szColumnItem);
            }

            // Set the recipient's name static text
            if (pFaxJobEntry->RecipientName) {
                SetDlgItemText(hDlg, IDC_FAX_RECIPIENTNAME, pFaxJobEntry->RecipientName);
            }

            // Set the recipient's fax number static text
            if (pFaxJobEntry->RecipientNumber) {
                SetDlgItemText(hDlg, IDC_FAX_RECIPIENTNUMBER, pFaxJobEntry->RecipientNumber);
            }

            // Set the sender's name static text
            if (pFaxJobEntry->SenderName) {
                SetDlgItemText(hDlg, IDC_FAX_SENDERNAME, pFaxJobEntry->SenderName);
            }

            // Set the sender's company static text
            if (pFaxJobEntry->SenderCompany) {
                SetDlgItemText(hDlg, IDC_FAX_SENDERCOMPANY, pFaxJobEntry->SenderCompany);
            }

            // Set the sender's department static text
            if (pFaxJobEntry->SenderDept) {
                SetDlgItemText(hDlg, IDC_FAX_SENDERDEPT, pFaxJobEntry->SenderDept);
            }

            // Set the billing code static text
            if (pFaxJobEntry->BillingCode) {
                SetDlgItemText(hDlg, IDC_FAX_BILLINGCODE, pFaxJobEntry->BillingCode);
            }

            // Get the column item text for the job type
            szColumnItem = GetColumnItemText(eJobType, pFaxJobEntry, NULL);
            // Set the job type static text
            if (szColumnItem) {
                SetDlgItemText(hDlg, IDC_FAX_JOBTYPE, szColumnItem);
                MemFree(szColumnItem);
            }

            // Get the column item text for the status
            szColumnItem = GetColumnItemText(eStatus, pFaxJobEntry, NULL);
            // Set the status static text
            if (szColumnItem) {
                SetDlgItemText(hDlg, IDC_FAX_STATUS, szColumnItem);
                MemFree(szColumnItem);
            }

            // Get the column item text for the pages
            szColumnItem = GetColumnItemText(ePages, pFaxJobEntry, NULL);
            // Set the pages static text
            if (szColumnItem) {
                SetDlgItemText(hDlg, IDC_FAX_PAGES, szColumnItem);
                MemFree(szColumnItem);
            }

            // Get the column item text for the size
            szColumnItem = GetColumnItemText(eSize, pFaxJobEntry, NULL);
            // Set the size static text
            if (szColumnItem) {
                SetDlgItemText(hDlg, IDC_FAX_SIZE, szColumnItem);
                MemFree(szColumnItem);
            }

            // Get the column item text for the scheduled time
            szColumnItem = GetColumnItemText(eScheduledTime, pFaxJobEntry, NULL);
            // Set the scheduled time static text
            if (szColumnItem) {
                SetDlgItemText(hDlg, IDC_FAX_SCHEDULEDTIME, szColumnItem);
                MemFree(szColumnItem);
            }

            FaxFreeBuffer(pFaxJobEntry);

            return TRUE;

        case WM_HELP:
        case WM_CONTEXTMENU:
            FAXWINHELP(iMsg, wParam, lParam, DocumentPropertiesHelpIDs);
            break;

    }

    return FALSE;
}

BOOL CALLBACK EnumThreadWndProc(HWND hWnd, LPARAM lParam)
{
    if (GetWindowLong(hWnd, GWL_STYLE) & WS_VISIBLE) {
        CopyMemory((LPBYTE) lParam, &hWnd, sizeof(hWnd));
        return FALSE;
    }

    return TRUE;
}

VOID PostEventToCompletionPort(HANDLE hCompletionPort, DWORD dwEventId, DWORD dwJobId)
{
    PFAX_EVENT  pFaxEvent;

    pFaxEvent = (PFAX_EVENT) LocalAlloc(LPTR, sizeof(FAX_EVENT));
    pFaxEvent->EventId = dwEventId;
    pFaxEvent->JobId = dwJobId;

    PostQueuedCompletionStatus(hCompletionPort, sizeof(FAX_EVENT), 0, (LPOVERLAPPED) pFaxEvent);
}

DWORD FaxEventThread (LPVOID lpv)
{
    // hExitStartEvents is a pointer to the g_hExitEvent and g_hStartEvent
    HANDLE               hExitStartEvents[2];

    // mii is the menu item info
    MENUITEMINFO         mii;
    // hFaxMenu is a handle to the fax menu
    HMENU                hFaxMenu;

    // pFaxConfig is the pointer to the fax configuration
    PFAX_CONFIGURATION   pFaxConfig;

    // pPortJobInfoList is a pointer to the port job info list
    PLIST_ENTRY          pPortJobInfoList;
    // pPortJobInfo is a pointer to a PORT_JOB_INFO_ITEM structure
    PPORT_JOB_INFO_ITEM  pPortJobInfoItem;
    // pFaxPortInfo is the pointer to the fax ports
    PFAX_PORT_INFO       pFaxPortInfo;
    // dwNumFaxPorts is the number of fax ports
    DWORD                dwNumFaxPorts;
    // szDeviceName is the device name of the current fax port
    LPTSTR               szDeviceName;
    // dwJobId is the fax job id on the current fax port
    DWORD                dwJobId;

    // pFaxJobEntry is the pointer to the fax jobs
    PFAX_JOB_ENTRY       pFaxJobEntry;
    // dwNumFaxJobs is the number of fax jobs
    DWORD                dwNumFaxJobs;
    // lvfi specifies the attributes of a particular item to find in the list view
    LV_FINDINFO          lvfi;
    // dwListIndex is the index of a particular item in the list view
    DWORD                dwListIndex;
    // nColumnIndex is used to enumerate each column of the list view
    INT                  nColumnIndex;
    // szColumnItem is the text of a column item
    LPTSTR               szColumnItem;
    // uState is the state of a particular item in the list view
    UINT                 uState;
    // dwOldFocusIndex is the old item in the list view with the focus
    DWORD                dwOldFocusIndex;
    // dwNewFocusIndex is the new item in the list view with the focus
    DWORD                dwNewFocusIndex;

    // pFaxEvent is a pointer to the port event
    PFAX_EVENT           pFaxEvent;
    DWORD                dwBytes;
    ULONG_PTR            ulpCompletionKey;

    // dwIndex is a counter to enumerate each fax port and fax job
    DWORD                dwIndex;
    DWORD                dwRslt;

    // Set hExitStartEvents
    // g_hExitEvent
    hExitStartEvents[0] = g_hExitEvent;
    // g_hStartEvent
    hExitStartEvents[1] = g_hStartEvent;

    // Initialize hFaxMenu
    hFaxMenu = NULL;

    // Set pPortJobInfo to NULL
    pPortJobInfoList = NULL;

    while (TRUE) {
        // Wait for Exit, or Start event
        dwRslt = WaitForMultipleObjects(2, hExitStartEvents, FALSE, INFINITE);
        if (dwRslt == WAIT_OBJECT_0) {
            // Exit event was signaled, so exit
            return 0;
        }

        // Set the window title to indicate connecting
        SetWindowText(g_hWndMain, g_szTitleConnecting);

        // Get the handle to the fax menu
        if (!hFaxMenu) {
            hFaxMenu = GetSubMenu(GetMenu(g_hWndMain), 0);
        }

        // Create the completion port
        g_hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
        if (!g_hCompletionPort) {
            goto ExitLevel0;
        }

        // Connect to the fax service
        if (!Connect()) {
            goto ExitLevel1;
        }

        // Initialize the fax event queue
        if (!FaxInitializeEventQueue(g_hFaxSvcHandle, g_hCompletionPort, 0, NULL, 0)) {
            // Disconnect from the fax service
            Disconnect();
            goto ExitLevel1;
        }

        // Determine if faxing is paused
        if (FaxGetConfiguration(g_hFaxSvcHandle, &pFaxConfig)) {
            // Check the menu item
            CheckMenuItem(hFaxMenu, IDM_FAX_PAUSE_FAXING, MF_BYCOMMAND | (pFaxConfig->PauseServerQueue ? MF_CHECKED : MF_UNCHECKED));
#ifdef TOOLBAR_ENABLED
            // Enable the pause faxing toolbar menu item
            EnableToolbarMenuState(g_hWndToolbar, IDM_FAX_PAUSE_FAXING, pFaxConfig->PauseServerQueue);
#endif // TOOLBAR_ENABLED
            FaxFreeBuffer(pFaxConfig);
        }

        // Enumerate the fax ports
        if (FaxEnumPorts(g_hFaxSvcHandle, &pFaxPortInfo, &dwNumFaxPorts)) {
            for (dwIndex = 0; dwIndex < dwNumFaxPorts; dwIndex++) {
                // Add each port job info into the list
                pPortJobInfoItem = (PPORT_JOB_INFO_ITEM) MemAlloc(sizeof(PORT_JOB_INFO_ITEM) + (lstrlen(pFaxPortInfo[dwIndex].DeviceName) + 1) * sizeof(TCHAR));
                if (pPortJobInfoItem) {
                    // Copy dwDeviceId
                    pPortJobInfoItem->dwDeviceId = pFaxPortInfo[dwIndex].DeviceId;

                    // Set szDeviceName
                    pPortJobInfoItem->szDeviceName = (LPTSTR) ((UINT_PTR) pPortJobInfoItem + sizeof(PORT_JOB_INFO_ITEM));
                    // Copy szDeviceName
                    lstrcpy(pPortJobInfoItem->szDeviceName, pFaxPortInfo[dwIndex].DeviceName);

                    // Set dwJobId
                    pPortJobInfoItem->dwJobId = (DWORD) -1;

                    // Insert the port job info into the list
                    if (pPortJobInfoList) {
                        InsertTailList(pPortJobInfoList, &pPortJobInfoItem->ListEntry);
                    }
                    else {
                        pPortJobInfoList = &pPortJobInfoItem->ListEntry;
                        InitializeListHead(pPortJobInfoList);
                    }
                }
            }

            FaxFreeBuffer(pFaxPortInfo);
        }

        // Enable the pause faxing menu item and the cancel all faxes menu item
        EnableMenuItem(hFaxMenu, IDM_FAX_PAUSE_FAXING, MF_BYCOMMAND | MF_ENABLED);
        EnableMenuItem(hFaxMenu, IDM_FAX_CANCEL_ALL_FAXES, MF_BYCOMMAND | MF_ENABLED);

#ifdef TOOLBAR_ENABLED
        // Enable the pause faxing toolbar menu item and the cancel all faxes toolbar menu item
        EnableToolbarMenuState(g_hWndToolbar, IDM_FAX_PAUSE_FAXING, TRUE);
        EnableToolbarMenuState(g_hWndToolbar, IDM_FAX_CANCEL_ALL_FAXES, TRUE);
#endif // TOOLBAR_ENABLED

        // Disconnect from the fax service
        Disconnect();

        // Wait for fax events
        while (GetQueuedCompletionStatus(g_hCompletionPort, &dwBytes, &ulpCompletionKey, (LPOVERLAPPED *) &pFaxEvent, INFINITE)) {
            if (pFaxEvent->EventId == FEI_FAXSVC_ENDED) {
                // Thread should stop listening for fax events
                LocalFree(pFaxEvent);
                break;
            }

            switch (pFaxEvent->EventId) {
                case FEI_MODEM_POWERED_ON:
                case FEI_MODEM_POWERED_OFF:
                case FEI_RINGING:
                case FEI_ABORTING:
                    // Ignore these fax events
                    break;

                case FEI_FAXSVC_STARTED:
                    // Set the window title to indicate refreshing
                    SetWindowText(g_hWndMain, g_szTitleRefreshing);

                    // Initialize the menu item info
                    mii.cbSize = sizeof(mii);
                    mii.fMask = MIIM_STATE;
                    mii.fState = 0;

                    // Get the state of the menu item
                    GetMenuItemInfo(hFaxMenu, IDM_FAX_PAUSE_FAXING, FALSE, &mii);

                    // Connect to the fax service
                    if (Connect()) {
                        // Enumerate the fax jobs
                        if (FaxEnumJobs(g_hFaxSvcHandle, &pFaxJobEntry, &dwNumFaxJobs)) {
                            // Initialize lvfi
                            lvfi.flags = LVFI_PARAM;

                            // Get the old item with the focus
                            dwOldFocusIndex = ListView_GetNextItem(g_hWndListView, -1, LVNI_ALL | LVNI_FOCUSED);

                            // Add new fax jobs and move existing fax jobs to their correct position
                            for (dwIndex = 0; dwIndex < dwNumFaxJobs; dwIndex++) {
                                // Set the search criteria
                                lvfi.lParam = pFaxJobEntry[dwIndex].JobId;

                                // Initialize the item's state
                                uState = 0;

                                // Initialize the device name
                                szDeviceName = NULL;

                                if (pPortJobInfoList) {
                                    // Find the appropriate job
                                    pPortJobInfoItem = (PPORT_JOB_INFO_ITEM) pPortJobInfoList;

                                    while (TRUE) {
                                        if (pFaxJobEntry[dwIndex].JobId == pPortJobInfoItem->dwJobId) {
                                            // Job id matches, so this is the appropriate port
                                            // Get the device name
                                            szDeviceName = pPortJobInfoItem->szDeviceName;
                                            break;
                                        }

                                        // Step to the next port job info item
                                        pPortJobInfoItem = (PPORT_JOB_INFO_ITEM) pPortJobInfoItem->ListEntry.Blink;

                                        if (pPortJobInfoItem == (PPORT_JOB_INFO_ITEM) pPortJobInfoList) {
                                            // The list has been traversed
                                            break;
                                        }
                                    }
                                }

                                // Find the job in the list view
                                dwListIndex = ListView_FindItem(g_hWndListView, -1, &lvfi);
                                if ((dwListIndex != -1) && (dwListIndex != dwIndex)) {
                                    // Job exists in the list view but is in the wrong position, so get the item
                                    // Get the item's state
                                    uState = ListView_GetItemState(g_hWndListView, dwListIndex, LVIS_FOCUSED | LVIS_SELECTED);

                                    // Delete the item from its current position
                                    ListView_DeleteItem(g_hWndListView, dwListIndex);
                                    // Set dwListIndex to -1 so item will be inserted into the list view
                                    dwListIndex = -1;
                                }

                                for (nColumnIndex = 0; nColumnIndex < (INT) eIllegalColumnIndex; nColumnIndex++) {
                                    // Get the column item text
                                    szColumnItem = GetColumnItemText((eListViewColumnIndex) nColumnIndex, &pFaxJobEntry[dwIndex], szDeviceName);
                                    // Insert item into the list view
                                    SetColumnItem(g_hWndListView, (dwListIndex == -1) ? TRUE : FALSE, dwIndex, nColumnIndex, szColumnItem, uState, &pFaxJobEntry[dwIndex]);
                                    if (szColumnItem) {
                                        MemFree(szColumnItem);
                                    }
                                }
                            }

                            // Get the new item with the focus
                            dwNewFocusIndex = ListView_GetNextItem(g_hWndListView, -1, LVNI_ALL | LVNI_FOCUSED);

                            if ((dwOldFocusIndex != -1) && (dwNewFocusIndex != -1) && (dwNewFocusIndex >= dwNumFaxJobs)) {
                                // Job will no longer exist in the list view, so set the focus to the item occupying that index
                                // Get the state of the item occupying that index
                                uState = ListView_GetItemState(g_hWndListView, dwOldFocusIndex, LVIS_FOCUSED | LVIS_SELECTED | LVIS_OVERLAYMASK);
                                // Set the focus to the new item
                                ListView_SetItemState(g_hWndListView, dwOldFocusIndex, uState | LVIS_FOCUSED, uState | LVIS_FOCUSED);
                            }

                            // Remove old fax jobs
                            dwListIndex = ListView_GetItemCount(g_hWndListView);
                            for (dwIndex = dwNumFaxJobs; dwIndex < dwListIndex; dwIndex++) {
                                ListView_DeleteItem(g_hWndListView, dwNumFaxJobs);
                            }

                            FaxFreeBuffer(pFaxJobEntry);
                        }

                        // Disconnect from the fax service
                        Disconnect();
                    }

                    // Set the window title to indicate connected or paused
                    SetWindowText(g_hWndMain, mii.fState & MFS_CHECKED ? g_szTitlePaused : g_szTitleConnected);

                    continue;

                case FEI_JOB_QUEUED:
                case FEI_ANSWERED:
                    // Initialize the device name
                    szDeviceName = NULL;

                    if ((pFaxEvent->EventId == FEI_ANSWERED) && (pPortJobInfoList)) {
                        // Find the appropriate port
                        pPortJobInfoItem = (PPORT_JOB_INFO_ITEM) pPortJobInfoList;

                        while (TRUE) {
                            if (pFaxEvent->DeviceId == pPortJobInfoItem->dwDeviceId) {
                                // Device id matches, so this is the appropriate port
                                // Set the job id
                                pPortJobInfoItem->dwJobId = pFaxEvent->JobId;
                                // Get the device name
                                szDeviceName = pPortJobInfoItem->szDeviceName;
                                break;
                            }

                            // Step to the next port job info item
                            pPortJobInfoItem = (PPORT_JOB_INFO_ITEM) pPortJobInfoItem->ListEntry.Blink;

                            if (pPortJobInfoItem == (PPORT_JOB_INFO_ITEM) pPortJobInfoList) {
                                // The list has been traversed
                                break;
                            }
                        }
                    }

                    // Connect to the fax service
                    if (Connect()) {
                        // Enumerate the fax jobs
                        if (FaxEnumJobs(g_hFaxSvcHandle, &pFaxJobEntry, &dwNumFaxJobs)) {
                            // Initialize lvfi
                            lvfi.flags = LVFI_PARAM;

                            // Add the new fax job to its correct position
                            for (dwIndex = 0; dwIndex < dwNumFaxJobs; dwIndex++) {
                                // Check if the current fax job matches the new fax job
                                if (pFaxJobEntry[dwIndex].JobId != pFaxEvent->JobId) {
                                    continue;
                                }

                                // Set the search criteria
                                lvfi.lParam = pFaxJobEntry[dwIndex].JobId;

                                // Find the job in the list view
                                dwListIndex = ListView_FindItem(g_hWndListView, -1, &lvfi);
                                if (dwListIndex != -1) {
                                    // Job exists in the list view but is in the wrong position, so get the item
                                    // Delete the item from its current position
                                    ListView_DeleteItem(g_hWndListView, dwListIndex);
                                }

                                for (nColumnIndex = 0; nColumnIndex < (INT) eIllegalColumnIndex; nColumnIndex++) {
                                    // Get the column item text
                                    szColumnItem = GetColumnItemText((eListViewColumnIndex) nColumnIndex, &pFaxJobEntry[dwIndex], szDeviceName);
                                    // Insert item into the list view
                                    SetColumnItem(g_hWndListView, TRUE, dwIndex, nColumnIndex, szColumnItem, 0, &pFaxJobEntry[dwIndex]);
                                    if (szColumnItem) {
                                        MemFree(szColumnItem);
                                    }
                                }

                                break;
                            }

                            FaxFreeBuffer(pFaxJobEntry);
                        }

                        // Disconnect from the fax service
                        Disconnect();
                    }

                    continue;

                case FEI_DELETED:
                    // Initialize lvfi
                    lvfi.flags = LVFI_PARAM;

                    // Set the search criteria
                    lvfi.lParam = pFaxEvent->JobId;

                    // Find the job in the list view
                    dwListIndex = ListView_FindItem(g_hWndListView, -1, &lvfi);
                    if (dwListIndex != -1) {
                        // Delete the item from the list view
                        ListView_DeleteItem(g_hWndListView, dwListIndex);
                    }

                    continue;

                default:
                    // Initialize the device name
                    szDeviceName = NULL;
                    // Initialize the fax job id
                    dwJobId = (DWORD) -1;

                    // Set the port job info item
                    if ((pFaxEvent->EventId != (DWORD) -1) && (pPortJobInfoList)) {
                        // Find the appropriate port
                        pPortJobInfoItem = (PPORT_JOB_INFO_ITEM) pPortJobInfoList;

                        while (TRUE) {
                            if (pFaxEvent->DeviceId == pPortJobInfoItem->dwDeviceId) {
                                // Device id matches, so this is the appropriate port
                                // Get the fax job id
                                if (pFaxEvent->EventId == FEI_IDLE) {
                                    dwJobId = pPortJobInfoItem->dwJobId;
                                }
                                else {
                                    dwJobId = pFaxEvent->JobId;
                                    // Set the device name
                                    szDeviceName = pPortJobInfoItem->szDeviceName;
                                }

                                // Update the job id
                                pPortJobInfoItem->dwJobId = pFaxEvent->JobId;
                                break;
                            }

                            // Step to the next port job info item
                            pPortJobInfoItem = (PPORT_JOB_INFO_ITEM) pPortJobInfoItem->ListEntry.Blink;

                            if (pPortJobInfoItem == (PPORT_JOB_INFO_ITEM) pPortJobInfoList) {
                                // The list has been traversed
                                break;
                            }
                        }
                    }
                    else if (pFaxEvent->EventId == (DWORD) -1) {
                        // Set the fax job id
                        dwJobId = pFaxEvent->JobId;
                    }

                    // Initialize lvfi
                    lvfi.flags = LVFI_PARAM;

                    // Set the search criteria
                    lvfi.lParam = dwJobId;

                    // Find the job in the list view
                    dwListIndex = ListView_FindItem(g_hWndListView, -1, &lvfi);
                    if (dwListIndex != -1) {
                        // Connect to the fax service
                        if (Connect()) {
                            // Get the fax job
                            pFaxJobEntry = NULL;
                            while ((FaxGetJob(g_hFaxSvcHandle, dwJobId, &pFaxJobEntry)) && (pFaxJobEntry->Status == FPS_AVAILABLE) && ((pFaxJobEntry->JobType == JT_SEND) || (pFaxJobEntry->JobType == JT_RECEIVE))) {
                                FaxFreeBuffer(pFaxJobEntry);
                                pFaxJobEntry = NULL;
                                Sleep(250);
                            }

                            if (pFaxJobEntry) {
                                // Job exists in the list view, so set the item
                                for (nColumnIndex = 0; nColumnIndex < (INT) eIllegalColumnIndex; nColumnIndex++) {
                                     // Get the column item text
                                     szColumnItem = GetColumnItemText((eListViewColumnIndex) nColumnIndex, pFaxJobEntry, szDeviceName);
                                     // Set item in the list view
                                     SetColumnItem(g_hWndListView, FALSE, dwListIndex, nColumnIndex, szColumnItem, 0, pFaxJobEntry);
                                     if (szColumnItem) {
                                         MemFree(szColumnItem);
                                     }
                                }

                                FaxFreeBuffer(pFaxJobEntry);
                            }

                            // Disconnect from the fax service
                            Disconnect();
                        }
                    }

                    break;
            }

            LocalFree(pFaxEvent);
        }

        // Disable the pause faxing menu item and the cancel all faxes menu item
        EnableMenuItem(hFaxMenu, IDM_FAX_PAUSE_FAXING, MF_BYCOMMAND | MF_GRAYED);
        EnableMenuItem(hFaxMenu, IDM_FAX_CANCEL_ALL_FAXES, MF_BYCOMMAND | MF_GRAYED);

#ifdef TOOLBAR_ENABLED
        // Disable the pause faxing toolbar menu item and the cancel all faxes toolbar menu item
        EnableToolbarMenuState(g_hWndToolbar, IDM_FAX_PAUSE_FAXING, FALSE);
        EnableToolbarMenuState(g_hWndToolbar, IDM_FAX_CANCEL_ALL_FAXES, FALSE);
#endif // TOOLBAR_ENABLED

        // Free the port job info list
        while (pPortJobInfoList) {
            // Get the head of the port job info list
            pPortJobInfoItem = (PPORT_JOB_INFO_ITEM) pPortJobInfoList;

            // Set the head of port job info list to the next port job info item in the list
            pPortJobInfoList = pPortJobInfoItem->ListEntry.Blink;

            if (IsListEmpty(pPortJobInfoList)) {
                // This is the last item in the list, so set the list head to NULL
                pPortJobInfoList = NULL;
            }
            else {
                // Remove the port job info item from the list
                RemoveEntryList(&pPortJobInfoItem->ListEntry);
            }
            // Free the port job info item
            MemFree(pPortJobInfoItem);
        }

ExitLevel1:
        CloseHandle(g_hCompletionPort);

ExitLevel0:
        // Reset the start event
        ResetEvent(g_hStartEvent);

        // Set the window title to indicate not connected
        SetWindowText(g_hWndMain, g_szTitleNotConnected);
    }

    return 0;
}