/*++ BUILD Version: 0000    // Increment this if a change has global effects

Copyright (c) 1994-96  Microsoft Corporation

Module Name:

    ui.c

Abstract:

    Contains UI support for TAPI Browser util.

Author:

    Dan Knudson (DanKn)    23-Oct-1994

Revision History:

--*/


#include "tb.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <io.h>
#include <malloc.h>
#include <time.h>
#include <commdlg.h>
#include "resource.h"
#include "vars.h"


extern char szdwSize[];
extern char szdwAddressID[];


HWND hwndEdit2;


char *
PASCAL
GetTimeStamp(
    void
    );

void
ShowStructByField(
    PSTRUCT_FIELD_HEADER    pHeader,
    BOOL    bSubStructure
    );

void
CompletionPortThread(
    LPVOID  pParams
    );


int
WINAPI
WinMain(
    HINSTANCE   hInstance,
    HINSTANCE   hPrevInstance,
    LPSTR       lpCmdLine,
    int         nCmdShow
    )
{
    MSG     msg;
    HWND    hwnd;
    HACCEL  hAccel;


    ghInst = hInstance;

    hwnd = CreateDialog(
        ghInst,
        (LPCSTR)MAKEINTRESOURCE(IDD_DIALOG1),
        (HWND)NULL,
        (DLGPROC) MainWndProc
        );

    hwndEdit2 = CreateWindow ("edit", "", 0, 0, 0, 0, 0, NULL, NULL, ghInst, NULL);

    if (!hwndEdit2)
    {
        MessageBox (NULL, "err creating edit ctl", "", MB_OK);
    }

    hAccel = LoadAccelerators(
        ghInst,
        (LPCSTR)MAKEINTRESOURCE(IDR_ACCELERATOR1)
        );

#if TAPI_2_0

    if ((ghCompletionPort = CreateIoCompletionPort(
            INVALID_HANDLE_VALUE,
            NULL,
            0,
            0
            )))
    {
        DWORD   dwTID;
        HANDLE  hThread;


        if ((hThread = CreateThread(
                (LPSECURITY_ATTRIBUTES) NULL,
                0,
                (LPTHREAD_START_ROUTINE) CompletionPortThread,
                NULL,
                0,
                &dwTID
                )))
        {
            CloseHandle (hThread);
        }
        else
        {
            ShowStr(
                "CreateThread(CompletionPortThread) failed, err=%d",
                GetLastError()
                );
        }
    }
    else
    {
        ShowStr ("CreateIoCompletionPort failed, err=%d", GetLastError());
    }

#endif

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

    DestroyWindow (hwndEdit2);

    return 0;
}


void
CompletionPortThread(
    LPVOID  pParams
    )
{
    DWORD           dwNumBytesTransfered, dwCompletionKey;
    LPLINEMESSAGE   pMsg;


    while (GetQueuedCompletionStatus(
            ghCompletionPort,
            &dwNumBytesTransfered,
            &dwCompletionKey,
            (LPOVERLAPPED *) &pMsg,
            INFINITE
            ))
    {
        if (pMsg)
        {
            tapiCallback(
                pMsg->hDevice,
                pMsg->dwMessageID,
                pMsg->dwCallbackInstance,
                pMsg->dwParam1,
                pMsg->dwParam2,
                pMsg->dwParam3
                );

            LocalFree (pMsg);
        }
        else
        {
            break;
        }
    }
}


void
GetCurrentSelections(
    void
    )
{
    LONG      lSel = SendMessage (ghwndList1, LB_GETCURSEL, 0, 0);
    PMYWIDGET pWidget = aWidgets;


    //
    // Init all pXxxSel ptrs to NULL
    //

    pLineAppSel  = (PMYLINEAPP) NULL;
    pLineSel     = (PMYLINE) NULL;
    pCallSel     =
    pCallSel2    = (PMYCALL) NULL;
    pPhoneAppSel = (PMYPHONEAPP) NULL;
    pPhoneSel    = (PMYPHONE) NULL;


    if (lSel != LB_ERR)
    {
        //
        // Find the selected widget & set globals appropriately
        //

        pWidget = (PMYWIDGET) SendMessage(
            ghwndList1,
            LB_GETITEMDATA,
            (WPARAM) lSel,
            0
            );

        switch (pWidget->dwType)
        {
        case WT_LINEAPP:

            pLineAppSel = (PMYLINEAPP) pWidget;
            break;

        case WT_LINE:

            pLineSel    = (PMYLINE) pWidget;
            pLineAppSel = pLineSel->pLineApp;
            break;

        case WT_CALL:

            pCallSel    = (PMYCALL) pWidget;
            pLineSel    = pCallSel->pLine;
            pLineAppSel = pCallSel->pLine->pLineApp;

            if (pWidget->pNext && (pWidget->pNext->dwType == WT_CALL))
            {
                pCallSel2 = (PMYCALL) pWidget->pNext;
            }
            else if ((((PMYWIDGET)pLineSel)->pNext != pWidget) &&
                     (((PMYWIDGET)pLineSel)->pNext->dwType == WT_CALL))
            {
                pCallSel2 =  (PMYCALL) (((PMYWIDGET)pLineSel)->pNext);
            }

            break;

        case WT_PHONEAPP:

            pPhoneAppSel = (PMYPHONEAPP) pWidget;
            break;

        case WT_PHONE:

            pPhoneSel    = (PMYPHONE) pWidget;
            pPhoneAppSel = pPhoneSel->pPhoneApp;
            break;
        }
    }


    //
    // The following is an attempt to up the usability level a little bit.
    // Most folks are going to be messing around with 1 lineapp/line/call
    // at a time, and it'd end up being a real PITA for them to have to
    // select a widget each time (and it's fairly obvious which widget
    // are referring to).  So we're going to try to make some intelligent
    // decisions in case they haven't selected a widget... (Obviously this
    // could be cleaned up a bit, like maybe maintaining dwNumXxx as
    // globals rather than walking the list each time.)
    //

    {
        DWORD       dwNumLineApps = 0, dwNumLines = 0, dwNumCalls = 0,
                    dwNumPhoneApps = 0, dwNumPhones = 0;
        PMYPHONEAPP pPhoneApp = NULL;


        pWidget = aWidgets;

        while (pWidget)
        {
            switch (pWidget->dwType)
            {
            case WT_LINEAPP:

                dwNumLineApps++;
                break;

            case WT_LINE:

                dwNumLines++;
                break;

            case WT_CALL:

                dwNumCalls++;
                break;

            case WT_PHONEAPP:

                dwNumPhoneApps++;

                if (dwNumPhoneApps == 1)
                {
                    pPhoneApp = (PMYPHONEAPP) pWidget;
                }

                break;

            case WT_PHONE:

                dwNumPhones++;
                break;
            }

            pWidget = pWidget->pNext;
        }

        if (dwNumLineApps == 1)
        {
            pLineAppSel = (PMYLINEAPP) aWidgets;

            if (dwNumLines == 1)
            {
                pLineSel = (PMYLINE) pLineAppSel->Widget.pNext;

                if (dwNumCalls == 1)
                {
                    pCallSel = (PMYCALL) pLineSel->Widget.pNext;
                }
            }
        }

        if (dwNumPhoneApps == 1)
        {
            pPhoneAppSel = (PMYPHONEAPP) pPhoneApp;

            if (dwNumPhones == 1)
            {
                pPhoneSel = (PMYPHONE) pPhoneAppSel->Widget.pNext;
            }
        }
    }
}

/* help
VOID
MyWinHelp(
    HWND  hwndOwner,
    BOOL  bTapiHlp,
    UINT  uiCommand,
    DWORD dwData
    )
{
    char *lpszHelpFile = (bTapiHlp ? szTapiHlp : szTspiHlp);


    if (lpszHelpFile[0] == 0 || _access (lpszHelpFile, 0))
    {
        //
        // Prompt user for helpfile path
        //

        OPENFILENAME ofn;
        char szDirName[256] = ".\\";
        char szFile[256] = "tapi.hlp\0";
        char szFileTitle[256] = "";
        char szFilter[] = "Telephony API Help\0tapi.hlp\0\0";


        if (MessageBox(
                hwndOwner,
                "Help file not found- do you want to specify a new help file?",
                "Warning",
                MB_YESNO
                ) == IDNO)
        {
            return;
        }

        if (!bTapiHlp)
        {
            szFile[1] = 's';
            szFilter[10] = 'S';
            szFilter[20] = 's';
        }

        ofn.lStructSize       = sizeof(OPENFILENAME);
        ofn.hwndOwner         = hwndOwner;
        ofn.lpstrFilter       = szFilter;
        ofn.lpstrCustomFilter = (LPSTR) NULL;
        ofn.nMaxCustFilter    = 0L;
        ofn.nFilterIndex      = 1;
        ofn.lpstrFile         = szFile;
        ofn.nMaxFile          = sizeof(szFile);
        ofn.lpstrFileTitle    = szFileTitle;
        ofn.nMaxFileTitle     = sizeof(szFileTitle);
        ofn.lpstrInitialDir   = szDirName;
        ofn.lpstrTitle        = (LPSTR) NULL;
        ofn.Flags             = 0L;
        ofn.nFileOffset       = 0;
        ofn.nFileExtension    = 0;
        ofn.lpstrDefExt       = "HLP";

        if (!GetOpenFileName(&ofn))
        {
            return;
        }

        strcpy (lpszHelpFile, szFile);
    }

    if (!WinHelp (ghwndMain, (LPCSTR) lpszHelpFile, uiCommand, dwData))
    {
        lpszHelpFile[0] = 0;
    }
}
*/

BOOL
CALLBACK
MainWndProc(
    HWND    hwnd,
    UINT    msg,
    WPARAM  wParam,
    LPARAM  lParam
    )
{
    static HICON  hIcon;
    static HMENU  hMenu;
    static int    icyButton, icyBorder;
    static HFONT  hFont, hFont2;

    int  i;

    static LONG cxList1, cxList2, cxWnd, xCapture, cxVScroll, lCaptureFlags = 0;
    static int cyWnd;

    typedef struct _XXX
    {
        DWORD   dwMenuID;

        DWORD   dwFlags;

    } XXX, *PXXX;

    static XXX aXxx[] =
    {
        { IDM_LOGSTRUCTDWORD        ,DS_BYTEDUMP },
        { IDM_LOGSTRUCTALLFIELD     ,DS_NONZEROFIELDS|DS_ZEROFIELDS },
        { IDM_LOGSTRUCTNONZEROFIELD ,DS_NONZEROFIELDS },
        { IDM_LOGSTRUCTNONE         ,0 }
    };

    switch (msg)
    {
    case WM_INITDIALOG:
    {
        RECT rect;
        char buf[64];


        ghwndMain  = hwnd;
        ghwndList1 = GetDlgItem (hwnd, IDC_LIST1);
        ghwndList2 = GetDlgItem (hwnd, IDC_LIST2);
        ghwndEdit  = GetDlgItem (hwnd, IDC_EDIT1);
        hMenu      = GetMenu (hwnd);
        hIcon      = LoadIcon (ghInst, MAKEINTRESOURCE(IDI_ICON1));

        icyBorder = GetSystemMetrics (SM_CYFRAME);
        cxVScroll = 2*GetSystemMetrics (SM_CXVSCROLL);

        GetWindowRect (GetDlgItem (hwnd, IDC_BUTTON1), &rect);
        icyButton = (rect.bottom - rect.top) + icyBorder + 3;

        for (i = 0; aFuncNames[i]; i++)
        {
            SendMessage(
                ghwndList2,
                LB_INSERTSTRING,
                (WPARAM) -1,
                (LPARAM) aFuncNames[i]
                );
        }

        SendMessage (ghwndList2, LB_SETCURSEL, (WPARAM) lInitialize, 0);


#ifdef WIN32
        SetWindowText (hwnd, "TAPI32 Browser");
#else
        SetWindowText (hwnd, "TAPI16 Browser");
#endif

        //
        // Read in defaults from ini file
        //

        {
            typedef struct _DEF_VALUE
            {
                char far *lpszEntry;
                char far *lpszDefValue;
                LPVOID   lp;

            } DEF_VALUE;

            DEF_VALUE aDefVals[] =
            {
                { "BufSize",            "1000", &dwBigBufSize },
                { "AddressID",          "0", &dwDefAddressID },
#if TAPI_2_0
                { "20LineAPIVersion",   "20000", &dwDefLineAPIVersion },
#else
#if TAPI_1_1
                { "14LineAPIVersion",   "10004", &dwDefLineAPIVersion },
#else
                { "13LineAPIVersion",   "10003", &dwDefLineAPIVersion },
#endif
#endif
                { "BearerMode",         "1", &dwDefBearerMode },
                { "CountryCode",        "0", &dwDefCountryCode },
                { "LineDeviceID",       "0", &dwDefLineDeviceID },
                { "LineExtVersion",     "0", &dwDefLineExtVersion },
                { "MediaMode",         "10", &dwDefMediaMode },        // DATAMODEM
                { "LinePrivilege",      "1", &dwDefLinePrivilege },    // NONE (dialout only)
#if TAPI_2_0
                { "20PhoneAPIVersion",  "20000", &dwDefPhoneAPIVersion },
#else
#if TAPI_1_1
                { "14PhoneAPIVersion",  "10004", &dwDefPhoneAPIVersion },
#else
                { "13PhoneAPIVersion",  "10003", &dwDefPhoneAPIVersion },
#endif
#endif
                { "PhoneDeviceID",      "0", &dwDefPhoneDeviceID },
                { "PhoneExtVersion",    "0", &dwDefPhoneExtVersion },
                { "PhonePrivilege",     "2", &dwDefPhonePrivilege },   // OWNER
#if TAPI_2_0
                { "20UserButton1",      "500", aUserButtonFuncs },
                { "20UserButton2",      "500", aUserButtonFuncs + 1 },
                { "20UserButton3",      "500", aUserButtonFuncs + 2 },
                { "20UserButton4",      "500", aUserButtonFuncs + 3 },
                { "20UserButton5",      "500", aUserButtonFuncs + 4 },
                { "20UserButton6",      "500", aUserButtonFuncs + 5 },
#else
#if TAPI_1_1
                { "14UserButton1",      "500", aUserButtonFuncs },
                { "14UserButton2",      "500", aUserButtonFuncs + 1 },
                { "14UserButton3",      "500", aUserButtonFuncs + 2 },
                { "14UserButton4",      "500", aUserButtonFuncs + 3 },
                { "14UserButton5",      "500", aUserButtonFuncs + 4 },
                { "14UserButton6",      "500", aUserButtonFuncs + 5 },
#else
                { "13UserButton1",      "500", aUserButtonFuncs },
                { "13UserButton2",      "500", aUserButtonFuncs + 1 },
                { "13UserButton3",      "500", aUserButtonFuncs + 2 },
                { "13UserButton4",      "500", aUserButtonFuncs + 3 },
                { "13UserButton5",      "500", aUserButtonFuncs + 4 },
                { "13UserButton6",      "500", aUserButtonFuncs + 5 },
#endif
#endif
                { "TimeStamp",          "0", &bTimeStamp },
                { "NukeIdleMonitorCalls",   "1", &bNukeIdleMonitorCalls },
                { "NukeIdleOwnedCalls",     "0", &bNukeIdleOwnedCalls },
                { "DisableHandleChecking", "0", &gbDisableHandleChecking },
                { "DumpStructsFlags",   "1",   &dwDumpStructsFlags },
                { NULL, NULL, NULL },
                { "UserUserInfo",       "my user user info", szDefUserUserInfo },
                { "DestAddress",        "55555", szDefDestAddress },
                { "LineDeviceClass",    "tapi/line", szDefLineDeviceClass },
                { "PhoneDeviceClass",   "tapi/phone", szDefPhoneDeviceClass },
                { "AppName",            "Tapi Browser", szDefAppName },
                { NULL, NULL, NULL },
#if TAPI_2_0
                { "20UserButton1Text",  "", &aUserButtonsText[0] },
                { "20UserButton2Text",  "", &aUserButtonsText[1] },
                { "20UserButton3Text",  "", &aUserButtonsText[2] },
                { "20UserButton4Text",  "", &aUserButtonsText[3] },
                { "20UserButton5Text",  "", &aUserButtonsText[4] },
                { "20UserButton6Text",  "", &aUserButtonsText[5] },
#else
#if TAPI_1_1
                { "14UserButton1Text",  "", &aUserButtonsText[0] },
                { "14UserButton2Text",  "", &aUserButtonsText[1] },
                { "14UserButton3Text",  "", &aUserButtonsText[2] },
                { "14UserButton4Text",  "", &aUserButtonsText[3] },
                { "14UserButton5Text",  "", &aUserButtonsText[4] },
                { "14UserButton6Text",  "", &aUserButtonsText[5] },
#else
                { "13UserButton1Text",  "", &aUserButtonsText[0] },
                { "13UserButton2Text",  "", &aUserButtonsText[1] },
                { "13UserButton3Text",  "", &aUserButtonsText[2] },
                { "13UserButton4Text",  "", &aUserButtonsText[3] },
                { "13UserButton5Text",  "", &aUserButtonsText[4] },
                { "13UserButton6Text",  "", &aUserButtonsText[5] },
#endif
#endif
                { NULL, NULL, NULL }
            };

            int i, j;

            #define MYSECTION "Tapi Browser"


            for (i = 0; aDefVals[i].lpszEntry; i++)
            {
                GetProfileString(
                    MYSECTION,
                    aDefVals[i].lpszEntry,
                    aDefVals[i].lpszDefValue,
                    buf,
                    15
                    );

                sscanf (buf, "%lx", aDefVals[i].lp);
            }

            i++;

            for (; aDefVals[i].lpszEntry; i++)
            {
                GetProfileString(
                    MYSECTION,
                    aDefVals[i].lpszEntry,
                    aDefVals[i].lpszDefValue,
                    (LPSTR) aDefVals[i].lp,
                    MAX_STRING_PARAM_SIZE - 1
                    );
            }

            i++;

            for (j = i; aDefVals[i].lpszEntry; i++)
            {
                GetProfileString(
                    MYSECTION,
                    aDefVals[i].lpszEntry,
                    aDefVals[i].lpszDefValue,
                    (LPSTR) aDefVals[i].lp,
                    MAX_USER_BUTTON_TEXT_SIZE - 1
                    );

                SetDlgItemText(
                    hwnd,
                    IDC_BUTTON13 + (i - j),
                    (LPCSTR)aDefVals[i].lp
                    );
            }

            lpszDefAppName          = szDefAppName;
            lpszDefUserUserInfo     = szDefUserUserInfo;
            lpszDefDestAddress      = szDefDestAddress;
            lpszDefLineDeviceClass  = szDefLineDeviceClass;
            lpszDefPhoneDeviceClass = szDefPhoneDeviceClass;

            if (GetProfileString (
                    MYSECTION,
                    "Version",
                    "",
                    buf,
                    15
                    ) == 0 || (strcmp (buf, szCurrVer)))
            {
                //
                // If here assume this is first time user had started
                // TB, so post a msg that will automatically bring up
                // the hlp dlg
                //

                PostMessage (hwnd, WM_COMMAND, IDM_USINGTB, 0);
            }

// help            GetProfileString (MYSECTION, "TapiHlpPath", "", szTapiHlp, 256);
// help            GetProfileString (MYSECTION, "TspiHlpPath", "", szTspiHlp, 256);
        }

        pBigBuf = malloc ((size_t)dwBigBufSize);

        {
            //HFONT hFontMenu = SendMessage (hMenu, WM_GETFONT, 0, 0);

            hFont = CreateFont(
                13, 5, 0, 0, 400, 0, 0, 0, 0, 1, 2, 1, 34, "MS Sans Serif"
                );
            hFont2 = CreateFont(
                13, 8, 0, 0, 400, 0, 0, 0, 0, 1, 2, 1, 49, "Courier"
                );

            for (i = 0; i < 18; i++)
            {
                SendDlgItemMessage(
                    hwnd,
                    IDC_BUTTON1 + i,
                    WM_SETFONT,
                    (WPARAM) hFont,
                    0
                    );
            }

            SendMessage (ghwndList1, WM_SETFONT, (WPARAM) hFont, 0);
            SendMessage (ghwndList2, WM_SETFONT, (WPARAM) hFont, 0);
            SendMessage (ghwndEdit, WM_SETFONT, (WPARAM) hFont2, 0);
        }

        GetProfileString(
            MYSECTION,
            "ControlRatios",
            "20, 20, 100",
            buf,
            63
            );

        sscanf (buf, "%ld,%ld,%ld", &cxList2, &cxList1, &cxWnd);

        GetProfileString(
            MYSECTION,
            "Position",
            "max",
            buf,
            63
            );

        if (strcmp (buf, "max") == 0)
        {
            ShowWindow (hwnd, SW_SHOWMAXIMIZED);
        }
        else
        {
            int left = 100, top = 100, right = 600, bottom = 400;


            sscanf (buf, "%d,%d,%d,%d", &left, &top, &right, &bottom);


            //
            // Check to see if wnd pos is wacky, if so reset to reasonable vals
            //

            if (left < 0 ||
                left >= (GetSystemMetrics (SM_CXSCREEN) - 32) ||
                top < 0 ||
                top >= (GetSystemMetrics (SM_CYSCREEN) - 32))
            {
                left = top = 100;
                right = 600;
                bottom = 400;
            }

            SetWindowPos(
                hwnd,
                HWND_TOP,
                left,
                top,
                right - left,
                bottom - top,
                SWP_SHOWWINDOW
                );

            GetClientRect (hwnd, &rect);

            SendMessage(
                hwnd,
                WM_SIZE,
                0,
                MAKELONG((rect.right-rect.left),(rect.bottom-rect.top))
                );

            ShowWindow (hwnd, SW_SHOW);
        }

        for (i = 0; aXxx[i].dwFlags != dwDumpStructsFlags; i++);

        CheckMenuItem (hMenu, aXxx[i].dwMenuID, MF_BYCOMMAND | MF_CHECKED);

        CheckMenuItem(
            hMenu,
            IDM_TIMESTAMP,
            MF_BYCOMMAND | (bTimeStamp ? MF_CHECKED : MF_UNCHECKED)
            );

        CheckMenuItem(
            hMenu,
            IDM_NUKEIDLEMONITORCALLS,
            MF_BYCOMMAND | (bNukeIdleMonitorCalls ? MF_CHECKED : MF_UNCHECKED)
            );

        CheckMenuItem(
            hMenu,
            IDM_NUKEIDLEOWNEDCALLS,
            MF_BYCOMMAND | (bNukeIdleOwnedCalls ? MF_CHECKED : MF_UNCHECKED)
            );

        CheckMenuItem(
            hMenu,
            IDM_NOHANDLECHK,
            MF_BYCOMMAND | (gbDisableHandleChecking ? MF_CHECKED : MF_UNCHECKED)
            );

        //
        // Alloc & init the global call params
        //

#if TAPI_2_0

        lpCallParamsW = NULL;

init_call_params:

#endif
        {
#if TAPI_2_0
            size_t callParamsSize =
                sizeof(LINECALLPARAMS) + 15 * MAX_STRING_PARAM_SIZE;
#else
            size_t callParamsSize =
                sizeof(LINECALLPARAMS) + 8 * MAX_STRING_PARAM_SIZE;
#endif

            if ((lpCallParams = (LPLINECALLPARAMS) malloc (callParamsSize)))
            {
                LPDWORD lpdwXxxOffset = &lpCallParams->dwOrigAddressOffset;


                memset (lpCallParams, 0, callParamsSize);

                lpCallParams->dwTotalSize   = (DWORD) callParamsSize;
                lpCallParams->dwBearerMode  = LINEBEARERMODE_VOICE;
                lpCallParams->dwMinRate     = 3100;
                lpCallParams->dwMaxRate     = 3100;
                lpCallParams->dwMediaMode   = LINEMEDIAMODE_INTERACTIVEVOICE;
                lpCallParams->dwAddressMode = LINEADDRESSMODE_ADDRESSID;

                for (i = 0; i < 8; i++)
                {
                    *(lpdwXxxOffset + 2*i) =
                        sizeof(LINECALLPARAMS) + i*MAX_STRING_PARAM_SIZE;
                }
#if TAPI_2_0
                {
                    LPDWORD pdwProxyRequests = (LPDWORD)
                                (((LPBYTE) lpCallParams) +
                                lpCallParams->dwDevSpecificOffset);


                    for (i = 0; i < 8; i++)
                    {
                        *(pdwProxyRequests + i) = i +
                            LINEPROXYREQUEST_SETAGENTGROUP;
                    }
                }

                lpdwXxxOffset = &lpCallParams->dwTargetAddressOffset;

                for (i = 0; i < 6; i++)
                {
                    *(lpdwXxxOffset + 2 * i) =
                        sizeof(LINECALLPARAMS) + (8+i)*MAX_STRING_PARAM_SIZE;
                }

                lpCallParams->dwCallingPartyIDOffset    =
                    sizeof(LINECALLPARAMS) + 14 * MAX_STRING_PARAM_SIZE;

                if (!lpCallParamsW)
                {
                    lpCallParamsW = lpCallParams;
                    goto init_call_params;
                }
#endif
            }
            else
            {
                // BUGBUG
            }
        }

        break;
    }
    case WM_COMMAND:
    {
        FUNC_INDEX funcIndex;
        BOOL bShowParamsSave = bShowParams;

        switch (LOWORD((DWORD)wParam))
        {
        case IDC_EDIT1:

#ifdef WIN32
            if (HIWORD(wParam) == EN_CHANGE)
#else
            if (HIWORD(lParam) == EN_CHANGE)
#endif
            {
                //
                // Watch to see if the edit control is full, & if so
                // purge the top half of the text to make room for more
                //

                int length = GetWindowTextLength (ghwndEdit);


                if (length > 29998)
                {
#ifdef WIN32
                    SendMessage(
                        ghwndEdit,
                        EM_SETSEL,
                        (WPARAM) 0,
                        (LPARAM) 10000
                        );
#else
                    SendMessage(
                        ghwndEdit,
                        EM_SETSEL,
                        (WPARAM) 1,
                        (LPARAM) MAKELONG (0, 10000)
                        );
#endif

                    SendMessage(
                        ghwndEdit,
                        EM_REPLACESEL,
                        0,
                        (LPARAM) (char far *) ""
                        );

#ifdef WIN32
                    SendMessage(
                        ghwndEdit,
                        EM_SETSEL,
                        (WPARAM)0xfffffffd,
                        (LPARAM)0xfffffffe
                        );
#else
                    SendMessage(
                        ghwndEdit,
                        EM_SETSEL,
                        (WPARAM)1,
                        (LPARAM) MAKELONG (0xfffd, 0xfffe)
                        );
#endif
                }
            }
            break;

        case IDC_BUTTON1:

            funcIndex = lInitialize;
            goto IDC_BUTTON10_callFuncDriver;

        case IDC_BUTTON2:

            funcIndex = lShutdown;
            goto IDC_BUTTON10_callFuncDriver;

        case IDC_BUTTON3:

            funcIndex = lOpen;
            goto IDC_BUTTON10_callFuncDriver;

        case IDC_BUTTON4:

            funcIndex = lClose;
            goto IDC_BUTTON10_callFuncDriver;

        case IDC_BUTTON5:

            funcIndex = lMakeCall;
            goto IDC_BUTTON10_callFuncDriver;

        case IDC_BUTTON6:

            GetCurrentSelections();

            if (pCallSel && (pCallSel->dwCallState == LINECALLSTATE_IDLE))
            {
                funcIndex = lDeallocateCall;
            }
            else
            {
                funcIndex = lDrop;
                gbDeallocateCall = TRUE;
            }

            FuncDriver (funcIndex);
            gbDeallocateCall = FALSE;
            break;

        case IDC_BUTTON7:

            funcIndex = pInitialize;
            goto IDC_BUTTON10_callFuncDriver;

        case IDC_BUTTON8:

            funcIndex = pShutdown;
            goto IDC_BUTTON10_callFuncDriver;

        case IDC_BUTTON9:

            funcIndex = pOpen;
            goto IDC_BUTTON10_callFuncDriver;

        case IDC_BUTTON10:

            funcIndex = pClose;

IDC_BUTTON10_callFuncDriver:

            //
            // Want to make the "hot buttons" as simple as possible, so
            // turn off the show params flag, then call FuncDriver, and
            // finally restore the flag
            //

            GetCurrentSelections ();
            FuncDriver (funcIndex);
            break;

        case IDC_BUTTON11:
        case IDM_CLEAR:

            SetWindowText (ghwndEdit, "");
            break;


        case IDM_PARAMS:
        case IDC_BUTTON12:

            bShowParams = (bShowParams ? FALSE : TRUE);

            if (bShowParams)
            {
                CheckMenuItem(
                    hMenu,
                    IDM_PARAMS,
                    MF_BYCOMMAND | MF_CHECKED
                    );

                CheckDlgButton (hwnd, IDC_BUTTON12, 1);
            }
            else
            {
                CheckMenuItem(
                    hMenu,
                    IDM_PARAMS,
                    MF_BYCOMMAND | MF_UNCHECKED
                    );

                CheckDlgButton (hwnd, IDC_BUTTON12, 0);
            }

            break;

        case IDC_BUTTON13:
        case IDC_BUTTON14:
        case IDC_BUTTON15:
        case IDC_BUTTON16:
        case IDC_BUTTON17:
        case IDC_BUTTON18:
        {
            DWORD i = (DWORD) (LOWORD((DWORD)wParam)) - IDC_BUTTON13;


            if (aUserButtonFuncs[i] >= MiscBegin)
            {
                //
                // Hot button func id is bogus, so bring
                // up hot button init dlg
                //

                DialogBoxParam(
                    ghInst,
                    (LPCSTR)MAKEINTRESOURCE(IDD_DIALOG3),
                    (HWND) hwnd,
                    (DLGPROC) UserButtonsDlgProc,
                    (LPARAM) &i
                    );
            }
            else
            {
                //
                // Invoke the user button's corresponding func
                //

                GetCurrentSelections ();
                FuncDriver ((FUNC_INDEX) aUserButtonFuncs[i]);
            }

            break;
        }
/* help        case IDC_F1HELP:

            if ((GetFocus() == ghwndEdit) &&
                SendMessage (ghwndEdit, EM_GETSEL, 0, 0))
            {
                //
                // Display help for the selected text in the edit ctrl
                //

                char buf[32];


                //
                // Copy the selected text in the edit ctrl to our
                // temp edit control, then query the temp edit ctrl's
                // text (this seems to be the easiest way to get the
                // selected text)
                //

                SendMessage (ghwndEdit, WM_COPY, 0, 0);
                SetWindowText (hwndEdit2, "");
                SendMessage (hwndEdit2, WM_PASTE, 0, 0);


                //
                // In the interest of getting an exact match on a
                // helpfile key strip off any trailing spaces
                //

                GetWindowText (hwndEdit2, buf, 32);
                buf[31] = 0;
                for (i = 0; i < 32; i++)
                {
                    if (buf[i] == ' ')
                    {
                        buf[i] = 0;
                        break;
                    }
                }

                MyWinHelp (hwnd, TRUE, HELP_PARTIALKEY, (DWORD) buf);
            }
            else
            {
                //
                // Display help for the currently selected func
                //

                FUNC_INDEX funcIndex = (FUNC_INDEX)
                    SendMessage (ghwndList2, LB_GETCURSEL, 0, 0);


                MyWinHelp (hwnd, TRUE, HELP_PARTIALKEY, (DWORD) aFuncNames[funcIndex]);
            }

            break;
*/
        case IDC_PREVCTRL:
        {
            HWND hwndPrev = GetNextWindow (GetFocus (), GW_HWNDPREV);

            if (!hwndPrev)
            {
                hwndPrev = ghwndList2;
            }

            SetFocus (hwndPrev);
            break;
        }
        case IDC_NEXTCTRL:
        {
            HWND hwndNext = GetNextWindow (GetFocus (), GW_HWNDNEXT);

            if (!hwndNext)
            {
                hwndNext = GetDlgItem (hwnd, IDC_BUTTON12);
            }

            SetFocus (hwndNext);
            break;
        }
        case IDC_ENTER:
        {
            if (GetFocus() != ghwndEdit)
            {
                GetCurrentSelections ();
                FuncDriver(
                    (FUNC_INDEX)SendMessage(
                        ghwndList2,
                        LB_GETCURSEL,
                        0,
                        0
                        ));
            }
            else
            {
                // Send the edit ctrl a cr/lf
            }

            break;
        }
        case IDC_LIST1:

#ifdef WIN32
            switch (HIWORD(wParam))
#else
            switch (HIWORD(lParam))
#endif
            {
            case LBN_DBLCLK:
            {
                LONG lSel = SendMessage (ghwndList1, LB_GETCURSEL, 0, 0);
                PMYWIDGET pWidget;


                pWidget = (PMYWIDGET) SendMessage(
                    ghwndList1,
                    LB_GETITEMDATA,
                    (WPARAM) lSel,
                    0
                    );

                if ((pWidget->dwType == WT_LINEAPP)
                    || (pWidget->dwType == WT_PHONEAPP)
                    )
                {
                    break;
                }

                bShowParams = FALSE;

                UpdateResults (TRUE);

                switch (pWidget->dwType)
                {
                case WT_LINE:
                {
                    DWORD dwDefLineDeviceIDSave = dwDefLineDeviceID;
                    PMYLINE pLine = (PMYLINE) pWidget;


                    dwDefLineDeviceID = pLine->dwDevID;

                    pLineAppSel = GetLineApp (pLine->hLineApp);
                    pLineSel = pLine;

                    FuncDriver (lGetDevCaps);
                    FuncDriver (lGetLineDevStatus);

                    dwDefLineDeviceID = dwDefLineDeviceIDSave;
                    break;
                }
                case WT_CALL:
                {
                    PMYCALL pCall = (PMYCALL) pWidget;


                    pCallSel = pCall;

                    FuncDriver (lGetCallInfo);
                    FuncDriver (lGetCallStatus);

                    break;
                }
                case WT_PHONE:
                {
                    DWORD dwDefPhoneDeviceIDSave = dwDefPhoneDeviceID;
                    PMYPHONE pPhone = (PMYPHONE) pWidget;


                    dwDefPhoneDeviceID = pPhone->dwDevID;

                    pPhoneAppSel = GetPhoneApp (pPhone->hPhoneApp);
                    pPhoneSel = pPhone;

                    FuncDriver (pGetDevCaps);
                    FuncDriver (pGetStatus);

                    dwDefPhoneDeviceID = dwDefPhoneDeviceIDSave;
                    break;
                }
                }

                UpdateResults (FALSE);

                bShowParams = bShowParamsSave;

                break;
            }
            } // switch

            break;

        case IDC_LIST2:

#ifdef WIN32
            if (HIWORD(wParam) == LBN_DBLCLK)
#else
            if (HIWORD(lParam) == LBN_DBLCLK)
#endif
            {
                GetCurrentSelections ();
                FuncDriver(
                    (FUNC_INDEX)SendMessage(
                        ghwndList2,
                        LB_GETCURSEL,
                        0,
                        0
                        ));
            }

            break;

        case IDM_EXIT:
        {
            PostMessage (hwnd, WM_CLOSE, 0, 0);
            break;
        }
        case IDM_DEFAULTVALUES:
        {
            char szTmpAppName[MAX_STRING_PARAM_SIZE];
            char szTmpUserUserInfo[MAX_STRING_PARAM_SIZE];
            char szTmpDestAddress[MAX_STRING_PARAM_SIZE];
            char szTmpLineDeviceClass[MAX_STRING_PARAM_SIZE];
            char szTmpPhoneDeviceClass[MAX_STRING_PARAM_SIZE];
            FUNC_PARAM params[] =
            {
                { "Buffer size",            PT_DWORD,  dwBigBufSize, NULL },
                { "lpszAppName",            PT_STRING, (DWORD) lpszDefAppName, szTmpAppName },

                { "line: dwAddressID",      PT_DWORD,  dwDefAddressID, NULL },
                { "line: dwAPIVersion",     PT_DWORD,  dwDefLineAPIVersion, NULL },
                { "line: dwBearerMode",     PT_FLAGS,  dwDefBearerMode, aBearerModes },
                { "line: dwCountryCode",    PT_DWORD,  dwDefCountryCode, NULL },
                { "line: dwDeviceID",       PT_DWORD,  dwDefLineDeviceID, NULL },
                { "line: dwExtVersion",     PT_DWORD,  dwDefLineExtVersion, NULL },
                { "line: dwMediaMode",      PT_FLAGS,  dwDefMediaMode, aMediaModes },
                { "line: dwPrivileges",     PT_FLAGS,  dwDefLinePrivilege, aLineOpenOptions },
                { "line: lpsUserUserInfo",  PT_STRING, (DWORD) lpszDefUserUserInfo, szTmpUserUserInfo },
                { "line: lpszDestAddress",  PT_STRING, (DWORD) lpszDefDestAddress, szTmpDestAddress },
                { "line: lpszDeviceClass",  PT_STRING, (DWORD) lpszDefLineDeviceClass, szTmpLineDeviceClass },
                { "phone: dwAPIVersion",    PT_DWORD,  dwDefPhoneAPIVersion, NULL },
                { "phone: dwDeviceID",      PT_DWORD,  dwDefPhoneDeviceID, NULL },
                { "phone: dwExtVersion",    PT_DWORD,  dwDefPhoneExtVersion, NULL },
                { "phone: dwPrivilege",     PT_FLAGS,  dwDefPhonePrivilege, aPhonePrivileges },
                { "phone: lpszDeviceClass", PT_STRING, (DWORD) lpszDefPhoneDeviceClass, szTmpPhoneDeviceClass }
            };
            FUNC_PARAM_HEADER paramsHeader =
                { 18, DefValues, params, NULL };
            BOOL bShowParamsSave = bShowParams;

            bShowParams = TRUE;

            strcpy (szTmpAppName, szDefAppName);
            strcpy (szTmpUserUserInfo, szDefUserUserInfo);
            strcpy (szTmpDestAddress, szDefDestAddress);
            strcpy (szTmpLineDeviceClass, szDefLineDeviceClass);
            strcpy (szTmpPhoneDeviceClass, szDefPhoneDeviceClass);

            if (LetUserMungeParams (&paramsHeader))
            {
                if (params[0].dwValue != dwBigBufSize)
                {
                    LPVOID pTmpBigBuf = malloc ((size_t) params[0].dwValue);

                    if (pTmpBigBuf)
                    {
                        free (pBigBuf);
                        pBigBuf = pTmpBigBuf;
                        dwBigBufSize = params[0].dwValue;
                    }
                }

                strcpy (szDefAppName, szTmpAppName);

                dwDefAddressID      = params[2].dwValue;
                dwDefLineAPIVersion = params[3].dwValue;
                dwDefBearerMode     = params[4].dwValue;
                dwDefCountryCode    = params[5].dwValue;
                dwDefLineDeviceID   = params[6].dwValue;
                dwDefLineExtVersion = params[7].dwValue;
                dwDefMediaMode      = params[8].dwValue;
                dwDefLinePrivilege  = params[9].dwValue;

                strcpy (szDefUserUserInfo, szTmpUserUserInfo);
                strcpy (szDefDestAddress, szTmpDestAddress);
                strcpy (szDefLineDeviceClass, szTmpLineDeviceClass);

                dwDefPhoneAPIVersion = params[13].dwValue;
                dwDefPhoneDeviceID   = params[14].dwValue;
                dwDefPhoneExtVersion = params[15].dwValue;
                dwDefPhonePrivilege  = params[16].dwValue;

                strcpy (szDefPhoneDeviceClass, szTmpPhoneDeviceClass);

                if (params[1].dwValue && (params[1].dwValue != 0xffffffff))
                {
                    lpszDefAppName = szDefAppName;
                }
                else
                {
                    lpszDefAppName = (char far *) params[1].dwValue;
                }

                if (params[10].dwValue && (params[10].dwValue != 0xffffffff))
                {
                    lpszDefUserUserInfo = szDefUserUserInfo;
                }
                else
                {
                    lpszDefUserUserInfo = (char far *) params[10].dwValue;
                }

                if (params[11].dwValue && (params[11].dwValue != 0xffffffff))
                {
                    lpszDefDestAddress = szDefDestAddress;
                }
                else
                {
                    lpszDefDestAddress = (char far *) params[11].dwValue;
                }

                if (params[12].dwValue && (params[12].dwValue != 0xffffffff))
                {
                    lpszDefLineDeviceClass  = szDefLineDeviceClass;
                }
                else
                {
                    lpszDefLineDeviceClass  = (char far *) params[12].dwValue;
                }

                if (params[17].dwValue && (params[17].dwValue != 0xffffffff))
                {
                    lpszDefPhoneDeviceClass = szDefPhoneDeviceClass;
                }
                else
                {
                    lpszDefPhoneDeviceClass = (char far *) params[17].dwValue;
                }
            }

            bShowParams = bShowParamsSave;

            break;
        }
        case IDM_USERBUTTONS:

            DialogBoxParam(
                ghInst,
                (LPCSTR)MAKEINTRESOURCE(IDD_DIALOG3),
                (HWND) hwnd,
                (DLGPROC) UserButtonsDlgProc,
                (LPARAM) NULL
                );

            break;

        case IDM_DUMPPARAMS:

            bDumpParams = (bDumpParams ? FALSE : TRUE);

            CheckMenuItem(
                hMenu,
                IDM_DUMPPARAMS,
                MF_BYCOMMAND | (bDumpParams ? MF_CHECKED : MF_UNCHECKED)
                );

            break;

        case IDM_LOGSTRUCTDWORD:
        case IDM_LOGSTRUCTALLFIELD:
        case IDM_LOGSTRUCTNONZEROFIELD:
        case IDM_LOGSTRUCTNONE:

            for (i = 0; aXxx[i].dwFlags != dwDumpStructsFlags; i++);

            CheckMenuItem(
                hMenu,
                aXxx[i].dwMenuID,
                MF_BYCOMMAND | MF_UNCHECKED
                );

            for (i = 0; aXxx[i].dwMenuID != LOWORD((DWORD)wParam); i++);

            CheckMenuItem(
                hMenu,
                aXxx[i].dwMenuID,
                MF_BYCOMMAND | MF_CHECKED
                );

            dwDumpStructsFlags = aXxx[i].dwFlags;

            break;

        case IDM_TIMESTAMP:

            bTimeStamp = (bTimeStamp ? FALSE : TRUE);

            CheckMenuItem(
                hMenu,
                IDM_TIMESTAMP,
                MF_BYCOMMAND | (bTimeStamp ? MF_CHECKED : MF_UNCHECKED)
                );

            break;

        case IDM_LOGFILE:
        {
            if (hLogFile)
            {
                fclose (hLogFile);
                hLogFile = (FILE *) NULL;
                CheckMenuItem(
                    hMenu,
                    IDM_LOGFILE,
                    MF_BYCOMMAND | MF_UNCHECKED
                    );
            }
            else
            {
                OPENFILENAME ofn;
                char szDirName[256] = ".\\";
                char szFile[256] = "tb.log\0";
                char szFileTitle[256] = "";
                static char *szFilter =
                    "Log files (*.log)\0*.log\0All files (*.*)\0*.*\0\0";


                ofn.lStructSize       = sizeof(OPENFILENAME);
                ofn.hwndOwner         = hwnd;
                ofn.lpstrFilter       = szFilter;
                ofn.lpstrCustomFilter = (LPSTR) NULL;
                ofn.nMaxCustFilter    = 0L;
                ofn.nFilterIndex      = 1;
                ofn.lpstrFile         = szFile;
                ofn.nMaxFile          = sizeof(szFile);
                ofn.lpstrFileTitle    = szFileTitle;
                ofn.nMaxFileTitle     = sizeof(szFileTitle);
                ofn.lpstrInitialDir   = szDirName;
                ofn.lpstrTitle        = (LPSTR) NULL;
                ofn.Flags             = 0L;
                ofn.nFileOffset       = 0;
                ofn.nFileExtension    = 0;
                ofn.lpstrDefExt       = "LOG";

                if (!GetOpenFileName(&ofn))
                {
                    return 0L;
                }

                if ((hLogFile = fopen (szFile, "at")) == (FILE *) NULL)
                {
                    MessageBox(
                        hwnd,
                        "Error creating log file",
#ifdef WIN32
                        "TB32.EXE",
#else
                        "TB.EXE",
#endif
                        MB_OK
                        );
                }
                else
                {
                    struct tm *newtime;
                    time_t aclock;


                    time (&aclock);
                    newtime = localtime (&aclock);
                    fprintf(
                        hLogFile,
                        "\n---Log opened: %s\n",
                        asctime (newtime)
                        );

                    CheckMenuItem(
                        hMenu,
                        IDM_LOGFILE,
                        MF_BYCOMMAND | MF_CHECKED
                        );
                }
            }
            break;
        }
        case IDM_USINGTB:
        {
            static char szUsingTB[] =

                "ABSTRACT:\r\n"                                         \
                "    TB (TAPI Browser) is an application that\r\n"      \
                "allows a user to interactively call into the\r\n"      \
                "Windows Telephony interface and inspect all\r\n"       \
                "returned information. The following versions\r\n"      \
                "of TB are available:\r\n\r\n"                          \
                "    TB13.EXE: 16-bit app, TAPI v1.0\r\n"               \
                "    TB14.EXE: 32-bit app, <= TAPI v1.4\r\n"            \
                "    TB20.EXE: 32-bit app, <= TAPI v2.0\r\n"            \

                "\r\n"                                                  \
                "GETTING STARTED:\r\n"                                  \
                "1. Press the 'LAp+' button to initialize TAPI \r\n"    \
                "2. Press the 'Line+' button to open a line device\r\n" \
                "3. Press the 'Call+' button to make a call\r\n"        \
                "*  Pressing the 'LAp-' button will shutdown TAPI\r\n"  \

                "\r\n"                                                  \
                "MORE INFO:\r\n"                                        \
                "*  Double-clicking on one of the items in the\r\n"     \
                "functions listbox (far left) will invoke that \r\n"    \
                "function. Check the 'Params' checkbox to enable\r\n"   \
                "parameter modification.\r\n"                           \
                "*  Choose 'Options/Default values...' to modify\r\n"   \
                "default parameter values (address,device ID, etc).\r\n"\
                "*  Choose 'Options/Record log file' to save all\r\n"   \
                "output to a file.\r\n"                                 \
                "*  All parameter values in hexadecimal unless\r\n"     \
                "specified (strings displayed by contents, not \r\n"    \
                "pointer value).\r\n"                                   \
                "*  All 'Xxx+' and 'Xxx-' buttons are essentially \r\n" \
                "hot-links to items in the functions listbox.\r\n"      \
                "*  Choose 'Options/User buttons...' or press\r\n"      \
                "one of the buttons on right side of toolbar to\r\n"    \
                "create a personal hot-link between a button and a\r\n" \
                "particular function.\r\n"                              \

                "\r\n"                                                  \
                "  *  Note: Selecting a API version parameter value\r\n"\
                "which is newer than that for which TB application\r\n" \
                "is targeted will result in erroneous dumps of some\r\n"\
                "structures (e.g. specifying an API version of\r\n"     \
                "0x10004 and calling lineGetTranslateCaps in TB13).\r\n";

            DialogBoxParam(
                ghInst,
                (LPCSTR)MAKEINTRESOURCE(IDD_DIALOG6),
                (HWND)hwnd,
                (DLGPROC) AboutDlgProc,
                (LPARAM) szUsingTB
                );

            break;
        }
// help        case IDM_TAPIHLP:
// help
// help            MyWinHelp (hwnd, TRUE, HELP_CONTENTS, 0);
// help            break;

// help        case IDM_TSPIHLP:
// help
// help            MyWinHelp (hwnd, FALSE, HELP_CONTENTS, 0);
// help            break;

        case IDM_NUKEIDLEMONITORCALLS:

            bNukeIdleMonitorCalls = (bNukeIdleMonitorCalls ? 0 : 1);

            CheckMenuItem(
                hMenu,
                IDM_NUKEIDLEMONITORCALLS,
                MF_BYCOMMAND |
                    (bNukeIdleMonitorCalls ? MF_CHECKED : MF_UNCHECKED)
                );

            break;

        case IDM_NUKEIDLEOWNEDCALLS:

            bNukeIdleOwnedCalls = (bNukeIdleOwnedCalls ? 0 : 1);

            CheckMenuItem(
                hMenu,
                IDM_NUKEIDLEOWNEDCALLS,
                MF_BYCOMMAND |
                    (bNukeIdleOwnedCalls ? MF_CHECKED : MF_UNCHECKED)
                );

            break;

        case IDM_NOHANDLECHK:

            gbDisableHandleChecking = (gbDisableHandleChecking ? 0 : 1);

            CheckMenuItem(
                hMenu,
                IDM_NOHANDLECHK,
                MF_BYCOMMAND |
                    (gbDisableHandleChecking ? MF_CHECKED : MF_UNCHECKED)
                );

            break;

        case IDM_ABOUT:
        {
            DialogBoxParam(
                ghInst,
                (LPCSTR)MAKEINTRESOURCE(IDD_DIALOG4),
                (HWND)hwnd,
                (DLGPROC) AboutDlgProc,
                0
                );

            break;
        }
        } // switch

        break;
    }
#ifdef WIN32
    case WM_CTLCOLORBTN:

        SetBkColor ((HDC) wParam, RGB (192,192,192));
        return (BOOL) GetStockObject (LTGRAY_BRUSH);
#else
    case WM_CTLCOLOR:
    {
        if (HIWORD(lParam) == CTLCOLOR_BTN)
        {
            SetBkColor ((HDC) wParam, RGB (192,192,192));
            return (BOOL) GetStockObject (LTGRAY_BRUSH);
        }
        break;
    }
#endif

    case WM_MOUSEMOVE:
    {
        LONG x = (LONG)((short)LOWORD(lParam));
        int y = (int)((short)HIWORD(lParam));


        if ((y > icyButton) || lCaptureFlags)
        {
            SetCursor (LoadCursor (NULL, MAKEINTRESOURCE(IDC_SIZEWE)));
        }

        if (lCaptureFlags == 1)
        {
            int cxList2New;


            x = (x > (cxList1 + cxList2 - cxVScroll) ?
                    (cxList1 + cxList2 - cxVScroll) : x);
            x = (x < cxVScroll ? cxVScroll : x);

            cxList2New = (int) (cxList2 + x - xCapture);

            SetWindowPos(
                ghwndList2,
                GetNextWindow (ghwndList2, GW_HWNDPREV),
                0,
                icyButton,
                cxList2New,
                cyWnd,
                SWP_SHOWWINDOW
                );

            SetWindowPos(
                ghwndList1,
                GetNextWindow (ghwndList1, GW_HWNDPREV),
                cxList2New + icyBorder,
                icyButton,
                (int) (cxList1 - (x - xCapture)),
                cyWnd,
                SWP_SHOWWINDOW
                );
        }
        else if (lCaptureFlags == 2)
        {
            int cxList1New;


            x = (x < (cxList2 + cxVScroll) ?  (cxList2 + cxVScroll) : x);
            x = (x > (cxWnd - cxVScroll) ?  (cxWnd - cxVScroll) : x);

            cxList1New = (int) (cxList1 + x - xCapture);

            SetWindowPos(
                ghwndList1,
                GetNextWindow (ghwndList1, GW_HWNDPREV),
                (int) cxList2 + icyBorder,
                icyButton,
                cxList1New,
                cyWnd,
                SWP_SHOWWINDOW
                );

            SetWindowPos(
                ghwndEdit,
                GetNextWindow (ghwndEdit, GW_HWNDPREV),
                (int) (cxList1New + cxList2) + 2*icyBorder,
                icyButton,
                (int)cxWnd - (cxList1New + (int)cxList2 + 2*icyBorder),
                cyWnd,
                SWP_SHOWWINDOW
                );
        }

        break;
    }
    case WM_LBUTTONDOWN:
    {
        if ((int)((short)HIWORD(lParam)) > icyButton)
        {
            xCapture = (LONG)LOWORD(lParam);

            SetCapture (hwnd);

            lCaptureFlags = ((xCapture < cxList1 + cxList2) ? 1 : 2);
        }

        break;
    }
    case WM_LBUTTONUP:
    {
        if (lCaptureFlags)
        {
            POINT p;
            LONG  x;
            RECT  rect = { 0, icyButton, 2000, 2000 };

            GetCursorPos (&p);
            MapWindowPoints (HWND_DESKTOP, hwnd, &p, 1);
            x = (LONG) p.x;

            ReleaseCapture();

            if (lCaptureFlags == 1)
            {
                x = (x < cxVScroll ? cxVScroll : x);
                x = (x > (cxList1 + cxList2 - cxVScroll) ?
                    (cxList1 + cxList2 - cxVScroll) : x);

                cxList2 = cxList2 + (x - xCapture);
                cxList1 = cxList1 - (x - xCapture);

                rect.right = (int) (cxList1 + cxList2) + icyBorder;
            }
            else
            {
                x = (x < (cxList2 + cxVScroll) ?
                    (cxList2 + cxVScroll) : x);
                x = (x > (cxWnd - cxVScroll) ?
                    (cxWnd - cxVScroll) : x);

                cxList1 = cxList1 + (x - xCapture);

                rect.left = (int)cxList2 + icyBorder;
            }

            lCaptureFlags = 0;

            InvalidateRect (hwnd, &rect, TRUE);
        }

        break;
    }
    case WM_SIZE:
    {
        if (wParam != SIZE_MINIMIZED)
        {
            LONG width = (LONG)LOWORD(lParam);


            //
            // Adjust globals based on new size
            //

            cxWnd = (cxWnd ? cxWnd : 1); // avoid div by 0

            cxList1 = (cxList1 * width) / cxWnd;
            cxList2 = (cxList2 * width) / cxWnd;
            cxWnd = width;
            cyWnd = ((int)HIWORD(lParam)) - icyButton;


            //
            // Now reposition the child windows
            //

            SetWindowPos(
                ghwndList2,
                GetNextWindow (ghwndList2, GW_HWNDPREV),
                0,
                icyButton,
                (int) cxList2,
                cyWnd,
                SWP_SHOWWINDOW
                );

            SetWindowPos(
                ghwndList1,
                GetNextWindow (ghwndList1, GW_HWNDPREV),
                (int) cxList2 + icyBorder,
                icyButton,
                (int) cxList1,
                cyWnd,
                SWP_SHOWWINDOW
                );

            SetWindowPos(
                ghwndEdit,
                GetNextWindow (ghwndEdit, GW_HWNDPREV),
                (int) (cxList1 + cxList2) + 2*icyBorder,
                icyButton,
                (int)width - ((int)(cxList1 + cxList2) + 2*icyBorder),
                cyWnd,
                SWP_SHOWWINDOW
                );

            InvalidateRect (hwnd, NULL, TRUE);
        }

        break;
    }
    case WM_PAINT:
    {
        PAINTSTRUCT ps;


        BeginPaint (hwnd, &ps);

        if (IsIconic (hwnd))
        {
            DrawIcon (ps.hdc, 0, 0, hIcon);
        }
        else
        {
            FillRect (ps.hdc, &ps.rcPaint, GetStockObject (LTGRAY_BRUSH));
#ifdef WIN32
            MoveToEx (ps.hdc, 0, 0, NULL);
#else
            MoveTo (ps.hdc, 0, 0);
#endif
            LineTo (ps.hdc, 5000, 0);

#ifdef WIN32
            MoveToEx (ps.hdc, 0, icyButton - 4, NULL);
#else
            MoveTo (ps.hdc, 0, icyButton - 4);
#endif
            LineTo (ps.hdc, 5000, icyButton - 4);
        }

        EndPaint (hwnd, &ps);

        break;
    }
    case WM_CLOSE:
    {
        BOOL bAutoShutdown = FALSE;
        RECT rect;


        //
        // Save defaults in ini file
        //

        {
            char buf[32];
            typedef struct _DEF_VALUE2
            {
                char far *lpszEntry;
                DWORD     dwValue;

            } DEF_VALUE2;

            DEF_VALUE2 aDefVals[] =
            {
                { "BufSize",            dwBigBufSize },
                { "AddressID",          dwDefAddressID },
#if TAPI_2_0
                { "20LineAPIVersion",   dwDefLineAPIVersion },
#else
#if TAPI_1_1
                { "14LineAPIVersion",   dwDefLineAPIVersion },
#else
                { "13LineAPIVersion",   dwDefLineAPIVersion },
#endif
#endif
                { "BearerMode",         dwDefBearerMode },
                { "CountryCode",        dwDefCountryCode },
                { "LineDeviceID",       dwDefLineDeviceID },
                { "LineExtVersion",     dwDefLineExtVersion },
                { "MediaMode",          dwDefMediaMode },
                { "LinePrivilege",      dwDefLinePrivilege },
#if TAPI_2_0
                { "20PhoneAPIVersion",  dwDefPhoneAPIVersion },
#else
#if TAPI_1_1
                { "14PhoneAPIVersion",  dwDefPhoneAPIVersion },
#else
                { "13PhoneAPIVersion",  dwDefPhoneAPIVersion },
#endif
#endif
                { "PhoneDeviceID",      dwDefPhoneDeviceID },
                { "PhoneExtVersion",    dwDefPhoneExtVersion },
                { "PhonePrivilege",     dwDefPhonePrivilege },
#if TAPI_2_0
                { "20UserButton1",      aUserButtonFuncs[0] },
                { "20UserButton2",      aUserButtonFuncs[1] },
                { "20UserButton3",      aUserButtonFuncs[2] },
                { "20UserButton4",      aUserButtonFuncs[3] },
                { "20UserButton5",      aUserButtonFuncs[4] },
                { "20UserButton6",      aUserButtonFuncs[5] },
#else
#if TAPI_1_1
                { "14UserButton1",      aUserButtonFuncs[0] },
                { "14UserButton2",      aUserButtonFuncs[1] },
                { "14UserButton3",      aUserButtonFuncs[2] },
                { "14UserButton4",      aUserButtonFuncs[3] },
                { "14UserButton5",      aUserButtonFuncs[4] },
                { "14UserButton6",      aUserButtonFuncs[5] },
#else
                { "13UserButton1",      aUserButtonFuncs[0] },
                { "13UserButton2",      aUserButtonFuncs[1] },
                { "13UserButton3",      aUserButtonFuncs[2] },
                { "13UserButton4",      aUserButtonFuncs[3] },
                { "13UserButton5",      aUserButtonFuncs[4] },
                { "13UserButton6",      aUserButtonFuncs[5] },
#endif
#endif
                { "TimeStamp",          bTimeStamp },
                { "NukeIdleMonitorCalls",  bNukeIdleMonitorCalls },
                { "NukeIdleOwnedCalls",    bNukeIdleOwnedCalls },
                { "DisableHandleChecking", gbDisableHandleChecking },
                { "DumpStructsFlags",   dwDumpStructsFlags },
                { NULL, 0 },
                { "Version",            (DWORD) szCurrVer },
                { "UserUserInfo",       (DWORD) szDefUserUserInfo },
                { "DestAddress",        (DWORD) szDefDestAddress },
                { "LineDeviceClass",    (DWORD) szDefLineDeviceClass },
                { "PhoneDeviceClass",   (DWORD) szDefPhoneDeviceClass },
                { "AppName",            (DWORD) szDefAppName },
#if TAPI_2_0
                { "20UserButton1Text",  (DWORD) &aUserButtonsText[0] },
                { "20UserButton2Text",  (DWORD) &aUserButtonsText[1] },
                { "20UserButton3Text",  (DWORD) &aUserButtonsText[2] },
                { "20UserButton4Text",  (DWORD) &aUserButtonsText[3] },
                { "20UserButton5Text",  (DWORD) &aUserButtonsText[4] },
                { "20UserButton6Text",  (DWORD) &aUserButtonsText[5] },
#else
#if TAPI_1_1
                { "14UserButton1Text",  (DWORD) &aUserButtonsText[0] },
                { "14UserButton2Text",  (DWORD) &aUserButtonsText[1] },
                { "14UserButton3Text",  (DWORD) &aUserButtonsText[2] },
                { "14UserButton4Text",  (DWORD) &aUserButtonsText[3] },
                { "14UserButton5Text",  (DWORD) &aUserButtonsText[4] },
                { "14UserButton6Text",  (DWORD) &aUserButtonsText[5] },
#else
                { "13UserButton1Text",  (DWORD) &aUserButtonsText[0] },
                { "13UserButton2Text",  (DWORD) &aUserButtonsText[1] },
                { "13UserButton3Text",  (DWORD) &aUserButtonsText[2] },
                { "13UserButton4Text",  (DWORD) &aUserButtonsText[3] },
                { "13UserButton5Text",  (DWORD) &aUserButtonsText[4] },
                { "13UserButton6Text",  (DWORD) &aUserButtonsText[5] },
#endif
#endif
// help                { "TapiHlpPath",        (DWORD) szTapiHlp },
// help                { "TspiHlpPath",        (DWORD) szTspiHlp },
                { NULL, 0 }
            };

            int i;

            for (i = 0; aDefVals[i].lpszEntry; i++)
            {
                sprintf (buf, "%lx", aDefVals[i].dwValue);

                WriteProfileString(
                    MYSECTION,
                    aDefVals[i].lpszEntry,
                    buf
                    );
            }

            i++;

            for (; aDefVals[i].lpszEntry; i++)
            {
                WriteProfileString(
                    MYSECTION,
                    aDefVals[i].lpszEntry,
                    (LPCSTR) aDefVals[i].dwValue
                    );
            }


            //
            // Save the window dimensions (if iconic then don't bother)
            //

            if (!IsIconic (hwnd))
            {
                if (IsZoomed (hwnd))
                {
                    strcpy (buf, "max");
                }
                else
                {
                    GetWindowRect (hwnd, &rect);

                    sprintf(
                        buf,
                        "%d,%d,%d,%d",
                        rect.left,
                        rect.top,
                        rect.right,
                        rect.bottom
                        );
                }

                WriteProfileString(
                    MYSECTION,
                    "Position",
                    (LPCSTR) buf
                    );

                sprintf (buf, "%ld,%ld,%ld", cxList2, cxList1, cxWnd);

                WriteProfileString(
                    MYSECTION,
                    "ControlRatios",
                    (LPCSTR) buf
                    );
            }
        }



        //
        // Give user chance to auto-shutdown any active line/phone apps
        //

        if (aWidgets)
        {
            if (MessageBox(
                    hwnd,
                    "Shutdown all hLineApps/hPhoneApps? (recommended)",
                    "Tapi Browser closing",
                    MB_YESNO
                    ) == IDNO)
            {
                goto WM_CLOSE_freeBigBuf;
            }

            bShowParams = FALSE;

            while (aWidgets)
            {
                PMYWIDGET pWidgetToClose = aWidgets;


                if (aWidgets->dwType == WT_LINEAPP)
                {
                    pLineAppSel = (PMYLINEAPP) aWidgets;
                    FuncDriver (lShutdown);
                }
                else if (aWidgets->dwType == WT_PHONEAPP)
                {
                    pPhoneAppSel = (PMYPHONEAPP) aWidgets;
                    FuncDriver (pShutdown);
                }

                if (pWidgetToClose == aWidgets)
                {
                    //
                    // The shutdown wasn't successful (or our list is
                    // messed up an it'd not a LINEAPP or PHONEAPP widget),
                    // so manually nuke this widget so we don't hang in
                    // this loop forever
                    //

                    RemoveWidgetFromList (pWidgetToClose);
                }
            }
        }

WM_CLOSE_freeBigBuf:

        if (hLogFile)
        {
            fclose (hLogFile);
        }
        DestroyIcon (hIcon);
        free (pBigBuf);
        free (lpCallParams);
//        if (aSelWidgets)
//        {
//            free (aSelWidgets);
//        }
//        if (aSelWidgetsPrev)
//        {
//            free (aSelWidgetsPrev);
//        }
        DeleteObject (hFont);
        DeleteObject (hFont2);
        PostQuitMessage (0);
        break;

    }
    } //switch

    return FALSE;
}


BOOL
CALLBACK
AboutDlgProc(
    HWND hwnd,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    switch (msg)
    {
    case WM_INITDIALOG:

        if (lParam)
        {
            SetDlgItemText (hwnd, IDC_EDIT1, (LPCSTR) lParam);
        }

        break;

    case WM_COMMAND:

        switch (LOWORD(wParam))
        {
        case IDOK:

            EndDialog (hwnd, 0);
            break;
        }
        break;

#ifdef WIN32
    case WM_CTLCOLORSTATIC:

        SetBkColor ((HDC) wParam, RGB (192,192,192));
        return (BOOL) GetStockObject (LTGRAY_BRUSH);
#else
    case WM_CTLCOLOR:
    {
        if (HIWORD(lParam) == CTLCOLOR_STATIC)
        {
            SetBkColor ((HDC) wParam, RGB (192,192,192));
            return (BOOL) GetStockObject (LTGRAY_BRUSH);
        }
        break;
    }
#endif
    case WM_PAINT:
    {
        PAINTSTRUCT ps;

        BeginPaint (hwnd, &ps);
        FillRect (ps.hdc, &ps.rcPaint, GetStockObject (LTGRAY_BRUSH));
        EndPaint (hwnd, &ps);

        break;
    }
    }

    return FALSE;
}


void
FAR
ShowStr(
    LPCSTR format,
    ...
    )
{
    char buf[256];
    va_list ap;


    va_start(ap, format);
    wvsprintf (buf, format, ap);

    if (hLogFile)
    {
        fprintf (hLogFile, "%s\n", buf);
    }

    strcat (buf, "\r\n");


    //
    // Insert text at end
    //

#ifdef WIN32
    SendMessage (ghwndEdit, EM_SETSEL, (WPARAM)0xfffffffd, (LPARAM)0xfffffffe);
#else
    SendMessage(
        ghwndEdit,
        EM_SETSEL,
        (WPARAM)0,
        (LPARAM) MAKELONG(0xfffd,0xfffe)
        );
#endif

    SendMessage (ghwndEdit, EM_REPLACESEL, 0, (LPARAM) buf);


#ifdef WIN32

    //
    // Scroll to end of text
    //

    SendMessage (ghwndEdit, EM_SCROLLCARET, 0, 0);
#endif

    va_end(ap);
}


VOID
UpdateWidgetListCall(
    PMYCALL pCall
    )
{
    LONG i, lSel;
    char buf[64];


    for (i = 0; aCallStates[i].dwVal != 0xffffffff; i++)
    {
        if (pCall->dwCallState == aCallStates[i].dwVal)
        {
            break;
        }
    }

    sprintf(
        buf,
        "    Call=x%lx %s %s",
        pCall->hCall,
        aCallStates[i].lpszVal,
        (pCall->bMonitor ? "Monitor" : "Owner")
        );

    i = (LONG) GetWidgetIndex ((PMYWIDGET) pCall);

    lSel = SendMessage (ghwndList1, LB_GETCURSEL, 0, 0);
    SendMessage (ghwndList1, LB_DELETESTRING, (WPARAM) i, 0);
    SendMessage (ghwndList1, LB_INSERTSTRING, (WPARAM) i, (LPARAM) buf);
    SendMessage (ghwndList1, LB_SETITEMDATA, (WPARAM) i, (LPARAM) pCall);

    if (lSel == i)
    {
        SendMessage (ghwndList1, LB_SETCURSEL, (WPARAM) i, 0);
    }
}


VOID
CALLBACK
tapiCallback(
    DWORD   hDevice,
    DWORD   dwMsg,
    DWORD   dwCallbackInstance,
    DWORD   dwParam1,
    DWORD   dwParam2,
    DWORD   dwParam3
    )
{
    typedef struct _MSG_PARAMS
    {
        char    *lpszMsg;

        LPVOID  aParamFlagTables[3];

    } MSG_PARAMS, *PMSG_PARAMS;

    static MSG_PARAMS msgParams[] =
    {
        { "LINE_ADDRESSSTATE",       { NULL, aAddressStates, NULL } },
        { "LINE_CALLINFO",           { aCallInfoStates, NULL, NULL } },
        { "LINE_CALLSTATE",          { aCallStates, NULL, aCallPrivileges } },
        { "LINE_CLOSE",              { NULL, NULL, NULL } },
        { "LINE_DEVSPECIFIC",        { NULL, NULL, NULL } },
        { "LINE_DEVSPECIFICFEATURE", { NULL, NULL, NULL } },
        { "LINE_GATHERDIGITS",       { aGatherTerms, NULL, NULL } },
        { "LINE_GENERATE",           { aGenerateTerms, NULL, NULL } },
        { "LINE_LINEDEVSTATE",       { aLineStates, NULL, NULL } },
        { "LINE_MONITORDIGITS",      { NULL, aDigitModes, NULL } },
        { "LINE_MONITORMEDIA",       { aMediaModes, NULL, NULL } },
        { "LINE_MONITORTONE",        { NULL, NULL, NULL } },
        { "LINE_REPLY",              { NULL, NULL, NULL } },
        { "LINE_REQUEST",            { aRequestModes, NULL, NULL } }
         ,
        { "PHONE_BUTTON",            { NULL, aButtonModes, aButtonStates } },
        { "PHONE_CLOSE",             { NULL, NULL, NULL } },
        { "PHONE_DEVSPECIFIC",       { NULL, NULL, NULL } },
        { "PHONE_REPLY",             { NULL, NULL, NULL } },
        { "PHONE_STATE",             { aPhoneStates, NULL, NULL } }

#if TAPI_1_1
         ,
        { "LINE_CREATE",             { NULL, NULL, NULL } },
        { "PHONE_CREATE",            { NULL, NULL, NULL } }

#if TAPI_2_0
         ,
        { "LINE_AGENTSPECIFIC",      { NULL, NULL, NULL } },
        { "LINE_AGENTSTATUS",        { NULL, NULL, NULL } },
        { "LINE_APPNEWCALL",         { NULL, NULL, aCallPrivileges } },
        { "LINE_PROXYREQUEST",       { NULL, NULL, NULL } },
        { "LINE_REMOVE",             { NULL, NULL, NULL } },
        { "PHONE_REMOVE",            { NULL, NULL, NULL } }

#endif

#endif

    };
    int     i, j;
    LONG    lResult;


    UpdateResults (TRUE);

#if TAPI_1_1
#if TAPI_2_0
    if (dwMsg <= PHONE_REMOVE)
#else
    if (dwMsg <= PHONE_CREATE)
#endif
#else
    if (dwMsg <= PHONE_STATE)
#endif
    {
        DWORD aParams[3] = { dwParam1, dwParam2, dwParam3 };


        {
            char *pszTimeStamp = GetTimeStamp();

            ShowStr(
                "%sreceived %s",
                pszTimeStamp,
                msgParams[dwMsg].lpszMsg
                );
        }

        ShowStr ("%sdevice=x%lx", szTab, hDevice);
        ShowStr ("%scbInst=x%lx", szTab, dwCallbackInstance);

        if (dwMsg == LINE_CALLSTATE)
        {
            msgParams[2].aParamFlagTables[1] = NULL;

            switch (dwParam1)
            {
            case LINECALLSTATE_BUSY:

                msgParams[2].aParamFlagTables[1] = (LPVOID) aBusyModes;
                break;

            case LINECALLSTATE_DIALTONE:

                msgParams[2].aParamFlagTables[1] = (LPVOID) aDialToneModes;
                break;

            case LINECALLSTATE_SPECIALINFO:

                msgParams[2].aParamFlagTables[1] = (LPVOID) aSpecialInfo;
                break;

            case LINECALLSTATE_DISCONNECTED:

                msgParams[2].aParamFlagTables[1] = (LPVOID) aDisconnectModes;
                break;

#if TAPI_1_1

            case LINECALLSTATE_CONNECTED:

                msgParams[2].aParamFlagTables[1] = (LPVOID) aConnectedModes;
                break;

            case LINECALLSTATE_OFFERING:

                msgParams[2].aParamFlagTables[1] = (LPVOID) aOfferingModes;
                break;

#endif // TAPI_1_1

            } // switch
        }

        else if (dwMsg == PHONE_STATE)
        {
            msgParams[18].aParamFlagTables[1] = NULL;

            switch (dwParam1)
            {
            case PHONESTATE_HANDSETHOOKSWITCH:
            case PHONESTATE_SPEAKERHOOKSWITCH:
            case PHONESTATE_HEADSETHOOKSWITCH:

                msgParams[18].aParamFlagTables[1] = aHookSwitchModes;
                break;

            } // switch
        }

        for (i = 0; i < 3; i++)
        {
            char buf[80];


            sprintf (buf, "%sparam%d=x%lx, ", szTab, i+1, aParams[i]);

            if (msgParams[dwMsg].aParamFlagTables[i])
            {
                PLOOKUP pLookup = (PLOOKUP)
                    msgParams[dwMsg].aParamFlagTables[i];


                for (j = 0; aParams[i], pLookup[j].dwVal != 0xffffffff; j++)
                {
                    if (aParams[i] & pLookup[j].dwVal)
                    {
                        if (buf[0] == 0)
                        {
                            sprintf (buf, "%s%s", szTab, szTab);
                        }

                        strcat (buf, pLookup[j].lpszVal);
                        strcat (buf, " ");
                        aParams[i] = aParams[i] & ~pLookup[j].dwVal;

                        if (strlen (buf) > 50)
                        {
                            //
                            // We don't want strings getting so long that
                            // they're going offscreen, so break them up.
                            //

                            ShowStr (buf);
                            buf[0] = 0;
                        }
                    }
                }

                if (aParams[i])
                {
                    strcat (buf, "<unknown flag(s)>");
                }

            }

            if (buf[0])
            {
                ShowStr (buf);
            }
        }
    }
    else
    {
        ShowStr ("ERROR! callback received unknown msg=x%lx", dwMsg);
        ShowStr ("%shDev=x%lx,  cbInst=x%lx, p1=x%lx, p2=x%lx, p3=x%lx",
            szTab,
            hDevice,
            dwCallbackInstance,
            dwParam1,
            dwParam2,
            dwParam3
            );

        return;
    }

    UpdateResults (FALSE);

    switch (dwMsg)
    {
    case LINE_CALLSTATE:
    {
        PMYLINE pLine;
        PMYCALL pCall = GetCall ((HCALL) hDevice);


        //
        // If the call state is idle & we're in "nuke idle xxx calls"
        // mode then determine the privilege of this callto see if we
        // need to nuke it
        //

        if ((dwParam1 == LINECALLSTATE_IDLE) &&
            (bNukeIdleMonitorCalls || bNukeIdleOwnedCalls))
        {
            BOOL            bNukeCall = FALSE;
            LINECALLSTATUS  callStatus;


            callStatus.dwTotalSize = (DWORD) sizeof(LINECALLSTATUS);

            lResult = lineGetCallStatus ((HCALL) hDevice, &callStatus);

            ShowLineFuncResult ("lineGetCallStatus", lResult);

            if (lResult == 0)
            {
                if ((callStatus.dwCallPrivilege & LINECALLPRIVILEGE_OWNER))
                {
                    if (bNukeIdleOwnedCalls)
                    {
                        bNukeCall = TRUE;
                    }
                }
                else
                {
                    if (bNukeIdleMonitorCalls)
                    {
                        bNukeCall = TRUE;
                    }
                }
            }

            if (bNukeCall)
            {
                if ((lResult = lineDeallocateCall ((HCALL) hDevice)) == 0)
                {
                    ShowStr ("Call x%lx deallocated on IDLE", hDevice);

                    if (pCall)
                    {
                        FreeCall (pCall);
                    }

                    break;
                }
                else
                {
                    ShowLineFuncResult ("lineDeallocateCall", lResult);
                }
            }
        }


        //
        // Find call in the widget list, save the call state, &
        // update it's text in the widget list.
        //

        if (pCall)
        {
            //
            // If dwNumPendingDrops is non-zero, then user previously
            // pressed "Call-" button and we're waiting for a call to
            // go IDLE so we can deallocate it. Check to see if this
            // is the call we want to nuke. (Note: we used to nuke the
            // call when we got a successful REPLY msg back from the
            // drop request; the problem with that is some SPs complete
            // the drop request *before* they set the call state to
            // IDLE, and our call to lineDeallocateCall would fail
            // since TAPI won't let a call owner deallocate a call if
            // it's not IDLE.)
            //

            if (dwNumPendingDrops &&
                (dwParam1 == LINECALLSTATE_IDLE) &&
                pCall->lDropReqID)
            {
                dwNumPendingDrops--;

                ShowStr(
                    "Deallocating hCall x%lx " \
                        "(REPLY for requestID x%lx might be filtered)",
                    pCall->hCall,
                    pCall->lDropReqID
                    );

                lResult = lineDeallocateCall (pCall->hCall);

                ShowLineFuncResult ("lineDeallocateCall", lResult);

                if (lResult == 0)
                {
                    FreeCall (pCall);
                    break;
                }
                else
                {
                    pCall->lDropReqID = 0;
                }
            }

            pCall->dwCallState = dwParam1;
            UpdateWidgetListCall (pCall);
        }


        //
        // If here this is the first we've heard of this this call,
        // so find out which line it's on & create a call widget
        // for it
        //

        else if (dwParam3 != 0)
        {
            LINECALLINFO callInfo;


            memset (&callInfo, 0, sizeof(LINECALLINFO));
            callInfo.dwTotalSize = sizeof(LINECALLINFO);
            lResult = lineGetCallInfo ((HCALL)hDevice, &callInfo);

            ShowStr(
                "%slineGetCallInfo returned x%lx, hLine=x%lx",
                szTab,
                lResult,
                callInfo.hLine
                );

            if (lResult == 0)
            {
                if ((pLine = GetLine (callInfo.hLine)))
                {
                    if ((pCall = AllocCall (pLine)))
                    {
                        pCall->hCall       = (HCALL) hDevice;
                        pCall->dwCallState = dwParam1;
                        pCall->bMonitor    = (dwParam3 ==
                            LINECALLPRIVILEGE_MONITOR ? TRUE : FALSE);
                        UpdateWidgetListCall (pCall);
                    }
                }
            }
        }

        break;
    }
    case LINE_GATHERDIGITS:
    {
        PMYCALL pCall;


        if ((pCall = GetCall ((HCALL) hDevice)) && pCall->lpsGatheredDigits)
        {
            ShowStr ("%sGathered digits:", szTab);
            ShowBytes (pCall->dwNumGatheredDigits, pCall->lpsGatheredDigits, 2);
            free (pCall->lpsGatheredDigits);
            pCall->lpsGatheredDigits = NULL;
        }

        break;
    }
    case LINE_REPLY:

        if (dwNumPendingMakeCalls)
        {
            //
            // Check to see if this is a reply for a lineMakeCall request
            //

            PMYWIDGET pWidget = aWidgets;


            while (pWidget && (pWidget->dwType != WT_PHONEAPP))
            {
                if (pWidget->dwType == WT_CALL)
                {
                    PMYCALL pCall = (PMYCALL) pWidget;

                    if ((DWORD)pCall->lMakeCallReqID == dwParam1)
                    {
                        //
                        // The reply id matches the make call req id
                        //

                        dwNumPendingMakeCalls--;

                        if (dwParam2 || !pCall->hCall)
                        {
                            //
                            // Request error or no call created, so free
                            // up the struct & update the hCalls listbox
                            //

                            if (dwParam2 == 0)
                            {
                                ShowStr(
                                    "    NOTE: *lphCall==NULL, "\
                                        "freeing call data structure"
                                    );
                            }

                            pWidget = pWidget->pNext;

                            FreeCall (pCall);

                            continue;
                        }
                        else
                        {
                            //
                            // Reset this field so we don't run into
                            // problems later with another of the same
                            // request id
                            //

                            pCall->lMakeCallReqID = 0;

                            UpdateWidgetListCall (pCall);
                        }
                    }
                }

                pWidget = pWidget->pNext;
            }
        }

        if (dwParam2)
        {
            //
            // Dump the error in a readable format
            //

            MessageBeep ((UINT) -1);

            if (dwParam2 > LAST_LINEERR)
            {
                ShowStr ("inval err code (x%lx)", dwParam2);
            }
            else
            {
                ShowStr(
                    "    %s%s",
                    "LINEERR_", // ...to shrink the aszLineErrs array
                    aszLineErrs[LOWORD(dwParam2)]
                    );
            }
        }

        break;

    case PHONE_REPLY:

        if (dwParam2)
        {
            //
            // Dump the error in a readable format
            //

            MessageBeep ((UINT) -1);

            if (dwParam2 > PHONEERR_REINIT)
            {
                ShowStr ("inval err code (x%lx)", dwParam2);
            }
            else
            {
                ShowStr(
                    "    %s%s",
                    "PHONEERR_", // ...to shrink the aszPhoneErrs array
                    aszPhoneErrs[LOWORD(dwParam2)]
                    );
            }
        }

        break;

    case LINE_CLOSE:

        FreeLine (GetLine ((HLINE) hDevice));
        break;

    case PHONE_CLOSE:

        FreePhone (GetPhone ((HPHONE) hDevice));
        break;

#if TAPI_2_0

    case LINE_APPNEWCALL:
    {
        PMYLINE pLine;
        PMYCALL pCall;


        if ((pLine = GetLine ((HLINE) hDevice)))
        {
            if ((pCall = AllocCall (pLine)))
            {
                pCall->hCall       = (HCALL) dwParam2;
                pCall->dwCallState = LINECALLSTATE_UNKNOWN;
                pCall->bMonitor    = (dwParam3 ==
                    LINECALLPRIVILEGE_MONITOR ? TRUE : FALSE);
                UpdateWidgetListCall (pCall);
            }
        }

        break;
    }
    case LINE_PROXYREQUEST:
    {
        LPLINEPROXYREQUEST  pRequest = (LPLINEPROXYREQUEST) dwParam1;
        STRUCT_FIELD fields[] =
        {
            { szdwSize,                     FT_DWORD,   pRequest->dwSize, NULL },
            { "dwClientMachineNameSize",    FT_SIZE,    pRequest->dwClientMachineNameSize, NULL },
            { "dwClientMachineNameOffset",  FT_OFFSET,  pRequest->dwClientMachineNameOffset, NULL },
            { "dwClientUserNameSize",       FT_SIZE,    pRequest->dwClientUserNameSize, NULL },
            { "dwClientUserNameOffset",     FT_OFFSET,  pRequest->dwClientUserNameOffset, NULL },
            { "dwClientAppAPIVersion",      FT_DWORD,   pRequest->dwClientAppAPIVersion, NULL },
            { "dwRequestType",              FT_ORD,     pRequest->dwRequestType, aProxyRequests }
        };
        STRUCT_FIELD_HEADER fieldHeader =
        {
            pRequest,
            "LINEPROXYREQUEST",
            7,
            fields
        };


        ShowStructByField (&fieldHeader, TRUE);

        switch (pRequest->dwRequestType)
        {
        case LINEPROXYREQUEST_SETAGENTGROUP:
        {
            STRUCT_FIELD fields[] =
            {
                { szdwAddressID,    FT_DWORD,   pRequest->SetAgentGroup.dwAddressID, NULL }

// BUGBUG LINE_PROXYREQUEST: dump agent grp list

            };
            STRUCT_FIELD_HEADER fieldHeader =
            {
                pRequest,
                "SetAgentGroup",
                1,
                fields
            };


            ShowStructByField (&fieldHeader, TRUE);

            break;
        }
        case LINEPROXYREQUEST_SETAGENTSTATE:
        {
            STRUCT_FIELD fields[] =
            {
                { szdwAddressID,        FT_DWORD,   pRequest->SetAgentState.dwAddressID, NULL },
                { "dwAgentState",       FT_ORD,     pRequest->SetAgentState.dwAgentState, aAgentStates },
                { "dwNextAgentState",   FT_ORD,     pRequest->SetAgentState.dwNextAgentState, aAgentStates }
            };
            STRUCT_FIELD_HEADER fieldHeader =
            {
                pRequest,
                "SetAgentState",
                3,
                fields
            };


            ShowStructByField (&fieldHeader, TRUE);

            break;
        }
        case LINEPROXYREQUEST_SETAGENTACTIVITY:
        {
            STRUCT_FIELD fields[] =
            {
                { szdwAddressID,    FT_DWORD,   pRequest->SetAgentActivity.dwAddressID, NULL },
                { "dwActivityID",   FT_DWORD,   pRequest->SetAgentActivity.dwActivityID, NULL }
            };
            STRUCT_FIELD_HEADER fieldHeader =
            {
                pRequest,
                "SetAgentActivity",
                2,
                fields
            };


            ShowStructByField (&fieldHeader, TRUE);

            break;
        }
        case LINEPROXYREQUEST_GETAGENTCAPS:
        {
            STRUCT_FIELD fields[] =
            {
                { szdwAddressID,    FT_DWORD,   pRequest->GetAgentCaps.dwAddressID, NULL }
            };
            STRUCT_FIELD_HEADER fieldHeader =
            {
                pRequest,
                "GetAgentCaps",
                1,
                fields
            };


// BUGBUG LINE_PROXYREQUEST: fill in agent caps?

            ShowStructByField (&fieldHeader, TRUE);

            break;
        }
        case LINEPROXYREQUEST_GETAGENTSTATUS:
        {
            STRUCT_FIELD fields[] =
            {
                { szdwAddressID,    FT_DWORD,   pRequest->GetAgentStatus.dwAddressID, NULL }
            };
            STRUCT_FIELD_HEADER fieldHeader =
            {
                pRequest,
                "GetAgentStatus",
                1,
                fields
            };


            ShowStructByField (&fieldHeader, TRUE);

            break;
        }
        case LINEPROXYREQUEST_AGENTSPECIFIC:
        {
            STRUCT_FIELD fields[] =
            {
                { szdwAddressID,                FT_DWORD,   pRequest->AgentSpecific.dwAddressID, NULL },
                { "dwAgentExtensionIDIndex",    FT_DWORD,   pRequest->AgentSpecific.dwAgentExtensionIDIndex, NULL },
                { szdwSize,                     FT_SIZE,    pRequest->AgentSpecific.dwSize, NULL },
                { "Params",                     FT_OFFSET,  (DWORD) (pRequest->AgentSpecific.Params - (LPBYTE) pRequest), NULL }
            };
            STRUCT_FIELD_HEADER fieldHeader =
            {
                pRequest,
                "AgentSpecific",
                4,
                fields
            };


            ShowStructByField (&fieldHeader, TRUE);

            break;
        }
        case LINEPROXYREQUEST_GETAGENTACTIVITYLIST:
        {
            STRUCT_FIELD fields[] =
            {
                { szdwAddressID,    FT_DWORD,   pRequest->GetAgentActivityList.dwAddressID, NULL }
            };
            STRUCT_FIELD_HEADER fieldHeader =
            {
                pRequest,
                "GetAgentActivityList",
                1,
                fields
            };


// BUGBUG LINE_PROXYREQUEST: fill in agent activity list?

            ShowStructByField (&fieldHeader, TRUE);

            break;
        }
        case LINEPROXYREQUEST_GETAGENTGROUPLIST:
        {
            STRUCT_FIELD fields[] =
            {
                { szdwAddressID,    FT_DWORD,   pRequest->GetAgentGroupList.dwAddressID, NULL }
            };
            STRUCT_FIELD_HEADER fieldHeader =
            {
                pRequest,
                "GetAgentGroupList",
                1,
                fields
            };


// BUGBUG LINE_PROXYREQUEST: fill in agent grp list?

            ShowStructByField (&fieldHeader, TRUE);

            break;
        }
        } // switch (pRequest->dwRequestType)

        break;
    }

#endif

    }
}


BOOL
CALLBACK
ParamsDlgProc(
    HWND hwnd,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    DWORD  i;

    typedef struct _DLG_INST_DATA
    {
        PFUNC_PARAM_HEADER pParamsHeader;

        LONG lLastSel;

        char szComboText[MAX_STRING_PARAM_SIZE];

    } DLG_INST_DATA, *PDLG_INST_DATA;

    PDLG_INST_DATA pDlgInstData = (PDLG_INST_DATA)
        GetWindowLong (hwnd, DWL_USER);


    switch (msg)
    {
    case WM_INITDIALOG:
    {
        //
        // Alloc a dlg instance data struct, init it, & save a ptr to it
        //

        pDlgInstData = (PDLG_INST_DATA) malloc (sizeof(DLG_INST_DATA));

        // BUGBUG if (!pDlgInstData)

        pDlgInstData->pParamsHeader = (PFUNC_PARAM_HEADER) lParam;
        pDlgInstData->lLastSel = -1;

        SetWindowLong (hwnd, DWL_USER, (LONG) pDlgInstData);


        //
        // Stick all the param names in the listbox, & for each PT_DWORD
        // param save it's default value
        //

        for (i = 0; i < pDlgInstData->pParamsHeader->dwNumParams; i++)
        {
            SendDlgItemMessage(
                hwnd,
                IDC_LIST1,
                LB_INSERTSTRING,
                (WPARAM) -1,
                (LPARAM) pDlgInstData->pParamsHeader->aParams[i].szName
                );

            if (pDlgInstData->pParamsHeader->aParams[i].dwType == PT_DWORD)
            {
                pDlgInstData->pParamsHeader->aParams[i].u.dwDefValue =
                    pDlgInstData->pParamsHeader->aParams[i].dwValue;
            }
        }


        //
        // Set the dlg title as appropriate
        //

// help        if (pDlgInstData->pParamsHeader->FuncIndex == DefValues)
// help        {
// help            EnableWindow (GetDlgItem (hwnd, IDC_TB_HELP), FALSE);
// help        }

        SetWindowText(
            hwnd,
            aFuncNames[pDlgInstData->pParamsHeader->FuncIndex]
            );


        //
        // Limit the max text length for the combobox's edit field
        // (NOTE: A combobox ctrl actually has two child windows: a
        // edit ctrl & a listbox.  We need to get the hwnd of the
        // child edit ctrl & send it the LIMITTEXT msg.)
        //

        {
            HWND hwndChild =
                GetWindow (GetDlgItem (hwnd, IDC_COMBO1), GW_CHILD);


            while (hwndChild)
            {
                char buf[8];


                GetClassName (hwndChild, buf, 7);

                if (_stricmp (buf, "edit") == 0)
                {
                    break;
                }

                hwndChild = GetWindow (hwndChild, GW_HWNDNEXT);
            }

            SendMessage(
                hwndChild,
                EM_LIMITTEXT,
                (WPARAM) (gbWideStringParams ?
                    (MAX_STRING_PARAM_SIZE/2 - 1) : MAX_STRING_PARAM_SIZE - 1),
                0
                );
        }

        break;
    }
    case WM_COMMAND:
    {
        LONG      lLastSel      = pDlgInstData->lLastSel;
        char far *lpszComboText = pDlgInstData->szComboText;
        PFUNC_PARAM_HEADER pParamsHeader = pDlgInstData->pParamsHeader;


        switch (LOWORD(wParam))
        {
        case IDOK:

            if (lLastSel != -1)
            {
                //
                // Save val of currently selected param
                //

                char buf[MAX_STRING_PARAM_SIZE];


                i = GetDlgItemText (hwnd, IDC_COMBO1, buf, MAX_STRING_PARAM_SIZE-1);

                switch (pParamsHeader->aParams[lLastSel].dwType)
                {
                case PT_STRING:
                {
                    LONG lComboSel;


                    lComboSel = SendDlgItemMessage(
                        hwnd,
                        IDC_COMBO1,
                        CB_GETCURSEL,
                        0,
                        0
                        );

                    if (lComboSel == 0) // "NULL pointer"
                    {
                        pParamsHeader->aParams[lLastSel].dwValue = (DWORD) 0;
                    }
                    else if (lComboSel == 2) // "Invalid string pointer"
                    {
                        pParamsHeader->aParams[lLastSel].dwValue = (DWORD)
                            0xffffffff;
                    }
                    else // "Valid string pointer"
                    {
                        strncpy(
                            pParamsHeader->aParams[lLastSel].u.buf,
                            buf,
                            MAX_STRING_PARAM_SIZE - 1
                            );

                        pParamsHeader->aParams[lLastSel].u.buf[MAX_STRING_PARAM_SIZE-1] = 0;

                        pParamsHeader->aParams[lLastSel].dwValue = (DWORD)
                            pParamsHeader->aParams[lLastSel].u.buf;
                    }

                    break;
                }
                case PT_POINTER:
                case PT_DWORD:
                case PT_FLAGS:
                case PT_CALLPARAMS: // ??? BUGBUG
                case PT_FORWARDLIST: // ??? BUGBUG
                case PT_ORDINAL:
                {
                    if (!sscanf(
                            buf,
                            "%08lx",
                            &pParamsHeader->aParams[lLastSel].dwValue
                            ))
                    {
                        //
                        // Default to 0
                        //

                        pParamsHeader->aParams[lLastSel].dwValue = 0;
                    }

                    break;
                }
                } // switch
            }

            free (pDlgInstData);
            EndDialog (hwnd, TRUE);
            break;

        case IDCANCEL:

            free (pDlgInstData);
            EndDialog (hwnd, FALSE);
            break;

// help        case IDC_TB_HELP:
// help
// help            MyWinHelp(
// help                hwnd,
// help                TRUE,
// help                HELP_PARTIALKEY,
// help                (DWORD) aFuncNames[pParamsHeader->FuncIndex]
// help                );
// help
// help            break;

        case IDC_LIST1:

#ifdef WIN32
            if (HIWORD(wParam) == LBN_SELCHANGE)
#else
            if (HIWORD(lParam) == LBN_SELCHANGE)
#endif
            {
                char buf[MAX_STRING_PARAM_SIZE] = "";
                LPCSTR lpstr = buf;
                LONG lSel =
                    SendDlgItemMessage (hwnd, IDC_LIST1, LB_GETCURSEL, 0, 0);


                if (lLastSel != -1)
                {
                    //
                    // Save the old param value
                    //

                    i = GetDlgItemText(
                        hwnd,
                        IDC_COMBO1,
                        buf,
                        MAX_STRING_PARAM_SIZE - 1
                        );

                    switch (pParamsHeader->aParams[lLastSel].dwType)
                    {
                    case PT_STRING:
                    {
                        LONG lComboSel;


                        lComboSel = SendDlgItemMessage(
                            hwnd,
                            IDC_COMBO1,
                            CB_GETCURSEL,
                            0,
                            0
                            );

                        if (lComboSel == 0) // "NULL pointer"
                        {
                            pParamsHeader->aParams[lLastSel].dwValue = (DWORD)0;
                        }
                        else if (lComboSel == 2) // "Invalid string pointer"
                        {
                            pParamsHeader->aParams[lLastSel].dwValue = (DWORD)
                                0xffffffff;
                        }
                        else // "Valid string pointer" or no sel
                        {
                            strncpy(
                                pParamsHeader->aParams[lLastSel].u.buf,
                                buf,
                                MAX_STRING_PARAM_SIZE - 1
                                );

                            pParamsHeader->aParams[lLastSel].u.buf[MAX_STRING_PARAM_SIZE - 1] = 0;

                            pParamsHeader->aParams[lLastSel].dwValue = (DWORD)
                                pParamsHeader->aParams[lLastSel].u.buf;
                        }

                        break;
                    }
                    case PT_POINTER:
                    case PT_DWORD:
                    case PT_FLAGS:
                    case PT_CALLPARAMS: // ??? BUGBUG
                    case PT_FORWARDLIST: // ??? BUGBUG
                    case PT_ORDINAL:
                    {
                        if (!sscanf(
                                buf,
                                "%08lx",
                                &pParamsHeader->aParams[lLastSel].dwValue
                                ))
                        {
                            //
                            // Default to 0
                            //

                            pParamsHeader->aParams[lLastSel].dwValue = 0;
                        }

                        break;
                    }
                    } // switch
                }


                SendDlgItemMessage (hwnd, IDC_LIST2, LB_RESETCONTENT, 0, 0);
                SendDlgItemMessage (hwnd, IDC_COMBO1, CB_RESETCONTENT, 0, 0);

                switch (pParamsHeader->aParams[lSel].dwType)
                {
                case PT_STRING:
                {
                    char * aszOptions[] =
                    {
                        "NULL pointer",
                        "Valid string pointer",
                        "Invalid string pointer"
                    };


                    for (i = 0; i < 3; i++)
                    {
                        SendDlgItemMessage(
                            hwnd,
                            IDC_COMBO1,
                            CB_INSERTSTRING,
                            (WPARAM) -1,
                            (LPARAM) aszOptions[i]
                            );
                    }

                    if (pParamsHeader->aParams[lSel].dwValue == 0)
                    {
                        i = 0;
                        buf[0] = 0;
                    }
                    else if (pParamsHeader->aParams[lSel].dwValue != 0xffffffff)
                    {
                        i = 1;
                        lpstr = (LPCSTR) pParamsHeader->aParams[lSel].dwValue;
                    }
                    else
                    {
                        i = 2;
                        buf[0] = 0;
                    }

                    SendDlgItemMessage(
                        hwnd,
                        IDC_COMBO1,
                        CB_SETCURSEL,
                        (WPARAM) i,
                        0
                        );

                    break;
                }
                case PT_POINTER:
                case PT_CALLPARAMS: // ??? BUGBUG
                case PT_FORWARDLIST: // ??? BUGBUG
                {
                    SendDlgItemMessage(
                        hwnd,
                        IDC_COMBO1,
                        CB_INSERTSTRING,
                        (WPARAM) -1,
                        (LPARAM) "00000000"
                        );

                    sprintf(
                        buf,
                        "%08lx (valid pointer)",
                        pParamsHeader->aParams[lSel].u.dwDefValue
                        );

                    SendDlgItemMessage(
                        hwnd,
                        IDC_COMBO1,
                        CB_INSERTSTRING,
                        (WPARAM) -1,
                        (LPARAM) buf
                        );

                    SendDlgItemMessage(
                        hwnd,
                        IDC_COMBO1,
                        CB_INSERTSTRING,
                        (WPARAM) -1,
                        (LPARAM) "ffffffff"
                        );

                    sprintf(
                        buf,
                        "%08lx",
                        pParamsHeader->aParams[lSel].dwValue
                        );

                    break;
                }
                case PT_DWORD:
                {
                    SendDlgItemMessage(
                        hwnd,
                        IDC_COMBO1,
                        CB_INSERTSTRING,
                        (WPARAM) -1,
                        (LPARAM) "0000000"
                        );

                    if (pParamsHeader->aParams[lSel].u.dwDefValue)
                    {
                        //
                        // Add the default val string to the combo
                        //

                        sprintf(
                            buf,
                            "%08lx",
                            pParamsHeader->aParams[lSel].u.dwDefValue
                            );

                        SendDlgItemMessage(
                            hwnd,
                            IDC_COMBO1,
                            CB_INSERTSTRING,
                            (WPARAM) -1,
                            (LPARAM) buf
                            );
                    }

                    SendDlgItemMessage(
                        hwnd,
                        IDC_COMBO1,
                        CB_INSERTSTRING,
                        (WPARAM) -1,
                        (LPARAM) "ffffffff"
                        );

                    sprintf(
                        buf,
                        "%08lx",
                        pParamsHeader->aParams[lSel].dwValue
                        );

                    break;
                }
                case PT_FLAGS:
                {
                    //
                    // Stick the bit flag strings in the list box
                    //

                    PLOOKUP pLookup = (PLOOKUP)
                        pParamsHeader->aParams[lSel].u.pLookup;

                    for (i = 0; pLookup[i].dwVal != 0xffffffff; i++)
                    {
                        SendDlgItemMessage(
                            hwnd,
                            IDC_LIST2,
                            LB_INSERTSTRING,
                            (WPARAM) -1,
                            (LPARAM) pLookup[i].lpszVal
                            );

                        if (pParamsHeader->aParams[lSel].dwValue &
                            pLookup[i].dwVal)
                        {
                            SendDlgItemMessage(
                                hwnd,
                                IDC_LIST2,
                                LB_SETSEL,
                                (WPARAM) TRUE,
                                (LPARAM) MAKELPARAM((WORD)i,0)
                                );
                        }
                    }

                    SendDlgItemMessage(
                        hwnd,
                        IDC_COMBO1,
                        CB_INSERTSTRING,
                        (WPARAM) -1,
                        (LPARAM) "select none"
                        );

                    SendDlgItemMessage(
                        hwnd,
                        IDC_COMBO1,
                        CB_INSERTSTRING,
                        (WPARAM) -1,
                        (LPARAM) "select all"
                        );

                    sprintf(
                        buf,
                        "%08lx",
                        pParamsHeader->aParams[lSel].dwValue
                        );

                    break;
                }
                case PT_ORDINAL:
                {
                    //
                    // Stick the bit flag strings in the list box
                    //

                    HWND hwndList2 = GetDlgItem (hwnd, IDC_LIST2);
                    PLOOKUP pLookup = (PLOOKUP)
                        pParamsHeader->aParams[lSel].u.pLookup;

                    for (i = 0; pLookup[i].dwVal != 0xffffffff; i++)
                    {
                        SendMessage(
                            hwndList2,
                            LB_INSERTSTRING,
                            (WPARAM) -1,
                            (LPARAM) pLookup[i].lpszVal
                            );

                        if (pParamsHeader->aParams[lSel].dwValue ==
                            pLookup[i].dwVal)
                        {
                            SendMessage(
                                hwndList2,
                                LB_SETSEL,
                                (WPARAM) TRUE,
                                (LPARAM) MAKELPARAM((WORD)i,0)
                                );
                        }
                    }

                    SendDlgItemMessage(
                        hwnd,
                        IDC_COMBO1,
                        CB_INSERTSTRING,
                        (WPARAM) -1,
                        (LPARAM) (char far *) "select none"
                        );

                    wsprintf(
                        buf,
                        "%08lx",
                        pParamsHeader->aParams[lSel].dwValue
                        );

                    break;
                }
                } //switch

                SetDlgItemText (hwnd, IDC_COMBO1, lpstr);

                pDlgInstData->lLastSel = lSel;
            }
            break;

        case IDC_LIST2:

#ifdef WIN32
            if (HIWORD(wParam) == LBN_SELCHANGE)
#else
            if (HIWORD(lParam) == LBN_SELCHANGE)
#endif
            {
                PLOOKUP pLookup = (PLOOKUP)
                    pParamsHeader->aParams[lLastSel].u.pLookup;
                char buf[16];
                DWORD dwValue = 0;
                int far *ai;
                LONG i, lSelCount =
                    SendDlgItemMessage (hwnd, IDC_LIST2, LB_GETSELCOUNT, 0, 0);


                if (lSelCount)
                {
                    ai = (int far *) malloc ((size_t)lSelCount * sizeof(int));

                    SendDlgItemMessage(
                        hwnd,
                        IDC_LIST2,
                        LB_GETSELITEMS,
                        (WPARAM) lSelCount,
                        (LPARAM) ai
                        );

                    if (pParamsHeader->aParams[lLastSel].dwType == PT_FLAGS)
                    {
                        for (i = 0; i < lSelCount; i++)
                        {
                            dwValue |= pLookup[ai[i]].dwVal;
                        }
                    }
                    else // if (.dwType == PT_ORDINAL)
                    {
                        if (lSelCount == 1)
                        {
                            dwValue = pLookup[ai[0]].dwVal;
                        }
                        else if (lSelCount == 2)
                        {
                            //
                            // Figure out which item we need to de-select,
                            // since we're doing ords & only want 1 item
                            // selected at a time
                            //

                            GetDlgItemText (hwnd, IDC_COMBO1, buf, 16);

                            if (sscanf (buf, "%lx", &dwValue))
                            {
                                if (pLookup[ai[0]].dwVal == dwValue)
                                {
                                    SendDlgItemMessage(
                                        hwnd,
                                        IDC_LIST2,
                                        LB_SETSEL,
                                        0,
                                        (LPARAM) ai[0]
                                        );

                                    dwValue = pLookup[ai[1]].dwVal;
                                }
                                else
                                {
                                    SendDlgItemMessage(
                                        hwnd,
                                        IDC_LIST2,
                                        LB_SETSEL,
                                        0,
                                        (LPARAM) ai[1]
                                        );

                                    dwValue = pLookup[ai[0]].dwVal;
                                }
                            }
                            else
                            {
                                // BUGBUG de-select items???

                                dwValue = 0;
                            }
                        }
                    }

                    free (ai);
                }

                sprintf (buf, "%08lx", dwValue);
                SetDlgItemText (hwnd, IDC_COMBO1, buf);
            }
            break;

        case IDC_COMBO1:

#ifdef WIN32
            switch (HIWORD(wParam))
#else
            switch (HIWORD(lParam))
#endif
            {
            case CBN_SELCHANGE:
            {
                LONG lSel =
                    SendDlgItemMessage (hwnd, IDC_COMBO1, CB_GETCURSEL, 0, 0);


                switch (pParamsHeader->aParams[lLastSel].dwType)
                {
                case PT_POINTER:
                {
                    if (lSel == 1)
                    {
                        //
                        // Strip off the "(valid pointer)" in the edit ctrl
                        //

                        sprintf(
                            lpszComboText,
                            "%08lx",
                            pParamsHeader->aParams[lLastSel].u.ptr
                            );

                        PostMessage (hwnd, WM_USER+55, 0, 0);
                    }

                    break;
                }
                case PT_FLAGS:
                {
                    BOOL bSelect = (lSel ? TRUE : FALSE);

                    SendDlgItemMessage(
                        hwnd,
                        IDC_LIST2,
                        LB_SETSEL,
                        (WPARAM) bSelect,
                        (LPARAM) -1
                        );

                    if (bSelect)
                    {
                        PLOOKUP pLookup = (PLOOKUP)
                            pParamsHeader->aParams[lLastSel].u.pLookup;
                        DWORD dwValue = 0;
                        int far *ai;
                        LONG i, lSelCount =
                            SendDlgItemMessage (hwnd, IDC_LIST2, LB_GETSELCOUNT, 0, 0);


                        if (lSelCount)
                        {
                            ai = (int far *) malloc ((size_t)lSelCount * sizeof(int));

                            SendDlgItemMessage(
                                hwnd,
                                IDC_LIST2,
                                LB_GETSELITEMS,
                                (WPARAM) lSelCount,
                                (LPARAM) ai
                                );

                            for (i = 0; i < lSelCount; i++)
                            {
                                dwValue |= pLookup[ai[i]].dwVal;
                            }

                            free (ai);
                        }

                        sprintf (lpszComboText, "%08lx", dwValue);

                    }
                    else
                    {
                        strcpy (lpszComboText, "00000000");
                    }

                    PostMessage (hwnd, WM_USER+55, 0, 0);

                    break;
                }
                case PT_STRING:

                    if (lSel == 1)
                    {
                        strncpy(
                            lpszComboText,
                            pParamsHeader->aParams[lLastSel].u.buf,
                            MAX_STRING_PARAM_SIZE
                            );

                        lpszComboText[MAX_STRING_PARAM_SIZE-1] = 0;
                    }
                    else
                    {
                        lpszComboText[0] = 0;
                    }

                    PostMessage (hwnd, WM_USER+55, 0, 0);

                    break;

                case PT_DWORD:

                    break;

                case PT_CALLPARAMS:
                {
                    if (lSel == 1)
                    {
#if TAPI_2_0
                        LPLINECALLPARAMS lpCP = (gbWideStringParams ?
                                            lpCallParamsW : lpCallParams);
#else
                        LPLINECALLPARAMS lpCP = lpCallParams;
#endif
                        char *p = (char *) lpCP;

#if TAPI_2_0
                        char    asz[14][MAX_STRING_PARAM_SIZE];
                        LPDWORD pdwProxyRequests = (LPDWORD) (((LPBYTE) lpCP) +
                                    lpCP->dwDevSpecificOffset);
#else
                        char asz[7][MAX_STRING_PARAM_SIZE];
#endif
                        FUNC_PARAM params[] =
                        {
                            { "dwBearerMode",                   PT_FLAGS,  lpCP->dwBearerMode, aBearerModes },
                            { "dwMinRate",                      PT_DWORD,  lpCP->dwMinRate, NULL },
                            { "dwMaxRate",                      PT_DWORD,  lpCP->dwMaxRate, NULL },
                            { "dwMediaMode",                    PT_FLAGS,  lpCP->dwMediaMode, aMediaModes },
                            { "dwCallParamFlags",               PT_FLAGS,  lpCP->dwCallParamFlags, aCallParamFlags },
                            { "dwAddressMode",                  PT_ORDINAL,lpCP->dwAddressMode, aAddressModes },
                            { "dwAddressID",                    PT_DWORD,  lpCP->dwAddressID, NULL },
                            { "DIALPARAMS.dwDialPause",         PT_DWORD,  lpCP->DialParams.dwDialPause, NULL },
                            { "DIALPARAMS.dwDialSpeed",         PT_DWORD,  lpCP->DialParams.dwDialSpeed, NULL },
                            { "DIALPARAMS.dwDigitDuration",     PT_DWORD,  lpCP->DialParams.dwDigitDuration, NULL },
                            { "DIALPARAMS.dwWaitForDialtone",   PT_DWORD,  lpCP->DialParams.dwWaitForDialtone, NULL },
                            { "szOrigAddress",                  PT_STRING, (DWORD) asz[0], asz[0] },
                            { "szDisplayableAddress",           PT_STRING, (DWORD) asz[1], asz[1] },
                            { "szCalledParty",                  PT_STRING, (DWORD) asz[2], asz[2] },
                            { "szComment",                      PT_STRING, (DWORD) asz[3], asz[3] },
                            { "szUserUserInfo",                 PT_STRING, (DWORD) asz[4], asz[4] },
                            { "szHighLevelComp",                PT_STRING, (DWORD) asz[5], asz[5] },
                            { "szLowLevelComp",                 PT_STRING, (DWORD) asz[6], asz[6] }
#if TAPI_2_0
                             ,
                            { "dwPredictiveAutoTransferStates", PT_FLAGS,  lpCP->dwPredictiveAutoTransferStates, aCallStates },
                            { "szTargetAddress",                PT_STRING, (DWORD) asz[7], asz[7] },
                            { "szSendingFlowspec",              PT_STRING, (DWORD) asz[8], asz[8] },
                            { "szReceivingFlowspec",            PT_STRING, (DWORD) asz[9], asz[9] },
                            { "szDeviceClass",                  PT_STRING, (DWORD) asz[10], asz[10] },
                            { "szDeviceConfig",                 PT_STRING, (DWORD) asz[11], asz[11] },
                            { "szCallData",                     PT_STRING, (DWORD) asz[12], asz[12] },
                            { "dwNoAnswerTimeout",              PT_DWORD,  lpCP->dwNoAnswerTimeout, NULL },
                            { "szCallingPartyID",               PT_STRING, (DWORD) asz[13], asz[13] },

                            { "NumProxyRequests",               PT_DWORD,  lpCP->dwDevSpecificSize / 4, NULL },
                            { "  ProxyRequest1",                PT_ORDINAL,*pdwProxyRequests, aProxyRequests },
                            { "  ProxyRequest2",                PT_ORDINAL,*(pdwProxyRequests + 1), aProxyRequests },
                            { "  ProxyRequest3",                PT_ORDINAL,*(pdwProxyRequests + 2), aProxyRequests },
                            { "  ProxyRequest4",                PT_ORDINAL,*(pdwProxyRequests + 3), aProxyRequests },
                            { "  ProxyRequest5",                PT_ORDINAL,*(pdwProxyRequests + 4), aProxyRequests },
                            { "  ProxyRequest6",                PT_ORDINAL,*(pdwProxyRequests + 5), aProxyRequests },
                            { "  ProxyRequest7",                PT_ORDINAL,*(pdwProxyRequests + 6), aProxyRequests },
                            { "  ProxyRequest8",                PT_ORDINAL,*(pdwProxyRequests + 7), aProxyRequests }
#endif
                        };
                        FUNC_PARAM_HEADER paramsHeader =
                            { 0, lCallParams, params, NULL };
                        int     i;

                        LPDWORD apXxxSize[] =
                        {
                            &lpCP->dwOrigAddressSize,
                            &lpCP->dwDisplayableAddressSize,
                            &lpCP->dwCalledPartySize,
                            &lpCP->dwCommentSize,
                            &lpCP->dwUserUserInfoSize,
                            &lpCP->dwHighLevelCompSize,
                            &lpCP->dwLowLevelCompSize,
#if TAPI_2_0
                            &lpCP->dwTargetAddressSize,
                            &lpCP->dwSendingFlowspecSize,
                            &lpCP->dwReceivingFlowspecSize,
                            &lpCP->dwDeviceClassSize,
                            &lpCP->dwDeviceConfigSize,
                            &lpCP->dwCallDataSize,
                            &lpCP->dwCallingPartyIDSize,
#endif
                            NULL
                        };
                        static DWORD   dwAPIVersion, adwStrParamIndices[] =
                        {
                            11, 12, 13, 14, 15, 16, 17,
#if TAPI_2_0
                            19, 20, 21, 22, 23, 24, 26,
#endif
                            0
                        };



                        //
                        // Init the tmp string params
                        //

                        for (i = 0; apXxxSize[i]; i++)
                        {
                            if (*apXxxSize[i])
                            {
#if TAPI_2_0
                                if (gbWideStringParams)
                                {
                                    WideCharToMultiByte(
                                        CP_ACP,
                                        0,
                                        (LPCWSTR) (p + *(apXxxSize[i] + 1)),
                                        -1,
                                        asz[i],
                                        MAX_STRING_PARAM_SIZE,
                                        NULL,
                                        NULL
                                        );
                                }
                                else
                                {
                                    strcpy (asz[i], p + *(apXxxSize[i] + 1));
                                }
#else
                                strcpy (asz[i], p + *(apXxxSize[i] + 1));
#endif
                            }
                            else
                            {
                                asz[i][0] = 0;
                            }
                        }

                        if (pDlgInstData->pParamsHeader->FuncIndex == lOpen)
                        {
                            dwAPIVersion = pDlgInstData->pParamsHeader->
                                aParams[3].dwValue;
                        }
                        else
                        {
#if TAPI_2_0
                            dwAPIVersion = 0x00020000;
#else
                            dwAPIVersion = 0x00010004;
#endif
                        }

                        if (dwAPIVersion < 0x00020000)
                        {
                            paramsHeader.dwNumParams = 18;
                            apXxxSize[8] = NULL;
                        }
#if TAPI_2_0
                        else if (pDlgInstData->pParamsHeader->FuncIndex ==
                                    lOpen)
                        {
                            paramsHeader.dwNumParams = 36;
                        }
                        else
                        {
                            paramsHeader.dwNumParams = 27;
                        }
#endif
                        if (DialogBoxParam(
                                ghInst,
                                (LPCSTR)MAKEINTRESOURCE(IDD_DIALOG2),
                                hwnd,
                                (DLGPROC) ParamsDlgProc,
                                (LPARAM) &paramsHeader
                                ))
                        {
                            LPDWORD lpdwXxx = &lpCP->dwBearerMode;


                            //
                            // Save the DWORD params
                            //

                            for (i = 0; i < 11; i++)
                            {
                                *(lpdwXxx + i) = params[i].dwValue;
                            }
#if TAPI_2_0
                            if (paramsHeader.dwNumParams > 18)
                            {
                                lpCP->dwPredictiveAutoTransferStates =
                                    params[18].dwValue;
                                lpCP->dwNoAnswerTimeout = params[25].dwValue;

                                if (paramsHeader.dwNumParams > 27)
                                {
                                    lpCP->dwDevSpecificSize =
                                        4 * params[27].dwValue;

                                    for (i = 0; i < 8; i++)
                                    {
                                        *(pdwProxyRequests + i) =
                                            params[28+i].dwValue;
                                    }
                                }
                            }
#endif

                            //
                            // Save the string params
                            //

                            for (i = 0; apXxxSize[i]; i++)
                            {
                                DWORD   length, index = adwStrParamIndices[i];


                                if (params[index].dwValue &&
                                    (params[index].dwValue != 0xffffffff))
                                {
#if TAPI_2_0
                                    if (gbWideStringParams)
                                    {
                                        length = MultiByteToWideChar(
                                            CP_ACP,
                                            MB_PRECOMPOSED,
                                            (LPCSTR) asz[i],
                                            -1,
                                            (LPWSTR) (p + *(apXxxSize[i] + 1)),
                                                                     // offset
                                            MAX_STRING_PARAM_SIZE/2
                                            );

                                        length *= sizeof (WCHAR);
                                    }
                                    else
                                    {
                                        strcpy(
                                            p + *(apXxxSize[i] + 1), // offset
                                            asz[i]
                                            );

                                        length = (DWORD) strlen (asz[i]) + 1;
                                    }
#else
                                    strcpy(
                                        p + *(apXxxSize[i] + 1), // offset
                                        asz[i]
                                        );

                                    length = (DWORD) strlen (asz[i]) + 1;
#endif
                                }
                                else
                                {
                                    length = 0;
                                }

                                *apXxxSize[i] = length;
                            }
                        }

                        //
                        // Strip off the "(valid pointer)" in the edit ctrl
                        //

                        sprintf (lpszComboText, "%08lx", lpCP);
                        PostMessage (hwnd, WM_USER+55, 0, 0);

                        pParamsHeader->aParams[lLastSel].dwValue =
                            (DWORD) lpCP;
                    }

                    break;
                }
                case PT_FORWARDLIST:
                {
                    if (lSel == 1)
                    {
                        char asz[MAX_LINEFORWARD_ENTRIES][2][MAX_STRING_PARAM_SIZE];
                        FUNC_PARAM params[] =
                        {
                            { "dwNumEntries",             PT_DWORD,  0, 0 },
                            { "[0].dwFowardMode",         PT_FLAGS,  0, aForwardModes },
                            { "[0].lpszCallerAddress",    PT_STRING, 0, asz[0][0] },
                            { "[0].dwDestCountryCode",    PT_DWORD,  0, 0 },
                            { "[0].lpszDestAddress",      PT_STRING, 0, asz[0][1] },
                            { "[1].dwFowardMode",         PT_FLAGS,  0, aForwardModes },
                            { "[1].lpszCallerAddress",    PT_STRING, 0, asz[1][0] },
                            { "[1].dwDestCountryCode",    PT_DWORD,  0, 0 },
                            { "[1].lpszDestAddress",      PT_STRING, 0, asz[1][1] },
                            { "[2].dwFowardMode",         PT_FLAGS,  0, aForwardModes },
                            { "[2].lpszCallerAddress",    PT_STRING, 0, asz[2][0] },
                            { "[2].dwDestCountryCode",    PT_DWORD,  0, 0 },
                            { "[2].lpszDestAddress",      PT_STRING, 0, asz[2][1] },
                            { "[3].dwFowardMode",         PT_FLAGS,  0, aForwardModes },
                            { "[3].lpszCallerAddress",    PT_STRING, 0, asz[3][0] },
                            { "[3].dwDestCountryCode",    PT_DWORD,  0, 0 },
                            { "[3].lpszDestAddress",      PT_STRING, 0, asz[3][1] },
                            { "[4].dwFowardMode",         PT_FLAGS,  0, aForwardModes },
                            { "[4].lpszCallerAddress",    PT_STRING, 0, asz[4][0] },
                            { "[4].dwDestCountryCode",    PT_DWORD,  0, 0 },
                            { "[4].lpszDestAddress",      PT_STRING, 0, asz[4][1] },

                        };
                        FUNC_PARAM_HEADER paramsHeader =
                            { 21, lForwardList, params, NULL };


                        memset(
                            asz,
                            0,
                            MAX_LINEFORWARD_ENTRIES*2*MAX_STRING_PARAM_SIZE
                            );

                        if (DialogBoxParam(
                                ghInst,
                                (LPCSTR)MAKEINTRESOURCE(IDD_DIALOG2),
                                hwnd,
                                (DLGPROC) ParamsDlgProc,
                                (LPARAM) &paramsHeader
                                ))
                        {

                            LPLINEFORWARDLIST lpForwardList =
                                (LPLINEFORWARDLIST)
                                    pParamsHeader->aParams[lLastSel].u.ptr;
                            LPLINEFORWARD lpEntry = lpForwardList->ForwardList;
                            DWORD dwNumEntriesToInit =
                                (params[0].dwValue > MAX_LINEFORWARD_ENTRIES ?
                                    MAX_LINEFORWARD_ENTRIES : params[0].dwValue);
                            DWORD i, dwFixedSize = sizeof(LINEFORWARDLIST) +
                                (MAX_LINEFORWARD_ENTRIES-1)*sizeof(LINEFORWARD);


                            lpForwardList->dwNumEntries = params[0].dwValue;

                            for (i = 0; i < dwNumEntriesToInit; i++)
                            {
                                lpEntry->dwForwardMode =
                                    params[1 + 4*i].dwValue;

                                if (params[2 + 4*i].dwValue &&
                                    params[2 + 4*i].dwValue != 0xffffffff)
                                {
                                    lpEntry->dwCallerAddressSize =
                                        strlen (asz[i][0]) + 1;

                                    lpEntry->dwCallerAddressOffset =
                                        dwFixedSize +
                                            2*i*MAX_STRING_PARAM_SIZE;
#if TAPI_2_0
                                    if (gbWideStringParams)
                                    {
                                        lpEntry->dwCallerAddressSize *=
                                            sizeof (WCHAR);

                                        MultiByteToWideChar(
                                            CP_ACP,
                                            MB_PRECOMPOSED,
                                            (LPCSTR) asz[i][0],
                                            -1,
                                            (LPWSTR) ((char *) lpForwardList +
                                                lpEntry->dwCallerAddressOffset),
                                            MAX_STRING_PARAM_SIZE / 2
                                            );
                                    }
                                    else
                                    {
                                        strcpy(
                                            (char *) lpForwardList +
                                                lpEntry->dwCallerAddressOffset,
                                            asz[i][0]
                                            );
                                    }
#else
                                    strcpy(
                                        (char *) lpForwardList +
                                            lpEntry->dwCallerAddressOffset,
                                        asz[i][0]
                                        );
#endif
                                }

                                lpEntry->dwDestCountryCode =
                                    params[3 + 4*i].dwValue;

                                if (params[4 + 4*i].dwValue &&
                                    params[4 + 4*i].dwValue != 0xffffffff)
                                {
                                    lpEntry->dwDestAddressSize =
                                        strlen (asz[i][1]) + 1;

                                    lpEntry->dwDestAddressOffset =
                                        dwFixedSize +
                                            (2*i + 1)*MAX_STRING_PARAM_SIZE;

#if TAPI_2_0
                                    if (gbWideStringParams)
                                    {
                                        lpEntry->dwDestAddressSize *=
                                            sizeof (WCHAR);

                                        MultiByteToWideChar(
                                            CP_ACP,
                                            MB_PRECOMPOSED,
                                            (LPCSTR) asz[i][1],
                                            -1,
                                            (LPWSTR) ((char *) lpForwardList +
                                                lpEntry->dwDestAddressOffset),
                                            MAX_STRING_PARAM_SIZE / 2
                                            );
                                    }
                                    else
                                    {
                                        strcpy(
                                            (char *) lpForwardList +
                                                lpEntry->dwDestAddressOffset,
                                            asz[i][1]
                                            );
                                    }
#else
                                    strcpy(
                                        (char *) lpForwardList +
                                            lpEntry->dwDestAddressOffset,
                                        asz[i][1]
                                        );
#endif
                                }

                                lpEntry++;
                            }
                        }

                        //
                        // Strip off the "(valid pointer)" in the edit ctrl
                        //

                        sprintf(
                            lpszComboText,
                            "%08lx",
                            pParamsHeader->aParams[lLastSel].u.ptr
                            );

                        PostMessage (hwnd, WM_USER+55, 0, 0);
                    }

                    break;
                }
                case PT_ORDINAL:

                    //
                    // The only option here is "select none"
                    //

                    strcpy (lpszComboText, "00000000");
                    PostMessage (hwnd, WM_USER+55, 0, 0);
                    break;

                } // switch

                break;
            }
            case CBN_EDITCHANGE:
            {
                //
                // If user entered text in the edit field then copy the
                // text to our buffer
                //

                if (pParamsHeader->aParams[lLastSel].dwType == PT_STRING)
                {
                    char buf[MAX_STRING_PARAM_SIZE];


                    GetDlgItemText(
                        hwnd,
                        IDC_COMBO1,
                        buf,
                        MAX_STRING_PARAM_SIZE
                        );

                    strncpy(
                        pParamsHeader->aParams[lLastSel].u.buf,
                        buf,
                        MAX_STRING_PARAM_SIZE
                        );

                    pParamsHeader->aParams[lLastSel].u.buf
                        [MAX_STRING_PARAM_SIZE-1] = 0;
                }
                break;
            }
            } // switch

        } // switch

        break;
    }
    case WM_USER+55:

        SetDlgItemText (hwnd, IDC_COMBO1, pDlgInstData->szComboText);
        break;

#ifdef WIN32
    case WM_CTLCOLORSTATIC:

        SetBkColor ((HDC) wParam, RGB (192,192,192));
        return (BOOL) GetStockObject (LTGRAY_BRUSH);
#else
    case WM_CTLCOLOR:
    {
        if (HIWORD(lParam) == CTLCOLOR_STATIC)
        {
            SetBkColor ((HDC) wParam, RGB (192,192,192));
            return (BOOL) GetStockObject (LTGRAY_BRUSH);
        }
        break;
    }
#endif
    case WM_PAINT:
    {
        PAINTSTRUCT ps;

        BeginPaint (hwnd, &ps);
        FillRect (ps.hdc, &ps.rcPaint, GetStockObject (LTGRAY_BRUSH));
        EndPaint (hwnd, &ps);

        break;
    }
    } // switch

    return FALSE;
}


BOOL
CALLBACK
IconDlgProc(
    HWND hwnd,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    static HICON hIcon;

    switch (msg)
    {
    case WM_INITDIALOG:

        hIcon = (HICON) lParam;

        break;

    case WM_COMMAND:

        switch (LOWORD(wParam))
        {
        case IDOK:

            EndDialog (hwnd, 0);
            break;
        }
        break;

    case WM_PAINT:
    {
        PAINTSTRUCT ps;

        BeginPaint (hwnd, &ps);
        FillRect (ps.hdc, &ps.rcPaint, GetStockObject (LTGRAY_BRUSH));
#ifdef WIN32
        MoveToEx (ps.hdc, 6, 6, (LPPOINT) NULL);
#else
        MoveTo (ps.hdc, 6, 6);
#endif
        LineTo (ps.hdc, 42, 6);
        LineTo (ps.hdc, 42, 42);
        LineTo (ps.hdc, 6, 42);
        LineTo (ps.hdc, 6, 6);
        DrawIcon (ps.hdc, 8, 8, hIcon);
        EndPaint (hwnd, &ps);

        break;
    }
    }

    return FALSE;
}


BOOL
CALLBACK
UserButtonsDlgProc(
    HWND hwnd,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    static int iButtonIndex;

    switch (msg)
    {
    case WM_INITDIALOG:
    {
        int i;
        char buf[32];

        if (lParam)
        {
            //
            // The dlg was invoked because someone pressed a user button
            // that was uninitialized, so only allow chgs on this button
            //

            iButtonIndex = *((int *) lParam);

            _itoa (iButtonIndex + 1, buf, 10);

            SendDlgItemMessage(
                hwnd,
                IDC_LIST1,
                LB_INSERTSTRING,
                (WPARAM) -1,
                (LPARAM) buf
                );
        }
        else
        {
            //
            // The dlg was invoked because the user chose a menuitem,
            // so allow chgs on all buttons
            //

            iButtonIndex = MAX_USER_BUTTONS;

            for (i = 1; i <= MAX_USER_BUTTONS; i++)
            {
                _itoa (i, buf, 10);

                SendDlgItemMessage(
                    hwnd,
                    IDC_LIST1,
                    LB_INSERTSTRING,
                    (WPARAM) -1,
                    (LPARAM) buf
                    );
            }

        }

        SendDlgItemMessage(
            hwnd,
            IDC_LIST1,
            LB_SETCURSEL,
            (WPARAM) 0,
            0
            );

        for (i = 0; aFuncNames[i]; i++)
        {
            SendDlgItemMessage(
                hwnd,
                IDC_LIST2,
                LB_INSERTSTRING,
                (WPARAM) -1,
                (LPARAM) aFuncNames[i]
                );
        }

        SendDlgItemMessage(
            hwnd,
            IDC_LIST2,
            LB_INSERTSTRING,
            (WPARAM) -1,
            (LPARAM) "<none>"
            );

        if (!lParam)
        {
#ifdef WIN32
            wParam = (WPARAM) MAKELONG (0, LBN_SELCHANGE);
#else
            lParam = (LPARAM) MAKELONG (0, LBN_SELCHANGE);
#endif
            goto IDC_LIST1_selchange;
        }

        break;
    }
    case WM_COMMAND:

        switch (LOWORD(wParam))
        {
        case IDOK:
        {
            LONG lFuncSel;


            lFuncSel = SendDlgItemMessage(hwnd, IDC_LIST2, LB_GETCURSEL, 0, 0);

            if (lFuncSel == LB_ERR)
            {
                MessageBox (hwnd, "Select a function", "", MB_OK);
                break;
            }

            if (iButtonIndex == MAX_USER_BUTTONS)
            {
                iButtonIndex = (int) SendDlgItemMessage(
                    hwnd,
                    IDC_LIST1,
                    LB_GETCURSEL,
                    0,
                    0
                    );
            }

            aUserButtonFuncs[iButtonIndex] = (DWORD) lFuncSel;

            if (lFuncSel == MiscBegin)
            {
                //
                // User selected "<none>" option so nullify string
                //

                aUserButtonsText[iButtonIndex][0] = 0;
            }
            else
            {
                GetDlgItemText(
                    hwnd,
                    IDC_EDIT1,
                    (LPSTR) &aUserButtonsText[iButtonIndex],
                    MAX_USER_BUTTON_TEXT_SIZE - 1
                    );

                aUserButtonsText[iButtonIndex][MAX_USER_BUTTON_TEXT_SIZE - 1] =
                    0;
            }

            SetDlgItemText(
                ghwndMain,
                IDC_BUTTON13 + iButtonIndex,
                (LPSTR) &aUserButtonsText[iButtonIndex]
                );

            // Fall thru to IDCANCEL code
        }
        case IDCANCEL:

            EndDialog (hwnd, FALSE);
            break;

        case IDC_LIST1:

IDC_LIST1_selchange:

#ifdef WIN32
            if (HIWORD(wParam) == LBN_SELCHANGE)
#else
            if (HIWORD(lParam) == LBN_SELCHANGE)
#endif
            {
                LONG lButtonSel =
                    SendDlgItemMessage(hwnd, IDC_LIST1, LB_GETCURSEL, 0, 0);


                SendDlgItemMessage(
                    hwnd,
                    IDC_LIST2,
                    LB_SETCURSEL,
                    (WPARAM) aUserButtonFuncs[lButtonSel],
                    0
                    );

                SetDlgItemText(
                    hwnd,
                    IDC_EDIT1,
                    aUserButtonsText[lButtonSel]
                    );
            }
            break;

        } // switch

        break;

#ifdef WIN32
    case WM_CTLCOLORSTATIC:

        SetBkColor ((HDC) wParam, RGB (192,192,192));
        return (BOOL) GetStockObject (LTGRAY_BRUSH);
#else
    case WM_CTLCOLOR:
    {
        if (HIWORD(lParam) == CTLCOLOR_STATIC)
        {
            SetBkColor ((HDC) wParam, RGB (192,192,192));
            return (BOOL) GetStockObject (LTGRAY_BRUSH);
        }
        break;
    }
#endif
    case WM_PAINT:
    {
        PAINTSTRUCT ps;

        BeginPaint (hwnd, &ps);
        FillRect (ps.hdc, &ps.rcPaint, GetStockObject (LTGRAY_BRUSH));
        EndPaint (hwnd, &ps);

        break;
    }
    } // switch

    return FALSE;
}