home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 10 / ioProg_10.iso / soft / platsdk / inetwork.exe / TAPI-S.cab / 36TAPICODE.C < prev    next >
Encoding:
C/C++ Source or Header  |  1997-10-05  |  145.9 KB  |  4,584 lines

  1. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  2. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  3. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  4. // PARTICULAR PURPOSE.
  5. //
  6. // Copyright (C) 1995-1997  Microsoft Corporation.  All Rights Reserved.
  7. //
  8. //  MODULE: TapiCode.c
  9. //
  10. //  PURPOSE: Handles all the TAPI routines for TapiComm.
  11. //
  12. //
  13. //  EXPORTED FUNCTIONS:  These functions are for use by other modules.
  14. //
  15. //    InitializeTAPI    - Initialize this app with TAPI.
  16. //    ShutdownTAPI      - Shutdown this app from TAPI.
  17. //    DialCall          - Dial a Call.
  18. //    HangupCall        - Hangup an existing Call.
  19. //    PostHangupCall    - Posts a HangupCall message to the main window.
  20. //
  21. //  INTERNAL FUNCTIONS:  These functions are for this module only.
  22. //
  23. //    DialCallInParts           - Actually Dial the call.
  24. //
  25. //    lineCallbackFunc          - TAPI callback for async messages.
  26. //
  27. //    CheckAndReAllocBuffer     - Helper function for I_ wrappers functions.
  28. //
  29. //    I_lineNegotiateAPIVersion - Wrapper for lineNegotiateAPIVersion.
  30. //    I_lineGetDevCaps          - Wrapper for lineGetDevCaps.
  31. //    I_lineGetAddressStatus    - Wrapper for lineGetAddressStatus.
  32. //    I_lineTranslateAddress    - Wrapper for lineTranslateAddress.
  33. //    I_lineGetCallStatus       - Wrapper for lineGetCallStatus.
  34. //    I_lineGetAddressCaps      - Wrapper for lineGetAddressCaps.
  35. //
  36. //    WaitForCallState          - Resynchronize by Waiting for a CallState.
  37. //    WaitForReply              - Resynchronize by Waiting for a LINE_REPLY.
  38. //
  39. //    DoLineReply               - Handle asynchronous LINE_REPLY.
  40. //    DoLineClose               - Handle asynchronous LINE_CLOSE.
  41. //    DoLineDevState            - Handle asynchronous LINE_LINEDEVSTATE.
  42. //    DoLineCallState           - Handle asynchronous LINE_CALLSTATE.
  43. //    DoLineCreate              - Handle asynchronous LINE_CREATE.
  44. //
  45. //    HandleLineErr             - Handler for most LINEERR errors.
  46. //
  47. //    HandleIniFileCorrupt      - LINEERR handler for INIFILECORRUPT.
  48. //    HandleNoDriver            - LINEERR handler for NODRIVER.
  49. //    HandleNoDevicesInstalled  - LINEERR handler for NODEVICE.
  50. //    HandleReInit              - LINEERR handler for REINIT.
  51. //    HandleNoMultipleInstance  - LINEERR handler for NOMULTIPLEINSTANCE.
  52. //    HandleNoMem               - LINEERR handler for NOMEM.
  53. //    HandleOperationFailed     - LINEERR handler for OPERATIONFAILED.
  54. //    HandleResourceUnavail     - LINEERR handler for RESOURCEUNAVAIL.
  55. //
  56. //    LaunchModemControlPanelAdd - Launches the Modem Control Panel.
  57. //
  58. //    WarningBox                - Warn user if a line in use is removed.
  59. //
  60. //    GetAddressToDial          - Launches a GetAddressToDial dialog.
  61. //    DialDialogProc            - Dialog Proc for the GetAddressToDial API.
  62. //    
  63. //    I_lineNegotiateLegacyAPIVersion - Wrapper to negoitiate with legacy TSPs
  64. //    VerifyUsableLine          - Verify that a line device is usable
  65. //    FillTAPILine              - Fill a combobox with TAPI Device names
  66. //    VerifyAndWarnUsableLine   - Verify and warn if a line device is usable
  67. //    FillCountryCodeList       - Fill a combobox with country codes
  68. //    FillLocationInfo          - Fill a combobox with current TAPI locations
  69. //    UseDialingRules           - Enable/Disable dialing rules controls 
  70. //    DisplayPhoneNumber        - Create and display a valid phone number
  71. //    PreConfigureDevice        - Preconfigure a device line
  72.  
  73.  
  74. #include <tapi.h>
  75. #include <windows.h>
  76. #include <string.h>
  77. #include "globals.h"
  78. #include "TapiInfo.h"
  79. #include "TapiCode.h"
  80. #include "CommCode.h"   
  81. #include "resource.h"
  82. #include "statbar.h"
  83. #include "toolbar.h"
  84.  
  85.  
  86. // All TAPI line functions return 0 for SUCCESS, so define it.
  87. #define SUCCESS 0
  88.  
  89. // Possible return error for resynchronization functions.
  90. #define WAITERR_WAITABORTED  1
  91. #define WAITERR_WAITTIMEDOUT 2
  92.  
  93. // Reasons why a line device might not be usable by TapiComm.
  94. #define LINENOTUSEABLE_ERROR            1
  95. #define LINENOTUSEABLE_NOVOICE          2
  96. #define LINENOTUSEABLE_NODATAMODEM      3
  97. #define LINENOTUSEABLE_NOMAKECALL       4
  98. #define LINENOTUSEABLE_ALLOCATED        5
  99. #define LINENOTUSEABLE_INUSE            6
  100. #define LINENOTUSEABLE_NOCOMMDATAMODEM  7
  101.  
  102. // Constant used in WaitForCallState when any new
  103. // callstate message is acceptable.
  104. #define I_LINECALLSTATE_ANY 0
  105.  
  106.  // Wait up to 30 seconds for an async completion.
  107. #define WAITTIMEOUT 30000
  108.  
  109. // TAPI version that this sample is designed to use.
  110. #define SAMPLE_TAPI_VERSION 0x00010004
  111.  
  112.  
  113. // Global TAPI variables.
  114. HWND     g_hWndMainWindow = NULL;   // Apps main window.
  115. HWND     g_hDlgParentWindow = NULL; // This will be the parent of all dialogs.
  116. HLINEAPP g_hLineApp = NULL;
  117. DWORD    g_dwNumDevs = 0;
  118.  
  119. // Global variable that holds the handle to a TAPI dialog
  120. // that needs to be dismissed if line conditions change.
  121. HWND g_hDialog = NULL;
  122.  
  123. // Global flags to prevent re-entrancy problems.
  124. BOOL g_bShuttingDown = FALSE;
  125. BOOL g_bStoppingCall = FALSE;
  126. BOOL g_bInitializing = FALSE;
  127.  
  128.  
  129. // This sample only supports one call in progress at a time.
  130. BOOL g_bTapiInUse = FALSE;
  131.  
  132.  
  133. // Data needed per call.  This sample only supports one call.
  134. HCALL g_hCall = NULL;
  135. HLINE g_hLine = NULL;
  136. DWORD g_dwDeviceID = 0;
  137. DWORD g_dwAPIVersion = 0;
  138. DWORD g_dwCallState = 0;
  139. char  g_szDisplayableAddress[1024] = "";
  140. char  g_szDialableAddress[1024] = "";
  141. BOOL  g_bConnected = FALSE;
  142. LPVOID g_lpDeviceConfig = NULL;
  143. DWORD g_dwSizeDeviceConfig;
  144.  
  145. // Global variables to allow us to do various waits.
  146. BOOL  g_bReplyRecieved;
  147. DWORD g_dwRequestedID;
  148. long  g_lAsyncReply;
  149. BOOL  g_bCallStateReceived;
  150.  
  151. // Structures needed to handle special non-dialable characters.
  152. #define g_sizeofNonDialable (sizeof(g_sNonDialable)/sizeof(g_sNonDialable[0]))
  153.  
  154. typedef struct {
  155.     LONG lError;
  156.     DWORD dwDevCapFlag;
  157.     LPSTR szToken;
  158.     LPSTR szMsg;
  159. } NONDIALTOKENS;
  160.  
  161. NONDIALTOKENS g_sNonDialable[] = {
  162.     {LINEERR_DIALBILLING,  LINEDEVCAPFLAGS_DIALBILLING,  "$", 
  163.             "Wait for the credit card bong tone" },
  164.     {LINEERR_DIALDIALTONE, LINEDEVCAPFLAGS_DIALDIALTONE, "W", 
  165.             "Wait for the second dial tone" },
  166.     {LINEERR_DIALDIALTONE, LINEDEVCAPFLAGS_DIALDIALTONE, "w", 
  167.             "Wait for the second dial tone" },
  168.     {LINEERR_DIALQUIET,    LINEDEVCAPFLAGS_DIALQUIET,    "@", 
  169.             "Wait for the remote end to answer" },
  170.     {LINEERR_DIALPROMPT,   0,                            "?", 
  171.             "Press OK when you are ready to continue dialing"},
  172. };
  173.  
  174. // "Dial" dialog controls and their associated help page IDs
  175. DWORD g_adwSampleMenuHelpIDs[] = 
  176. {
  177.     IDC_COUNTRYCODE          , IDC_COUNTRYCODE,
  178.     IDC_STATICCOUNTRYCODE    , IDC_COUNTRYCODE,
  179.     IDC_AREACODE             , IDC_AREACODE,
  180.     IDC_STATICAREACODE       , IDC_AREACODE,
  181.     IDC_PHONENUMBER          , IDC_PHONENUMBER,
  182.     IDC_STATICPHONENUMBER    , IDC_PHONENUMBER,
  183.     IDC_USEDIALINGRULES      , IDC_USEDIALINGRULES,
  184.     IDC_LOCATION             , IDC_LOCATION,
  185.     IDC_STATICLOCATION       , IDC_LOCATION,
  186.     IDC_CALLINGCARD          , IDC_CALLINGCARD,
  187.     IDC_STATICCALLINGCARD    , IDC_CALLINGCARD,
  188.     IDC_DIALINGPROPERTIES    , IDC_DIALINGPROPERTIES,
  189.     IDC_TAPILINE             , IDC_TAPILINE,
  190.     IDC_STATICTAPILINE       , IDC_TAPILINE,
  191.     IDC_CONFIGURELINE        , IDC_CONFIGURELINE,
  192.     IDC_CANONICALNUMBER      , IDC_CANONICALNUMBER,
  193.     IDC_STATICCANONICAL      , IDC_CANONICALNUMBER,
  194.     IDC_DIALABLENUMBER       , IDC_DIALABLENUMBER,
  195.     IDC_STATICDIALABLE       , IDC_DIALABLENUMBER,
  196.     IDC_DISPLAYABLENUMBER    , IDC_DISPLAYABLENUMBER,
  197.     IDC_STATICDISPLAYABLE    , IDC_DISPLAYABLENUMBER,
  198.     IDC_DIAL                 , IDC_DIAL,
  199.     IDC_LINEICON             , IDC_LINEICON,
  200.     //IDC_STATICWHERETODIAL    , IDC_STATICWHERETODIAL,
  201.     //IDC_STATICHOWTODIAL      , IDC_STATICHOWTODIAL,
  202.     //IDC_STATICCONNECTUSING   , IDC_STATICCONNECTUSING,
  203.     //IDC_STATICPHONENUMBER    , IDC_PHONENUMBER,
  204.     0,0
  205. };
  206.  
  207. //**************************************************
  208. // Prototypes for functions used only in this module.
  209. //**************************************************
  210.  
  211. BOOL DialCallInParts (
  212.     LPLINEDEVCAPS lpLineDevCaps,
  213.     LPCSTR lpszAddress,
  214.     LPCSTR lpszDisplayableAddress);
  215.  
  216. LPLINECALLPARAMS CreateCallParams (
  217.     LPLINECALLPARAMS lpCallParams,
  218.     LPCSTR lpszDisplayableAddress);
  219.  
  220. DWORD I_lineNegotiateAPIVersion (
  221.     DWORD dwDeviceID);
  222.  
  223. LPVOID CheckAndReAllocBuffer(
  224.     LPVOID lpBuffer, size_t sizeBufferMinimum, 
  225.     LPCSTR szApiPhrase);
  226.  
  227. LPLINEDEVCAPS I_lineGetDevCaps (
  228.     LPLINEDEVCAPS lpLineDevCaps,
  229.     DWORD dwDeviceID,
  230.     DWORD dwAPIVersion);
  231.  
  232. LPLINEADDRESSSTATUS I_lineGetAddressStatus (
  233.     LPLINEADDRESSSTATUS lpLineAddressStatus,
  234.     HLINE hLine,
  235.     DWORD dwAddressID);
  236.  
  237. LPLINETRANSLATEOUTPUT I_lineTranslateAddress (
  238.     LPLINETRANSLATEOUTPUT lpLineTranslateOutput,
  239.     DWORD dwDeviceID,
  240.     DWORD dwAPIVersion,
  241.     LPCSTR lpszDialAddress);
  242.  
  243. LPLINECALLSTATUS I_lineGetCallStatus (
  244.     LPLINECALLSTATUS lpLineCallStatus,
  245.     HCALL hCall);
  246.  
  247. LPLINEADDRESSCAPS I_lineGetAddressCaps (
  248.     LPLINEADDRESSCAPS lpLineAddressCaps,
  249.     DWORD dwDeviceID, DWORD dwAddressID,
  250.     DWORD dwAPIVersion, DWORD dwExtVersion);
  251.  
  252. long WaitForCallState (DWORD dwNewCallState);
  253.  
  254. long WaitForReply (long lRequestID);
  255.  
  256. void CALLBACK lineCallbackFunc(
  257.     DWORD hDevice, DWORD dwMsg, DWORD dwCallbackInstance, 
  258.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);
  259.  
  260. void DoLineReply(
  261.     DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
  262.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);
  263. void DoLineClose(
  264.     DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
  265.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);
  266. void DoLineDevState(
  267.     DWORD dwDevice, DWORD dwsg, DWORD dwCallbackInstance,
  268.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);
  269. void DoLineCallState(
  270.     DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
  271.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);
  272. void DoLineCreate(
  273.     DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
  274.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);
  275.  
  276. BOOL HandleLineErr(long lLineErr);
  277.  
  278. BOOL HandleIniFileCorrupt();
  279. BOOL HandleNoDriver();
  280. BOOL HandleNoDevicesInstalled();
  281. BOOL HandleReInit();
  282. BOOL HandleNoMultipleInstance();
  283. BOOL HandleNoMem();
  284. BOOL HandleOperationFailed();
  285. BOOL HandleResourceUnavail();
  286.  
  287. BOOL LaunchModemControlPanelAdd();
  288.  
  289. void WarningBox(LPCSTR lpszMessage);
  290.  
  291. BOOL CALLBACK DialDialogProc(
  292.     HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  293.  
  294. BOOL GetAddressToDial();
  295.  
  296. DWORD I_lineNegotiateLegacyAPIVersion(DWORD dwDeviceID);
  297. long VerifyUsableLine(DWORD dwDeviceID);
  298. void FillTAPILine(HWND hwndDlg);
  299. BOOL VerifyAndWarnUsableLine(HWND hwndDlg);
  300. void FillCountryCodeList(HWND hwndDlg, DWORD dwDefaultCountryID);
  301. void FillLocationInfo(HWND hwndDlg, LPSTR lpszCurrentLocation, 
  302.     LPDWORD lpdwCountryID, LPSTR lpszAreaCode);
  303. void UseDialingRules(HWND hwndDlg);
  304. void DisplayPhoneNumber(HWND hwndDlg);
  305. void PreConfigureDevice(HWND hwndDlg, DWORD dwDeviceID);
  306.  
  307.  
  308. //**************************************************
  309. // Entry points from the UI
  310. //**************************************************
  311.  
  312.  
  313. //
  314. //  FUNCTION: BOOL InitializeTAPI(HWND)
  315. //
  316. //  PURPOSE: Initializes TAPI
  317. //
  318. //  PARAMETERS:
  319. //    hWndParent - Window to use as parent of any dialogs.
  320. //
  321. //  RETURN VALUE:
  322. //    Always returns 0 - command handled.
  323. //
  324. //  COMMENTS:
  325. //
  326. //    This is the API that initializes the app with TAPI.
  327. //    If NULL is passed for the hWndParent, then its assumed
  328. //    that re-initialization has occurred and the previous hWnd
  329. //    is used.
  330. //
  331. //
  332.  
  333. BOOL InitializeTAPI(HWND hWndParent)
  334. {
  335.     long lReturn;
  336.     BOOL bTryReInit = TRUE;
  337.  
  338.     // If we're already initialized, then initialization succeeds.
  339.     if (g_hLineApp)
  340.         return TRUE;
  341.  
  342.     // If we're in the middle of initializing, then fail, we're not done.
  343.     if (g_bInitializing)
  344.         return FALSE;
  345.  
  346.     g_bInitializing = TRUE;
  347.  
  348.     // Initialize TAPI
  349.     do
  350.     {
  351.         lReturn = lineInitialize(&g_hLineApp, hInst, 
  352.             lineCallbackFunc, "TapiComm", &g_dwNumDevs);
  353.  
  354.         // If we get this error, its because some other app has yet
  355.         // to respond to the REINIT message.  Wait 5 seconds and try
  356.         // again.  If it still doesn't respond, tell the user.
  357.         if (lReturn == LINEERR_REINIT)
  358.         {
  359.             if (bTryReInit)
  360.             {
  361.                 MSG msg; 
  362.                 DWORD dwTimeStarted;
  363.  
  364.                 dwTimeStarted = GetTickCount();
  365.  
  366.                 while(GetTickCount() - dwTimeStarted < 5000)
  367.                 {
  368.                     if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
  369.                     {
  370.                         TranslateMessage(&msg);
  371.                         DispatchMessage(&msg);
  372.                     }
  373.                 }
  374.             
  375.                 bTryReInit = FALSE;
  376.                 continue;
  377.             }
  378.             else
  379.             {
  380.                 MessageBox(g_hDlgParentWindow,
  381.                     "A change to the system configuration requires that "
  382.                     "all Telephony applications relinquish their use of "
  383.                     "Telephony before any can progress.  "
  384.                     "Some have not yet done so."
  385.                     ,"Warning",MB_OK);
  386.                 g_bInitializing = FALSE;
  387.                 return FALSE;
  388.             }
  389.         }
  390.  
  391.         if (lReturn == LINEERR_NODEVICE)
  392.         {
  393.             if (HandleNoDevicesInstalled())
  394.                 continue;
  395.             else
  396.             {
  397.                 OutputDebugString("No devices installed.\n");
  398.                 g_bInitializing = FALSE;
  399.                 return FALSE;
  400.             }
  401.         }
  402.  
  403.         if (HandleLineErr(lReturn))
  404.             continue;
  405.         else
  406.         {
  407.             OutputDebugLineError(lReturn, 
  408.                 "lineInitialize unhandled error: ");
  409.             g_bInitializing = FALSE;
  410.             return FALSE;
  411.         }
  412.     }
  413.     while(lReturn != SUCCESS);
  414.  
  415.     // if hWndParent is a valid hWnd, we keep it as the parent for
  416.     // all dialogs.
  417.     if (IsWindow(hWndParent))
  418.     {
  419.         g_hDlgParentWindow = g_hWndMainWindow = hWndParent;
  420.     }
  421.     else
  422.     {
  423.         // Has the old g_hWndMainWindow gone away?
  424.         if (!IsWindow(g_hWndMainWindow))
  425.         {
  426.             OutputDebugString("Main window unavailable.\n");
  427.             g_hDlgParentWindow = g_hWndMainWindow = NULL;
  428.         }
  429.     }
  430.  
  431.     g_hCall = NULL;
  432.     g_hLine = NULL;
  433.  
  434.     OutputDebugString("Tapi initialized.\n");
  435.     g_bInitializing = FALSE;
  436.     return TRUE;
  437. }
  438.  
  439.  
  440. //
  441. //  FUNCTION: BOOL ShutdownTAPI()
  442. //
  443. //  PURPOSE: Shuts down all use of TAPI
  444. //
  445. //  PARAMETERS:
  446. //    None
  447. //
  448. //  RETURN VALUE:
  449. //    True if TAPI successfully shut down.
  450. //
  451. //  COMMENTS:
  452. //
  453. //    If ShutdownTAPI fails, then its likely either a problem
  454. //    with the service provider (and might require a system
  455. //    reboot to correct) or the application ran out of memory.
  456. //
  457. //
  458.  
  459. BOOL ShutdownTAPI()
  460. {
  461.     long lReturn;
  462.  
  463.     // If we aren't initialized, then Shutdown is unnecessary.
  464.     if (g_hLineApp == NULL)
  465.         return TRUE;
  466.  
  467.     // Prevent ShutdownTAPI re-entrancy problems.
  468.     if (g_bShuttingDown)
  469.         return TRUE;
  470.  
  471.     g_bShuttingDown = TRUE;
  472.  
  473.     HangupCall();
  474.     
  475.     do
  476.     {
  477.         lReturn = lineShutdown(g_hLineApp);
  478.         if (HandleLineErr(lReturn))
  479.             continue;
  480.         else
  481.         {
  482.             OutputDebugLineError(lReturn, "lineShutdown unhandled error: ");
  483.             break;
  484.         }
  485.     }
  486.     while(lReturn != SUCCESS);
  487.  
  488.     g_bTapiInUse = FALSE;
  489.     g_bConnected = FALSE;
  490.     g_hLineApp = NULL;
  491.     g_hCall = NULL;
  492.     g_hLine = NULL;
  493.     g_bShuttingDown = FALSE;
  494.     OutputDebugString("TAPI uninitialized.\n");
  495.     return TRUE;
  496. }
  497.  
  498.  
  499.  
  500. //
  501. //  FUNCTION: BOOL HangupCall()
  502. //
  503. //  PURPOSE: Hangup the call in progress if it exists.
  504. //
  505. //  PARAMETERS:
  506. //    none
  507. //
  508. //  RETURN VALUE:
  509. //    TRUE if call hung up successfully.
  510. //
  511. //  COMMENTS:
  512. //
  513. //    If HangupCall fails, then its likely either a problem
  514. //    with the service provider (and might require a system
  515. //    reboot to correct) or the application ran out of memory.
  516. //
  517. //
  518.  
  519. BOOL HangupCall()
  520. {         
  521.     LPLINECALLSTATUS pLineCallStatus = NULL;
  522.     long lReturn;
  523.  
  524.     // Prevent HangupCall re-entrancy problems.
  525.     if (g_bStoppingCall)
  526.         return TRUE;
  527.  
  528.     // if the 'Call' dialog is up, dismiss it.
  529.     if (g_hDialog)
  530.         PostMessage(g_hDialog, WM_COMMAND, IDCANCEL, 0);
  531.  
  532.     // If Tapi is not being used right now, then the call is hung up.
  533.     if (!g_bTapiInUse)
  534.         return TRUE;
  535.  
  536.     g_bStoppingCall = TRUE;
  537.     OutputDebugString("Stopping Call in progress\n");
  538.  
  539.     // Disable the 'hangup call' user interface.
  540.     EnableHangupCall(g_hWndMainWindow, FALSE);
  541.  
  542.     // Stop any data communications on the comm port.
  543.     StopComm();
  544.  
  545.     // If there is a call in progress, drop and deallocate it.
  546.     if (g_hCall)
  547.     {
  548.         // I_lineGetCallStatus returns a LocalAlloc()d buffer
  549.         pLineCallStatus = I_lineGetCallStatus(pLineCallStatus, g_hCall);
  550.         if (pLineCallStatus == NULL)
  551.         {
  552.             ShutdownTAPI();
  553.             g_bStoppingCall = FALSE;
  554.             return FALSE;
  555.         }
  556.  
  557.         // Only drop the call when the line is not IDLE.
  558.         if (!((pLineCallStatus -> dwCallState) & LINECALLSTATE_IDLE))
  559.         {
  560.             do
  561.             {
  562.                 lReturn = WaitForReply(lineDrop(g_hCall, NULL, 0));
  563.  
  564.                 if (lReturn == WAITERR_WAITTIMEDOUT)
  565.                 {
  566.                     OutputDebugString("Call timed out in WaitForReply.\n");
  567.                     break;
  568.                 }
  569.  
  570.                 if (lReturn == WAITERR_WAITABORTED)
  571.                 {
  572.                     OutputDebugString("lineDrop: WAITERR_WAITABORTED.\n");
  573.                     break;
  574.                 }
  575.  
  576.                 // Was the call already in IDLE?
  577.                 if (lReturn == LINEERR_INVALCALLSTATE)
  578.                     break;
  579.  
  580.                 if (HandleLineErr(lReturn))
  581.                     continue;
  582.                 else
  583.                 {
  584.                     OutputDebugLineError(lReturn, 
  585.                         "lineDrop unhandled error: ");
  586.                     break;
  587.                 }
  588.             }
  589.             while(lReturn != SUCCESS);
  590.  
  591.             // Wait for the dropped call to go IDLE before continuing.
  592.             lReturn = WaitForCallState(LINECALLSTATE_IDLE);
  593.  
  594.             if (lReturn == WAITERR_WAITTIMEDOUT)
  595.                 OutputDebugString("Call timed out waiting for IDLE state.\n");
  596.  
  597.             if (lReturn == WAITERR_WAITABORTED)
  598.                 OutputDebugString(
  599.                     "WAITERR_WAITABORTED while waiting for IDLE state.\n");
  600.  
  601.             OutputDebugString("Call Dropped.\n");
  602.         }
  603.  
  604.         // The call is now idle.  Deallocate it!
  605.         do
  606.         {
  607.             lReturn = lineDeallocateCall(g_hCall);
  608.             if (HandleLineErr(lReturn))
  609.                 continue;
  610.             else
  611.             {
  612.                 OutputDebugLineError(lReturn,
  613.                     "lineDeallocateCall unhandled error: ");
  614.                 break;
  615.             }
  616.         }
  617.         while(lReturn != SUCCESS);
  618.             
  619.         OutputDebugString("Call Deallocated.\n");
  620.     }
  621.  
  622.  
  623.     // if we have a line open, close it.
  624.     if (g_hLine)
  625.     {
  626.         do
  627.         {
  628.             lReturn = lineClose(g_hLine);
  629.             if (HandleLineErr(lReturn))
  630.                 continue;
  631.             else
  632.             {
  633.                 OutputDebugLineError(lReturn, 
  634.                     "lineClose unhandled error: ");
  635.                 break;
  636.             }
  637.         }
  638.         while(lReturn != SUCCESS);
  639.         
  640.         OutputDebugString("Line Closed.\n");
  641.     }
  642.  
  643.     // Call and Line are taken care of.  Finish cleaning up.
  644.  
  645.     // If there is device configuration information, free the memory.
  646.     if (g_lpDeviceConfig)
  647.         LocalFree(g_lpDeviceConfig);
  648.     g_lpDeviceConfig = NULL;
  649.  
  650.     g_hCall = NULL;
  651.     g_hLine = NULL;
  652.     g_bConnected = FALSE;
  653.  
  654.     g_bTapiInUse = FALSE;
  655.     g_bStoppingCall = FALSE; // allow HangupCall to be called again.
  656.     OutputDebugString("Call stopped\n");
  657.  
  658.     // Update the user interface.
  659.     UpdateStatusBar("Ready to make a call.",1,0);
  660.     EnableMakeCall(g_hWndMainWindow, TRUE);
  661.  
  662.     // Need to free LocalAlloc()d buffer returned from I_lineGetCallStatus
  663.     if (pLineCallStatus)
  664.         LocalFree(pLineCallStatus);  
  665.         
  666.     return TRUE;
  667. }
  668.  
  669.  
  670. //
  671. //  FUNCTION: PostHangupCall()
  672. //
  673. //  PURPOSE: Posts a message to the main TAPI thread to hangup the call.
  674. //
  675. //  PARAMETERS:
  676. //    none
  677. //
  678. //  RETURN VALUE:
  679. //    none
  680. //
  681. //  COMMENTS:
  682. //
  683. //    TAPI is thread specific, meaning that only the thread that does the
  684. //    lineInitialize can get asynchronous messages through the callback.
  685. //    Since the HangupCall can potentially go into a loop waiting for 
  686. //    specific events, any other threads that call HangupCall can cause
  687. //    timing confusion.  Best to just have other threads 'ask' the main thread
  688. //    to hangup the call.
  689. //
  690.  
  691. void PostHangupCall()
  692. {
  693.     PostMessage(g_hWndMainWindow, WM_COMMAND, IDM_HANGUPCALL, 0);
  694. }
  695.  
  696.  
  697.  
  698. //
  699. //  FUNCTION: DialCall()
  700. //
  701. //  PURPOSE: Get a number from the user and dial it.
  702. //
  703. //  PARAMETERS:
  704. //    none
  705. //
  706. //  RETURN VALUE:
  707. //    TRUE if able to get a number, find a line, and dial successfully.
  708. //
  709. //  COMMENTS:
  710. //
  711. //    This function makes several assumptions:
  712. //    - The number dialed will always explicitly come from the user.
  713. //    - There will only be one outgoing address per line.
  714. //
  715.  
  716. BOOL DialCall()
  717. {
  718.     long lReturn;
  719.     LPLINEADDRESSSTATUS lpLineAddressStatus = NULL;
  720.     LPLINEDEVCAPS lpLineDevCaps = NULL;
  721.  
  722.     if (g_bTapiInUse)
  723.     {
  724.         OutputDebugString("A call is already being handled\n");
  725.         return FALSE;
  726.     }
  727.  
  728.     // If TAPI isn't initialized, its either because we couldn't initialize
  729.     // at startup (and this might have been corrected by now), or because
  730.     // a REINIT event was received.  In either case, try to init now.
  731.  
  732.     if (!g_hLineApp)
  733.     {
  734.         if (!InitializeTAPI(NULL))
  735.             return FALSE;
  736.     }
  737.  
  738.     // If there are no line devices installed on the machine, lets give
  739.     // the user the opportunity to install one.
  740.     if (g_dwNumDevs < 1)
  741.     {
  742.         if (!HandleNoDevicesInstalled())
  743.             return FALSE;
  744.     }
  745.  
  746.     // We now have a call active.  Prevent future calls.
  747.     g_bTapiInUse = TRUE;
  748.     EnableMakeCall(g_hWndMainWindow, FALSE);
  749.  
  750.  
  751.     // Get a phone number from the user.
  752.     // Phone number will be placed in global variables if successful
  753.     if (!GetAddressToDial())
  754.     {
  755.         HangupCall();
  756.         goto DeleteBuffers;
  757.     }
  758.  
  759.     // Negotiate the API version to use for this device.
  760.     g_dwAPIVersion = I_lineNegotiateAPIVersion(g_dwDeviceID);
  761.     if (g_dwAPIVersion == 0)
  762.     {
  763.         MessageBox(g_hDlgParentWindow,
  764.             "Line Version unsupported by this Sample",
  765.             "Unable to Use Line",MB_OK);
  766.         HangupCall();
  767.         goto DeleteBuffers;
  768.     }
  769.  
  770.     // Need to check the DevCaps to make sure this line is usable.
  771.     // The 'Dial' dialog checks also, but better safe than sorry.
  772.     lpLineDevCaps = I_lineGetDevCaps(lpLineDevCaps,
  773.         g_dwDeviceID, g_dwAPIVersion);
  774.     if (lpLineDevCaps == NULL)
  775.     {
  776.         HangupCall();
  777.         MessageBox(g_hDlgParentWindow,
  778.             "Error on Requested line",
  779.             "Unable to Use Line",MB_OK);
  780.         goto DeleteBuffers;
  781.     }
  782.  
  783.     if (!(lpLineDevCaps->dwBearerModes & LINEBEARERMODE_VOICE ))
  784.     {
  785.         HangupCall();
  786.         MessageBox(g_hDlgParentWindow,
  787.             "Error on Requested line",
  788.             "The selected line doesn't support VOICE capabilities",
  789.             MB_OK);
  790.         goto DeleteBuffers;
  791.     }
  792.  
  793.     if (!(lpLineDevCaps->dwMediaModes & LINEMEDIAMODE_DATAMODEM))
  794.     {
  795.         HangupCall();
  796.         MessageBox(g_hDlgParentWindow,
  797.             "Error on Requested line",
  798.             "The selected line doesn't support DATAMODEM capabilities",
  799.             MB_OK);
  800.         goto DeleteBuffers;
  801.     }
  802.  
  803.     // Does this line have the capability to make calls?
  804.     // It is possible that some lines can't make outbound calls.
  805.     if (!(lpLineDevCaps->dwLineFeatures & LINEFEATURE_MAKECALL))
  806.     {
  807.         HangupCall();
  808.         MessageBox(g_hDlgParentWindow,
  809.             "Error on Requested line",
  810.             "The selected line doesn't support MAKECALL capabilities",
  811.             MB_OK);
  812.         goto DeleteBuffers;
  813.     }
  814.  
  815.     // Open the Line for an outgoing DATAMODEM call.
  816.     do
  817.     {
  818.         lReturn = lineOpen(g_hLineApp, g_dwDeviceID, &g_hLine,
  819.             g_dwAPIVersion, 0, 0,
  820.             LINECALLPRIVILEGE_NONE, LINEMEDIAMODE_DATAMODEM,
  821.             0);
  822.  
  823.         if(lReturn == LINEERR_ALLOCATED)
  824.         {
  825.             HangupCall();
  826.             MessageBox(g_hDlgParentWindow,
  827.                 "Line is already in use by a non-TAPI application "
  828.                 "or by another TAPI Service Provider.",
  829.                 "Unable to Use Line",MB_OK);
  830.             goto DeleteBuffers;
  831.         }
  832.  
  833.         if (HandleLineErr(lReturn))
  834.             continue;
  835.         else
  836.         {
  837.             OutputDebugLineError(lReturn, "lineOpen unhandled error: ");
  838.             MessageBox(g_hDlgParentWindow,
  839.                 "Error on Requested line",
  840.                 "Unable to Use Line",MB_OK);
  841.             HangupCall();
  842.             goto DeleteBuffers;
  843.         }
  844.     }
  845.     while(lReturn != SUCCESS);
  846.  
  847.     // Tell the service provider that we want all notifications that
  848.     // have anything to do with this line.
  849.     do
  850.     {
  851.         // Set the messages we are interested in.
  852.  
  853.         // Note that while most applications aren't really interested
  854.         // in dealing with all of the possible messages, its interesting
  855.         // to see which come through the callback for testing purposes.
  856.  
  857.         lReturn = lineSetStatusMessages(g_hLine, 
  858.             LINEDEVSTATE_OTHER          |
  859.             LINEDEVSTATE_RINGING        |
  860.             LINEDEVSTATE_CONNECTED      |  // Important state!
  861.             LINEDEVSTATE_DISCONNECTED   |  // Important state!
  862.             LINEDEVSTATE_MSGWAITON      |
  863.             LINEDEVSTATE_MSGWAITOFF     |
  864.             LINEDEVSTATE_INSERVICE      |
  865.             LINEDEVSTATE_OUTOFSERVICE   |  // Important state!
  866.             LINEDEVSTATE_MAINTENANCE    |  // Important state!
  867.             LINEDEVSTATE_OPEN           |
  868.             LINEDEVSTATE_CLOSE          |
  869.             LINEDEVSTATE_NUMCALLS       |
  870.             LINEDEVSTATE_NUMCOMPLETIONS |
  871.             LINEDEVSTATE_TERMINALS      |
  872.             LINEDEVSTATE_ROAMMODE       |
  873.             LINEDEVSTATE_BATTERY        |
  874.             LINEDEVSTATE_SIGNAL         |
  875.             LINEDEVSTATE_DEVSPECIFIC    |
  876.             LINEDEVSTATE_REINIT         |  // Not allowed to disable this.
  877.             LINEDEVSTATE_LOCK           |
  878.             LINEDEVSTATE_CAPSCHANGE     |
  879.             LINEDEVSTATE_CONFIGCHANGE   |
  880.             LINEDEVSTATE_COMPLCANCEL    ,
  881.  
  882.             LINEADDRESSSTATE_OTHER      |
  883.             LINEADDRESSSTATE_DEVSPECIFIC|
  884.             LINEADDRESSSTATE_INUSEZERO  |
  885.             LINEADDRESSSTATE_INUSEONE   |
  886.             LINEADDRESSSTATE_INUSEMANY  |
  887.             LINEADDRESSSTATE_NUMCALLS   |
  888.             LINEADDRESSSTATE_FORWARD    |
  889.             LINEADDRESSSTATE_TERMINALS  |
  890.             LINEADDRESSSTATE_CAPSCHANGE);
  891.  
  892.  
  893.         if (HandleLineErr(lReturn))
  894.             continue;
  895.         else
  896.         {
  897.             // If we do get an unhandled problem, we don't care.
  898.             // We just won't get notifications.
  899.             OutputDebugLineError(lReturn,
  900.                 "lineSetStatusMessages unhandled error: ");
  901.             break;
  902.         }
  903.     }
  904.     while(lReturn != SUCCESS);
  905.  
  906.  
  907.     // Get LineAddressStatus so we can make sure the line
  908.     // isn't already in use by a TAPI application.
  909.     lpLineAddressStatus = 
  910.         I_lineGetAddressStatus(lpLineAddressStatus, g_hLine, 0);
  911.         
  912.     if (lpLineAddressStatus == NULL)
  913.     {
  914.         HangupCall();
  915.         MessageBox(g_hDlgParentWindow,
  916.             "Error on Requested line",
  917.             "Unable to Use Line",MB_OK);
  918.         goto DeleteBuffers;
  919.     }
  920.  
  921.     // MAKECALL will be set if there are any available call appearances
  922.     if ( ! ((lpLineAddressStatus -> dwAddressFeatures) &
  923.             LINEADDRFEATURE_MAKECALL) )
  924.     {
  925.         OutputDebugString("This line is not available to place a call.\n");
  926.         HangupCall();
  927.         MessageBox(g_hDlgParentWindow,
  928.             "Requested line is already in use",
  929.             "Unable to Use Line",MB_OK);
  930.         goto DeleteBuffers;
  931.     }
  932.  
  933.     // If the line was configured in the 'Dial' dialog, then
  934.     // we need to actually complete the configuration.
  935.     if (g_lpDeviceConfig)
  936.         lineSetDevConfig(g_dwDeviceID, g_lpDeviceConfig,
  937.             g_dwSizeDeviceConfig, "comm/datamodem");
  938.  
  939.     // Start dialing the number
  940.     if (DialCallInParts(lpLineDevCaps, g_szDialableAddress,
  941.             g_szDisplayableAddress))
  942.     {
  943.         OutputDebugString("DialCallInParts succeeded.\n");
  944.     }
  945.     else
  946.     {
  947.         OutputDebugString("DialCallInParts failed.\n");
  948.         HangupCall();
  949.         goto DeleteBuffers;
  950.     }
  951.  
  952. DeleteBuffers:
  953.  
  954.     if (lpLineAddressStatus)
  955.         LocalFree(lpLineAddressStatus);
  956.     if (lpLineDevCaps)
  957.         LocalFree(lpLineDevCaps);
  958.     if (g_bTapiInUse)
  959.         EnableHangupCall(g_hWndMainWindow, TRUE);
  960.  
  961.     return g_bTapiInUse;
  962. }
  963.  
  964.  
  965. //**************************************************
  966. // These APIs are specific to this module 
  967. //**************************************************
  968.  
  969.  
  970.  
  971. //
  972. //  FUNCTION: DialCallInParts(LPLINEDEVCAPS, LPCSTR, LPCSTR)
  973. //
  974. //  PURPOSE: Dials the call, handling special characters.
  975. //
  976. //  PARAMETERS:
  977. //    lpLineDevCaps - LINEDEVCAPS for the line to be used.
  978. //    lpszAddress   - Address to Dial.
  979. //    lpszDisplayableAddress - Displayable Address.
  980. //
  981. //  RETURN VALUE:
  982. //    Returns TRUE if we successfully Dial.
  983. //
  984. //  COMMENTS:
  985. //
  986. //    This function dials the Address and handles any
  987. //    special characters in the address that the service provider
  988. //    can't handle.  It requires input from the user to handle
  989. //    these characters; this can cause problems for fully automated
  990. //    dialing.
  991. //
  992. //    Note that we can return TRUE, even if we don't reach a
  993. //    CONNECTED state.  DIalCallInParts returns as soon as the
  994. //    Address is fully dialed or when an error occurs.
  995. //
  996. //
  997.  
  998. BOOL DialCallInParts(LPLINEDEVCAPS lpLineDevCaps,
  999.     LPCSTR lpszAddress, LPCSTR lpszDisplayableAddress)
  1000. {
  1001.     LPLINECALLPARAMS  lpCallParams = NULL;
  1002.     LPLINEADDRESSCAPS lpAddressCaps = NULL;
  1003.     LPLINECALLSTATUS  lpLineCallStatus = NULL;
  1004.  
  1005.     long lReturn;
  1006.     int i;
  1007.     DWORD dwDevCapFlags;
  1008.     char szFilter[1+sizeof(g_sNonDialable)] = "";
  1009.     BOOL bFirstDial = TRUE;
  1010.                                
  1011.     // Variables to handle Dialable Substring dialing.
  1012.     LPSTR lpDS; // This is just so we can free lpszDialableSubstring later.
  1013.     LPSTR lpszDialableSubstring;
  1014.     int nAddressLength = 0;
  1015.     int nCurrentAddress = 0;
  1016.     char chUnhandledCharacter;
  1017.  
  1018.     // Get the capabilities for the line device we're going to use.
  1019.     lpAddressCaps = I_lineGetAddressCaps(lpAddressCaps,
  1020.         g_dwDeviceID, 0, g_dwAPIVersion, 0);
  1021.     if (lpAddressCaps == NULL)
  1022.         return FALSE;
  1023.  
  1024.     // Setup our CallParams for DATAMODEM settings.
  1025.     lpCallParams = CreateCallParams (lpCallParams, lpszDisplayableAddress);
  1026.     if (lpCallParams == NULL)
  1027.         return FALSE;
  1028.  
  1029.     // Determine which special characters the service provider
  1030.     // does *not* handle so we can handle them manually.
  1031.     // Keep list of unhandled characters in szFilter.
  1032.     
  1033.     dwDevCapFlags = lpLineDevCaps -> dwDevCapFlags;  // SP handled characters.
  1034.     for (i = 0; i < g_sizeofNonDialable ; i++)
  1035.     {
  1036.         if ((dwDevCapFlags & g_sNonDialable[i].dwDevCapFlag) == 0)
  1037.         {
  1038.             strcat(szFilter, g_sNonDialable[i].szToken);
  1039.         }
  1040.     }
  1041.  
  1042.     // szFilter now contains the set of tokens which delimit dialable substrings
  1043.     
  1044.     // Setup the strings for substring dialing.
  1045.                            
  1046.     nAddressLength = strlen(lpszAddress);
  1047.     lpDS = lpszDialableSubstring = (LPSTR) LocalAlloc(LPTR, nAddressLength + 1);
  1048.     if (lpszDialableSubstring == NULL)
  1049.     {
  1050.         OutputDebugLastError(GetLastError(), "LocalAlloc failed: ");
  1051.         HandleNoMem();
  1052.         goto errExit;
  1053.     }
  1054.  
  1055.     // Lets start dialing substrings!
  1056.     while (nCurrentAddress < nAddressLength)
  1057.     {
  1058.   retryAfterError:
  1059.  
  1060.         // Find the next undialable character
  1061.         i = strcspn(&lpszAddress[nCurrentAddress], szFilter);
  1062.  
  1063.         // Was there one before the end of the Address string?
  1064.         if (i + nCurrentAddress < nAddressLength)
  1065.         {
  1066.             // Make sure this device can handle partial dial.
  1067.             if (! (lpAddressCaps -> dwAddrCapFlags & 
  1068.                    LINEADDRCAPFLAGS_PARTIALDIAL))
  1069.             {
  1070.                 MessageBox(g_hDlgParentWindow,
  1071.                     "This line doesn't support partial dialing.\n",
  1072.                     "Warning",MB_OK);
  1073.                 goto errExit;
  1074.             }
  1075.             // Remember what the unhandled character is so we can handle it.
  1076.             chUnhandledCharacter = lpszAddress[nCurrentAddress+i];
  1077.             
  1078.             // Copy the dialable string to the Substring.
  1079.             memcpy(lpszDialableSubstring, &lpszAddress[nCurrentAddress], i);
  1080.  
  1081.             // Terminate the substring with a ';' to signify the partial dial.
  1082.             lpszDialableSubstring[i] = ';';
  1083.             lpszDialableSubstring[i+1] = '\0';
  1084.             
  1085.             // Increment the address for next iteration.
  1086.             nCurrentAddress += i + 1;
  1087.         }
  1088.         else // No more partial dials.  Dial the rest of the Address.
  1089.         {
  1090.             lpszDialableSubstring = (LPSTR) &lpszAddress[nCurrentAddress];
  1091.             chUnhandledCharacter = 0;
  1092.             nCurrentAddress = nAddressLength;
  1093.         }
  1094.         
  1095.         do
  1096.         {                   
  1097.             if (bFirstDial)
  1098.                 lReturn = WaitForReply( 
  1099.                     lineMakeCall(g_hLine, &g_hCall, lpszDialableSubstring,
  1100.                         0, lpCallParams) );
  1101.             else
  1102.                 lReturn = WaitForReply( 
  1103.                     lineDial(g_hCall, lpszDialableSubstring, 0) );
  1104.  
  1105.             switch(lReturn)
  1106.             {
  1107.                 // We should not have received these errors because of the
  1108.                 // prefiltering strategy, but there may be some ill-behaved
  1109.                 // service providers which do not correctly set their
  1110.                 // devcapflags.  Add the character corresponding to the error
  1111.                 // to the filter set and retry dialing.
  1112.                 //
  1113.                 case LINEERR_DIALBILLING:
  1114.                 case LINEERR_DIALDIALTONE:
  1115.                 case LINEERR_DIALQUIET:
  1116.                 case LINEERR_DIALPROMPT:
  1117.                 {
  1118.                     OutputDebugString("Service Provider incorrectly sets dwDevCapFlags\n");
  1119.  
  1120.                     for (i = 0; i < g_sizeofNonDialable; i++)
  1121.                         if (lReturn == g_sNonDialable[i].lError)
  1122.                         {
  1123.                             strcat(szFilter, g_sNonDialable[i].szToken);
  1124.                         }
  1125.  
  1126.                     goto retryAfterError;
  1127.                 }
  1128.  
  1129.                 case WAITERR_WAITABORTED:
  1130.                     OutputDebugString("While Dialing, WaitForReply aborted.\n");
  1131.                     goto errExit;
  1132.             
  1133.             }
  1134.  
  1135.             if (HandleLineErr(lReturn))
  1136.                 continue;
  1137.             else
  1138.             {
  1139.                 if (bFirstDial)
  1140.                     OutputDebugLineError(lReturn, "lineMakeCall unhandled error: ");
  1141.                 else
  1142.                     OutputDebugLineError(lReturn, "lineDial unhandled error: ");
  1143.  
  1144.                 goto errExit;
  1145.             }
  1146.  
  1147.         }
  1148.         while (lReturn != SUCCESS);
  1149.         
  1150.         bFirstDial = FALSE;
  1151.                                 
  1152.         // The dial was successful; now handle characters the service
  1153.         // provider didn't (if any).
  1154.         if (chUnhandledCharacter)
  1155.         {
  1156.             LPSTR lpMsg = "";
  1157.  
  1158.             // First, wait until we know we can continue dialing.  While the
  1159.             // last string is still pending to be dialed, we can't dial another.
  1160.  
  1161.             while(TRUE)
  1162.             {
  1163.  
  1164.                 lpLineCallStatus = I_lineGetCallStatus(lpLineCallStatus, g_hCall);
  1165.                 if (lpLineCallStatus == NULL)
  1166.                     goto errExit;
  1167.  
  1168.                 // Does CallStatus say we can dial now?
  1169.                 if ((lpLineCallStatus->dwCallFeatures) & LINECALLFEATURE_DIAL)
  1170.                 {
  1171.                     OutputDebugString("Ok to continue dialing.\n");
  1172.                     break;
  1173.                 }
  1174.                 
  1175.                 // We can't dial yet, so wait for a CALLSTATE message
  1176.                 OutputDebugString("Waiting for dialing to be enabled.\n");
  1177.  
  1178.                 if (WaitForCallState(I_LINECALLSTATE_ANY) != SUCCESS)
  1179.                     goto errExit;
  1180.             }
  1181.  
  1182.             for (i = 0; i < g_sizeofNonDialable; i++)
  1183.                 if (chUnhandledCharacter == g_sNonDialable[i].szToken[0])
  1184.                     lpMsg = g_sNonDialable[i].szMsg;
  1185.                     
  1186.             MessageBox(g_hDlgParentWindow, lpMsg, "Dialing Paused", MB_OK);
  1187.         }
  1188.         
  1189.     } // continue dialing until we dial all Dialable Substrings.
  1190.  
  1191.     LocalFree(lpCallParams);
  1192.     LocalFree(lpDS);
  1193.     LocalFree(lpAddressCaps);
  1194.     if (lpLineCallStatus)
  1195.         LocalFree(lpLineCallStatus);
  1196.     
  1197.     return TRUE;
  1198.     
  1199.   errExit:
  1200.         // if lineMakeCall has already been successfully called, there's a call in progress. 
  1201.         // let the invoking routine shut down the call.
  1202.         // if the invoker did not clean up the call, it should be done here.
  1203.  
  1204.     if (lpLineCallStatus)
  1205.         LocalFree(lpLineCallStatus);
  1206.     if (lpDS)
  1207.         LocalFree(lpDS);
  1208.     if (lpCallParams)
  1209.         LocalFree(lpCallParams);
  1210.     if (lpAddressCaps)
  1211.         LocalFree(lpAddressCaps);
  1212.  
  1213.     return FALSE;
  1214. }   
  1215.  
  1216.  
  1217. //
  1218. //  FUNCTION: CreateCallParams(LPLINECALLPARAMS, LPCSTR)
  1219. //
  1220. //  PURPOSE: Allocates and fills a LINECALLPARAMS structure
  1221. //
  1222. //  PARAMETERS:
  1223. //    lpCallParams - 
  1224. //    lpszDisplayableAddress - 
  1225. //
  1226. //  RETURN VALUE:
  1227. //    Returns a LPLINECALLPARAMS ready to use for dialing DATAMODEM calls.
  1228. //    Returns NULL if unable to allocate the structure.
  1229. //
  1230. //  COMMENTS:
  1231. //
  1232. //    If a non-NULL lpCallParams is passed in, it must have been allocated
  1233. //    with LocalAlloc, and can potentially be freed and reallocated.  It must
  1234. //    also have the dwTotalSize field correctly set.
  1235. //
  1236. //
  1237.  
  1238. LPLINECALLPARAMS CreateCallParams (
  1239.     LPLINECALLPARAMS lpCallParams, LPCSTR lpszDisplayableAddress)
  1240. {
  1241.     size_t sizeDisplayableAddress;
  1242.  
  1243.     if (lpszDisplayableAddress == NULL)
  1244.         lpszDisplayableAddress = "";
  1245.         
  1246.     sizeDisplayableAddress = strlen(lpszDisplayableAddress) + 1;
  1247.                           
  1248.     lpCallParams = (LPLINECALLPARAMS) CheckAndReAllocBuffer(
  1249.         (LPVOID) lpCallParams, 
  1250.         sizeof(LINECALLPARAMS) + sizeDisplayableAddress,
  1251.         "CreateCallParams: ");
  1252.  
  1253.     if (lpCallParams == NULL)
  1254.         return NULL;
  1255.     
  1256.     // This is where we configure the line for DATAMODEM usage.
  1257.     lpCallParams -> dwBearerMode = LINEBEARERMODE_VOICE;
  1258.     lpCallParams -> dwMediaMode  = LINEMEDIAMODE_DATAMODEM;
  1259.  
  1260.     // This specifies that we want to use only IDLE calls and
  1261.     // don't want to cut into a call that might not be IDLE (ie, in use).
  1262.     lpCallParams -> dwCallParamFlags = LINECALLPARAMFLAGS_IDLE;
  1263.                                     
  1264.     // if there are multiple addresses on line, use first anyway.
  1265.     // It will take a more complex application than a simple tty app
  1266.     // to use multiple addresses on a line anyway.
  1267.     lpCallParams -> dwAddressMode = LINEADDRESSMODE_ADDRESSID;
  1268.     lpCallParams -> dwAddressID = 0;
  1269.  
  1270.     // Since we don't know where we originated, leave these blank.
  1271.     lpCallParams -> dwOrigAddressSize = 0;
  1272.     lpCallParams -> dwOrigAddressOffset = 0;
  1273.     
  1274.     // Unimodem ignores these values.
  1275.     (lpCallParams -> DialParams) . dwDialSpeed = 0;
  1276.     (lpCallParams -> DialParams) . dwDigitDuration = 0;
  1277.     (lpCallParams -> DialParams) . dwDialPause = 0;
  1278.     (lpCallParams -> DialParams) . dwWaitForDialtone = 0;
  1279.     
  1280.     // Address we are dialing.
  1281.     lpCallParams -> dwDisplayableAddressOffset = sizeof(LINECALLPARAMS);
  1282.     lpCallParams -> dwDisplayableAddressSize = sizeDisplayableAddress;
  1283.     strcpy((LPSTR)lpCallParams + sizeof(LINECALLPARAMS),
  1284.            lpszDisplayableAddress);
  1285.  
  1286.     return lpCallParams;
  1287. }
  1288.  
  1289.  
  1290. //
  1291. //  FUNCTION: long WaitForReply(long)
  1292. //
  1293. //  PURPOSE: Resynchronize by waiting for a LINE_REPLY 
  1294. //
  1295. //  PARAMETERS:
  1296. //    lRequestID - The asynchronous request ID that we're
  1297. //                 on a LINE_REPLY for.
  1298. //
  1299. //  RETURN VALUE:
  1300. //    - 0 if LINE_REPLY responded with a success.
  1301. //    - LINEERR constant if LINE_REPLY responded with a LINEERR
  1302. //    - 1 if the line was shut down before LINE_REPLY is received.
  1303. //
  1304. //  COMMENTS:
  1305. //
  1306. //    This function allows us to resynchronize an asynchronous
  1307. //    TAPI line call by waiting for the LINE_REPLY message.  It
  1308. //    waits until a LINE_REPLY is received or the line is shut down.
  1309. //
  1310. //    Note that this could cause re-entrancy problems as
  1311. //    well as mess with any message preprocessing that might
  1312. //    occur on this thread (such as TranslateAccelerator).
  1313. //
  1314. //    This function should to be called from the thread that did
  1315. //    lineInitialize, or the PeekMessage is on the wrong thread
  1316. //    and the synchronization is not guaranteed to work.  Also note
  1317. //    that if another PeekMessage loop is entered while waiting,
  1318. //    this could also cause synchronization problems.
  1319. //
  1320. //    One more note.  This function can potentially be re-entered
  1321. //    if the call is dropped for any reason while waiting.  If this
  1322. //    happens, just drop out and assume the wait has been canceled.  
  1323. //    This is signaled by setting bReentered to FALSE when the function 
  1324. //    is entered and TRUE when it is left.  If bReentered is ever TRUE 
  1325. //    during the function, then the function was re-entered.
  1326. //
  1327. //    This function times out and returns WAITERR_WAITTIMEDOUT
  1328. //    after WAITTIMEOUT milliseconds have elapsed.
  1329. //
  1330. //
  1331.  
  1332.  
  1333. long WaitForReply (long lRequestID)
  1334. {
  1335.     static BOOL bReentered;
  1336.     bReentered = FALSE;
  1337.  
  1338.     if (lRequestID > SUCCESS)
  1339.     {
  1340.         MSG msg; 
  1341.         DWORD dwTimeStarted;
  1342.  
  1343.         g_bReplyRecieved = FALSE;
  1344.         g_dwRequestedID = (DWORD) lRequestID;
  1345.  
  1346.         // Initializing this just in case there is a bug
  1347.         // that sets g_bReplyRecieved without setting the reply value.
  1348.         g_lAsyncReply = LINEERR_OPERATIONFAILED;
  1349.  
  1350.         dwTimeStarted = GetTickCount();
  1351.  
  1352.         while(!g_bReplyRecieved)
  1353.         {
  1354.             if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
  1355.             {
  1356.                 TranslateMessage(&msg);
  1357.                 DispatchMessage(&msg);
  1358.             }
  1359.  
  1360.             // This should only occur if the line is shut down while waiting.
  1361.             if (!g_bTapiInUse || bReentered)
  1362.             {
  1363.                 bReentered = TRUE;
  1364.                 return WAITERR_WAITABORTED;
  1365.             }
  1366.  
  1367.             // Its a really bad idea to timeout a wait for a LINE_REPLY.
  1368.             // If we are execting a LINE_REPLY, we should wait till we get
  1369.             // it; it might take a long time to dial (for example).
  1370.  
  1371.             // If 5 seconds go by without a reply, it might be a good idea
  1372.             // to display a dialog box to tell the user that a
  1373.             // wait is in progress and to give the user the capability to
  1374.             // abort the wait.
  1375.         }
  1376.  
  1377.         bReentered = TRUE;
  1378.         return g_lAsyncReply;
  1379.     }
  1380.  
  1381.     bReentered = TRUE;
  1382.     return lRequestID;
  1383. }
  1384.  
  1385.  
  1386. //
  1387. //  FUNCTION: long WaitForCallState(DWORD)
  1388. //
  1389. //  PURPOSE: Wait for the line to reach a specific CallState.
  1390. //
  1391. //  PARAMETERS:
  1392. //    dwDesiredCallState - specific CallState to wait for.
  1393. //
  1394. //  RETURN VALUE:
  1395. //    Returns 0 (SUCCESS) when we reach the Desired CallState.
  1396. //    Returns WAITERR_WAITTIMEDOUT if timed out.
  1397. //    Returns WAITERR_WAITABORTED if call was closed while waiting.
  1398. //
  1399. //  COMMENTS:
  1400. //
  1401. //    This function allows us to synchronously wait for a line
  1402. //    to reach a specific LINESTATE or until the line is shut down.
  1403. //
  1404. //    Note that this could cause re-entrancy problems as
  1405. //    well as mess with any message preprocessing that might
  1406. //    occur on this thread (such as TranslateAccelerator).
  1407. //
  1408. //    One more note.  This function can potentially be re-entered
  1409. //    if the call is dropped for any reason while waiting.  If this
  1410. //    happens, just drop out and assume the wait has been canceled.  
  1411. //    This is signaled by setting bReentered to FALSE when the function 
  1412. //    is entered and TRUE when it is left.  If bReentered is ever TRUE 
  1413. //    during the function, then the function was re-entered.
  1414. //
  1415. //    This function should to be called from the thread that did
  1416. //    lineInitialize, or the PeekMessage is on the wrong thread
  1417. //    and the synchronization is not guaranteed to work.  Also note
  1418. //    that if another PeekMessage loop is entered while waiting,
  1419. //    this could also cause synchronization problems.
  1420. //
  1421. //    If the constant value I_LINECALLSTATE_ANY is used for the 
  1422. //    dwDesiredCallState, then WaitForCallState will return SUCCESS
  1423. //    upon receiving any CALLSTATE messages.
  1424. //    
  1425. //
  1426. //
  1427.  
  1428. long WaitForCallState(DWORD dwDesiredCallState)
  1429. {
  1430.     MSG msg;
  1431.     DWORD dwTimeStarted;
  1432.     static BOOL bReentered;
  1433.  
  1434.     bReentered = FALSE;
  1435.  
  1436.     dwTimeStarted = GetTickCount();
  1437.  
  1438.     g_bCallStateReceived = FALSE;
  1439.  
  1440.     while ((dwDesiredCallState == I_LINECALLSTATE_ANY) || 
  1441.            (g_dwCallState != dwDesiredCallState))
  1442.     {
  1443.         if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
  1444.         {
  1445.             TranslateMessage(&msg);
  1446.             DispatchMessage(&msg);
  1447.         }
  1448.  
  1449.         // If we are waiting for any call state and get one, succeed.
  1450.         if ((dwDesiredCallState == I_LINECALLSTATE_ANY) && 
  1451.             g_bCallStateReceived)
  1452.         {
  1453.             break;
  1454.         }
  1455.  
  1456.         // This should only occur if the line is shut down while waiting.
  1457.         if (!g_bTapiInUse || bReentered)
  1458.         {
  1459.             bReentered = TRUE;
  1460.             OutputDebugString("WAITABORTED\n");
  1461.             return WAITERR_WAITABORTED;
  1462.         }
  1463.  
  1464.         // If we don't get the reply in a reasonable time, we time out.
  1465.         if (GetTickCount() - dwTimeStarted > WAITTIMEOUT)
  1466.         {
  1467.             bReentered = TRUE;
  1468.             OutputDebugString("WAITTIMEDOUT\n");
  1469.             return WAITERR_WAITTIMEDOUT;
  1470.         }
  1471.  
  1472.     }
  1473.  
  1474.     bReentered = TRUE;
  1475.     return SUCCESS;
  1476. }
  1477.  
  1478. //**************************************************
  1479. // lineCallback Function and Handlers.
  1480. //**************************************************
  1481.  
  1482.  
  1483. //
  1484. //  FUNCTION: lineCallbackFunc(..)
  1485. //
  1486. //  PURPOSE: Receive asynchronous TAPI events
  1487. //
  1488. //  PARAMETERS:
  1489. //    dwDevice  - Device associated with the event, if any
  1490. //    dwMsg     - TAPI event that occurred.
  1491. //    dwCallbackInstance - User defined data supplied when opening the line.
  1492. //    dwParam1  - dwMsg specific information
  1493. //    dwParam2  - dwMsg specific information
  1494. //    dwParam3  - dwMsg specific information
  1495. //
  1496. //  RETURN VALUE:
  1497. //    none
  1498. //
  1499. //  COMMENTS:
  1500. //    This is the function where all asynchronous events will come.
  1501. //    Almost all events will be specific to an open line, but a few
  1502. //    will be general TAPI events (such as LINE_REINIT).
  1503. //
  1504. //    Its important to note that this callback will *ALWAYS* be
  1505. //    called in the context of the thread that does the lineInitialize.
  1506. //    Even if another thread (such as the COMM threads) calls the API
  1507. //    that would result in the callback being called, it will be called
  1508. //    in the context of the main thread (since in this sample, the main
  1509. //    thread does the lineInitialize).
  1510. //
  1511. //
  1512.  
  1513.  
  1514. void CALLBACK lineCallbackFunc(
  1515.     DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance, 
  1516.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  1517. {
  1518.  
  1519.     OutputDebugLineCallback(
  1520.         dwDevice, dwMsg, dwCallbackInstance, 
  1521.         dwParam1, dwParam2, dwParam3);
  1522.  
  1523.     // All we do is dispatch the dwMsg to the correct handler.
  1524.     switch(dwMsg)
  1525.     {
  1526.         case LINE_CALLSTATE:
  1527.             DoLineCallState(dwDevice, dwMsg, dwCallbackInstance,
  1528.                 dwParam1, dwParam2, dwParam3);
  1529.             break;
  1530.  
  1531.         case LINE_CLOSE:
  1532.             DoLineClose(dwDevice, dwMsg, dwCallbackInstance,
  1533.                 dwParam1, dwParam2, dwParam3);
  1534.             break;
  1535.  
  1536.         case LINE_LINEDEVSTATE:
  1537.             DoLineDevState(dwDevice, dwMsg, dwCallbackInstance,
  1538.                 dwParam1, dwParam2, dwParam3);
  1539.             break;
  1540.  
  1541.         case LINE_REPLY:
  1542.             DoLineReply(dwDevice, dwMsg, dwCallbackInstance,
  1543.                 dwParam1, dwParam2, dwParam3);
  1544.             break;
  1545.  
  1546.         case LINE_CREATE:
  1547.             DoLineCreate(dwDevice, dwMsg, dwCallbackInstance,
  1548.                 dwParam1, dwParam2, dwParam3);
  1549.             break;
  1550.  
  1551.         default:
  1552.             OutputDebugString("lineCallbackFunc message ignored\n");
  1553.             break;
  1554.  
  1555.     }
  1556.  
  1557.     return;
  1558.  
  1559. }
  1560.  
  1561.  
  1562. //
  1563. //  FUNCTION: DoLineReply(..)
  1564. //
  1565. //  PURPOSE: Handle LINE_REPLY asynchronous messages.
  1566. //
  1567. //  PARAMETERS:
  1568. //    dwDevice  - Line Handle associated with this LINE_REPLY.
  1569. //    dwMsg     - Should always be LINE_REPLY.
  1570. //    dwCallbackInstance - Unused by this sample.
  1571. //    dwParam1  - Asynchronous request ID.
  1572. //    dwParam2  - success or LINEERR error value.
  1573. //    dwParam3  - Unused.
  1574. //
  1575. //  RETURN VALUE:
  1576. //    none
  1577. //
  1578. //  COMMENTS:
  1579. //
  1580. //    All line API calls that return an asynchronous request ID
  1581. //    will eventually cause a LINE_REPLY message.  Handle it.
  1582. //
  1583. //    This sample assumes only one call at time, and that we wait
  1584. //    for a LINE_REPLY before making any other line API calls.
  1585. //
  1586. //    The only exception to the above is that we might shut down
  1587. //    the line before receiving a LINE_REPLY.
  1588. //
  1589. //
  1590.  
  1591. void DoLineReply(
  1592.     DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
  1593.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  1594. {
  1595.     if ((long) dwParam2 != SUCCESS)
  1596.         OutputDebugLineError((long) dwParam2, "LINE_REPLY error: ");
  1597.     else
  1598.         OutputDebugString("LINE_REPLY: successfully replied.\n");
  1599.  
  1600.     // If we are currently waiting for this async Request ID
  1601.     // then set the global variables to acknowledge it.
  1602.     if (g_dwRequestedID == dwParam1)
  1603.     {
  1604.         g_bReplyRecieved = TRUE;
  1605.         g_lAsyncReply = (long) dwParam2;
  1606.     }
  1607. }
  1608.  
  1609.  
  1610. //
  1611. //  FUNCTION: DoLineClose(..)
  1612. //
  1613. //  PURPOSE: Handle LINE_CLOSE asynchronous messages.
  1614. //
  1615. //  PARAMETERS:
  1616. //    dwDevice  - Line Handle that was closed.
  1617. //    dwMsg     - Should always be LINE_CLOSE.
  1618. //    dwCallbackInstance - Unused by this sample.
  1619. //    dwParam1  - Unused.
  1620. //    dwParam2  - Unused.
  1621. //    dwParam3  - Unused.
  1622. //
  1623. //  RETURN VALUE:
  1624. //    none
  1625. //
  1626. //  COMMENTS:
  1627. //
  1628. //    This message is sent when something outside our app shuts
  1629. //    down a line in use.
  1630. //
  1631. //    The hLine (and any hCall on this line) are no longer valid.
  1632. //
  1633. //
  1634.  
  1635. void DoLineClose(
  1636.     DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
  1637.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  1638. {
  1639.     // Line has been shut down.  Clean up our internal variables.
  1640.     g_hLine = NULL;
  1641.     g_hCall = NULL;
  1642.     UpdateStatusBar("Call was shut down.",1,0);
  1643.     MessageBox(g_hDlgParentWindow,
  1644.         "Call was shut down.","Warning",MB_OK);
  1645.     HangupCall();
  1646. }
  1647.  
  1648.  
  1649. //
  1650. //  FUNCTION: DoLineDevState(..)
  1651. //
  1652. //  PURPOSE: Handle LINE_LINEDEVSTATE asynchronous messages.
  1653. //
  1654. //  PARAMETERS:
  1655. //    dwDevice  - Line Handle that was closed.
  1656. //    dwMsg     - Should always be LINE_LINEDEVSTATE.
  1657. //    dwCallbackInstance - Unused by this sample.
  1658. //    dwParam1  - LINEDEVSTATE constant.
  1659. //    dwParam2  - Depends on dwParam1.
  1660. //    dwParam3  - Depends on dwParam1.
  1661. //
  1662. //  RETURN VALUE:
  1663. //    none
  1664. //
  1665. //  COMMENTS:
  1666. //
  1667. //    The LINE_LINEDEVSTATE message is received if the state of the line
  1668. //    changes.  Examples are RINGING, MAINTENANCE, MSGWAITON.  Very few of
  1669. //    these are relevant to this sample.
  1670. //
  1671. //    Assuming that any LINEDEVSTATE that removes the line from use by TAPI
  1672. //    will also send a LINE_CLOSE message.
  1673. //
  1674. //
  1675.  
  1676. void DoLineDevState(
  1677.     DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
  1678.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  1679. {
  1680.     switch(dwParam1)
  1681.     {
  1682.         case LINEDEVSTATE_RINGING:
  1683.             UpdateStatusBar("Line Ringing",1,0);
  1684.             OutputDebugString("Line Ringing.\n");
  1685.             break;
  1686.  
  1687.         case LINEDEVSTATE_REINIT:
  1688.         // This is an important case!  Usually means that a service provider
  1689.         // has changed in such a way that requires TAPI to REINIT.  
  1690.         // Note that there are both 'soft' REINITs and 'hard' REINITs.
  1691.         // Soft REINITs don't actually require a full shutdown but is instead
  1692.         // just an informational change that historically required a REINIT
  1693.         // to force the application to deal with.  TAPI API Version 1.3 apps
  1694.         // will still need to do a full REINIT for both hard and soft REINITs.
  1695.  
  1696.             switch(dwParam2)
  1697.             {
  1698.                 // This is the hard REINIT.  No reason given, just REINIT.
  1699.                 // TAPI is waiting for everyone to shutdown.
  1700.                 // Our response is to immediately shutdown any calls,
  1701.                 // shutdown our use of TAPI and notify the user.
  1702.                 case 0:
  1703.                     ShutdownTAPI();
  1704.                     WarningBox("Tapi line configuration has changed.");
  1705.                     break;
  1706.  
  1707.                 case LINE_CREATE:
  1708.                     OutputDebugString("Soft REINIT: LINE_CREATE.\n");
  1709.                     DoLineCreate(dwDevice, dwParam2, dwCallbackInstance,
  1710.                         dwParam3, 0, 0);
  1711.                     break;
  1712.  
  1713.                 case LINE_LINEDEVSTATE:
  1714.                     OutputDebugString("Soft REINIT: LINE_LINEDEVSTATE.\n");
  1715.                     DoLineDevState(dwDevice, dwParam2, dwCallbackInstance,
  1716.                         dwParam3, 0, 0);
  1717.                     break;
  1718.  
  1719.                 // There might be other reasons to send a soft reinit.
  1720.                 // No need to to shutdown for these.
  1721.                 default:
  1722.                     OutputDebugString("Ignoring soft REINIT\n");
  1723.                     break;
  1724.             }
  1725.             break;
  1726.  
  1727.         case LINEDEVSTATE_OUTOFSERVICE:
  1728.             WarningBox("Line selected is now Out of Service.");
  1729.             HangupCall();
  1730.             break;
  1731.  
  1732.         case LINEDEVSTATE_DISCONNECTED:
  1733.             WarningBox("Line selected is now disconnected.");
  1734.             HangupCall();
  1735.             break;
  1736.  
  1737.         case LINEDEVSTATE_MAINTENANCE:
  1738.             WarningBox("Line selected is now out for maintenance.");
  1739.             HangupCall();
  1740.             break;
  1741.  
  1742.         case LINEDEVSTATE_TRANSLATECHANGE:
  1743.             if (g_hDialog)
  1744.                 PostMessage(g_hDialog, WM_COMMAND, IDC_CONFIGURATIONCHANGED, 0);
  1745.             break;
  1746.  
  1747.         case LINEDEVSTATE_REMOVED:
  1748.             OutputDebugString("A Line device has been removed;"
  1749.                 " no action taken.\n");
  1750.             break;
  1751.  
  1752.         default:
  1753.             OutputDebugString("Unhandled LINEDEVSTATE message\n");
  1754.     }
  1755. }
  1756.  
  1757.  
  1758. //
  1759. //  FUNCTION: DoLineCreate(..)
  1760. //
  1761. //  PURPOSE: Handle LINE_LINECREATE asynchronous messages.
  1762. //
  1763. //  PARAMETERS:
  1764. //    dwDevice  - Unused.
  1765. //    dwMsg     - Should always be LINE_CREATE.
  1766. //    dwCallbackInstance - Unused.
  1767. //    dwParam1  - dwDeviceID of new Line created.
  1768. //    dwParam2  - Unused.
  1769. //    dwParam3  - Unused.
  1770. //
  1771. //  RETURN VALUE:
  1772. //    none
  1773. //
  1774. //  COMMENTS:
  1775. //
  1776. //    This message is new for Windows 95.  It is sent when a new line is
  1777. //    added to the system.  This allows us to handle new lines without having
  1778. //    to REINIT.  This allows for much more graceful Plug and Play.
  1779. //
  1780. //    This sample just changes the number of devices available and can use
  1781. //    it next time a call is made.  It also tells the "Dial" dialog.
  1782. //
  1783. //
  1784.  
  1785. void DoLineCreate(
  1786.     DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
  1787.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  1788. {
  1789.     // dwParam1 is the Device ID of the new line.  
  1790.     // Add one to get the number of total devices.
  1791.     if (g_dwNumDevs <= dwParam1)
  1792.         g_dwNumDevs = dwParam1+1;
  1793.     if (g_hDialog)
  1794.         PostMessage(g_hDialog, WM_COMMAND, IDC_LINECREATE, 0);
  1795.  
  1796. }
  1797.  
  1798.  
  1799. //
  1800. //  FUNCTION: DoLineCallState(..)
  1801. //
  1802. //  PURPOSE: Handle LINE_CALLSTATE asynchronous messages.
  1803. //
  1804. //  PARAMETERS:
  1805. //    dwDevice  - Handle to Call who's state is changing.
  1806. //    dwMsg     - Should always be LINE_CALLSTATE.
  1807. //    dwCallbackInstance - Unused by this sample.
  1808. //    dwParam1  - LINECALLSTATE constant specifying state change.
  1809. //    dwParam2  - Specific to dwParam1.
  1810. //    dwParam3  - LINECALLPRIVILEGE change, if any.
  1811. //
  1812. //  RETURN VALUE:
  1813. //    none
  1814. //
  1815. //  COMMENTS:
  1816. //
  1817. //    This message is received whenever a call changes state.  Lots of
  1818. //    things we do, ranging from notifying the user to closing the line
  1819. //    to actually connecting to the target of our phone call.
  1820. //
  1821. //    What we do is usually obvious based on the call state change.
  1822. //
  1823.  
  1824. void DoLineCallState(
  1825.     DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
  1826.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  1827. {
  1828.  
  1829.     // Error if this CALLSTATE doesn't apply to our call in progress.
  1830.     if ((HCALL) dwDevice != g_hCall)
  1831.     {
  1832.         OutputDebugPrintf("LINE_CALLSTATE: Unknown device ID '0x%lx'.",
  1833.             dwDevice);
  1834.         return;
  1835.     }
  1836.  
  1837.     // This sets the global g_dwCallState variable so if we are waiting
  1838.     // for a specific call state change, we will know when it happens.
  1839.     g_dwCallState = dwParam1;
  1840.     g_bCallStateReceived = TRUE;
  1841.  
  1842.     // dwParam3 contains changes to LINECALLPRIVILEGE, if there are any.
  1843.     switch (dwParam3)
  1844.     {
  1845.         case 0:
  1846.             break; // no change to call state
  1847.  
  1848.          // close line if we are made monitor.  Shouldn't happen!
  1849.          case LINECALLPRIVILEGE_MONITOR:
  1850.             OutputDebugString("line given monitor privilege; closing\n");
  1851.             HangupCall();
  1852.             return;
  1853.  
  1854.          // close line if we are made owner.  Shouldn't happen!
  1855.         case LINECALLPRIVILEGE_OWNER:
  1856.             OutputDebugString("line given owner privilege; closing\n");
  1857.             HangupCall();
  1858.             break;
  1859.  
  1860.         default: // Shouldn't happen!  All cases handled.
  1861.             OutputDebugString("Unknown LINECALLPRIVILEGE message: closing\n");
  1862.             HangupCall();
  1863.             return;
  1864.     }
  1865.  
  1866.     // dwParam1 is the specific CALLSTATE change that is occurring.
  1867.     switch (dwParam1)
  1868.     {
  1869.         case LINECALLSTATE_DIALTONE:
  1870.             UpdateStatusBar("Dial Tone",1,0);
  1871.             OutputDebugString("Dial Tone\n");
  1872.             break;
  1873.  
  1874.         case LINECALLSTATE_DIALING:
  1875.             UpdateStatusBar("Dialing Call",1,0);
  1876.             OutputDebugString("Dialing\n");
  1877.             break;
  1878.  
  1879.         case LINECALLSTATE_PROCEEDING:
  1880.             UpdateStatusBar("Call is Proceeding",1,0);
  1881.             OutputDebugString("Proceeding\n");
  1882.             break;
  1883.  
  1884.         case LINECALLSTATE_RINGBACK:
  1885.             UpdateStatusBar("RingBack",1,0);
  1886.             OutputDebugString("RingBack\n");
  1887.             break;
  1888.  
  1889.         case LINECALLSTATE_BUSY:
  1890.             UpdateStatusBar("Line is busy",1,0);
  1891.             OutputDebugString("Line busy, shutting down\n");
  1892.             HangupCall();
  1893.             break;
  1894.  
  1895.         case LINECALLSTATE_IDLE:
  1896.             UpdateStatusBar("Line is idle",1,0);
  1897.             OutputDebugString("Line idle\n");
  1898.             HangupCall();
  1899.             break;
  1900.  
  1901.         case LINECALLSTATE_SPECIALINFO:
  1902.             UpdateStatusBar(
  1903.                 "Special Info, probably couldn't dial number",1,0);
  1904.             OutputDebugString(
  1905.                 "Special Info, probably couldn't dial number\n");
  1906.             HangupCall();
  1907.             break;
  1908.  
  1909.         case LINECALLSTATE_DISCONNECTED:
  1910.         {
  1911.             LPSTR pszReasonDisconnected;
  1912.  
  1913.             switch (dwParam2)
  1914.             {
  1915.                 case LINEDISCONNECTMODE_NORMAL:
  1916.                     pszReasonDisconnected = "Remote Party Disconnected";
  1917.                     break;
  1918.  
  1919.                 case LINEDISCONNECTMODE_UNKNOWN:
  1920.                     pszReasonDisconnected = "Disconnected: Unknown reason";
  1921.                     break;
  1922.  
  1923.                 case LINEDISCONNECTMODE_REJECT:
  1924.                     pszReasonDisconnected = "Remote Party rejected call";
  1925.                     break;
  1926.  
  1927.                 case LINEDISCONNECTMODE_PICKUP:
  1928.                     pszReasonDisconnected = 
  1929.                         "Disconnected: Local phone picked up";
  1930.                     break;
  1931.  
  1932.                 case LINEDISCONNECTMODE_FORWARDED:
  1933.                     pszReasonDisconnected = "Disconnected: Forwarded";
  1934.                     break;
  1935.  
  1936.                 case LINEDISCONNECTMODE_BUSY:
  1937.                     pszReasonDisconnected = "Disconnected: Busy";
  1938.                     break;
  1939.  
  1940.                 case LINEDISCONNECTMODE_NOANSWER:
  1941.                     pszReasonDisconnected = "Disconnected: No Answer";
  1942.                     break;
  1943.  
  1944.                 case LINEDISCONNECTMODE_BADADDRESS:
  1945.                     pszReasonDisconnected = "Disconnected: Bad Address";
  1946.                     break;
  1947.  
  1948.                 case LINEDISCONNECTMODE_UNREACHABLE:
  1949.                     pszReasonDisconnected = "Disconnected: Unreachable";
  1950.                     break;
  1951.  
  1952.                 case LINEDISCONNECTMODE_CONGESTION:
  1953.                     pszReasonDisconnected = "Disconnected: Congestion";
  1954.                     break;
  1955.  
  1956.                 case LINEDISCONNECTMODE_INCOMPATIBLE:
  1957.                     pszReasonDisconnected = "Disconnected: Incompatible";
  1958.                     break;
  1959.  
  1960.                 case LINEDISCONNECTMODE_UNAVAIL:
  1961.                     pszReasonDisconnected = "Disconnected: Unavail";
  1962.                     break;
  1963.  
  1964.                 case LINEDISCONNECTMODE_NODIALTONE:
  1965.                     pszReasonDisconnected = "Disconnected: No Dial Tone";
  1966.                     break;
  1967.  
  1968.                 default:
  1969.                     pszReasonDisconnected = 
  1970.                         "Disconnected: LINECALLSTATE; Bad Reason";
  1971.                     break;
  1972.  
  1973.             }
  1974.  
  1975.             UpdateStatusBar(pszReasonDisconnected,1,0);
  1976.             OutputDebugString(pszReasonDisconnected);
  1977.             OutputDebugString("\n");
  1978.             HangupCall();
  1979.             break;
  1980.         }
  1981.  
  1982.         
  1983.         case LINECALLSTATE_CONNECTED:  // CONNECTED!!!
  1984.         {
  1985.             LPVARSTRING lpVarString = NULL;
  1986.             DWORD dwSizeofVarString = sizeof(VARSTRING) + 1024;
  1987.             HANDLE hCommFile = NULL;
  1988.             long lReturn;
  1989.  
  1990.             // Very first, make sure this isn't a duplicated message.
  1991.             // A CALLSTATE message can be sent whenever there is a
  1992.             // change to the capabilities of a line, meaning that it is
  1993.             // possible to receive multiple CONNECTED messages per call.
  1994.             // The CONNECTED CALLSTATE message is the only one in TapiComm
  1995.             // where it would cause problems if it where sent more
  1996.             // than once.
  1997.  
  1998.             if (g_bConnected)
  1999.                 break;
  2000.  
  2001.             g_bConnected = TRUE;
  2002.  
  2003.             // Get the handle to the comm port from the driver so we can start
  2004.             // communicating.  This is returned in a LPVARSTRING structure.
  2005.             do
  2006.             {
  2007.                 // Allocate the VARSTRING structure
  2008.                 lpVarString = CheckAndReAllocBuffer((LPVOID) lpVarString,
  2009.                     dwSizeofVarString,"lineGetID: ");
  2010.  
  2011.                 if (lpVarString == NULL)
  2012.                     goto ErrorConnecting;
  2013.  
  2014.                 // Fill the VARSTRING structure
  2015.                 lReturn = lineGetID(0, 0, g_hCall, LINECALLSELECT_CALL,
  2016.                     lpVarString, "comm/datamodem");
  2017.  
  2018.                 if (HandleLineErr(lReturn))
  2019.                     ; // Still need to check if structure was big enough.
  2020.                 else
  2021.                 {
  2022.                     OutputDebugLineError(lReturn, 
  2023.                         "lineGetID unhandled error: ");
  2024.                     goto ErrorConnecting;
  2025.                 }
  2026.  
  2027.                 // If the VARSTRING wasn't big enough, loop again.
  2028.                 if ((lpVarString -> dwNeededSize) > (lpVarString -> dwTotalSize))
  2029.                 {
  2030.                     dwSizeofVarString = lpVarString -> dwNeededSize;
  2031.                     lReturn = -1; // Lets loop again.
  2032.                 }
  2033.             }
  2034.             while(lReturn != SUCCESS);
  2035.  
  2036.             OutputDebugString("Connected!  Starting communications!\n");
  2037.  
  2038.             // Again, the handle to the comm port is contained in a
  2039.             // LPVARSTRING structure.  Thus, the handle is the very first
  2040.             // thing after the end of the structure.  Note that the name of
  2041.             // the comm port is right after the handle, but I don't want it.
  2042.             hCommFile = 
  2043.                 *((LPHANDLE)((LPBYTE)lpVarString +
  2044.                     lpVarString -> dwStringOffset));
  2045.  
  2046.             // Started communications!
  2047.             if (StartComm(hCommFile))
  2048.             {
  2049.                 char szBuff[300];
  2050.  
  2051.                 wsprintf(szBuff,"Connected to '%s'",g_szDisplayableAddress);
  2052.                 UpdateStatusBar(szBuff, 1, 0);
  2053.  
  2054.                 LocalFree(lpVarString);
  2055.                 break;
  2056.             }
  2057.  
  2058.             // Couldn't start communications.  Clean up instead.
  2059.           ErrorConnecting:
  2060.  
  2061.             // Its very important that we close all Win32 handles.
  2062.             // The CommCode module is responsible for closing the hCommFile
  2063.             // handle if it succeeds in starting communications.
  2064.             if (hCommFile)
  2065.                 CloseHandle(hCommFile);
  2066.  
  2067.             HangupCall();
  2068.             {
  2069.                 char szBuff[300];
  2070.                 wsprintf(szBuff,"Failed to Connect to '%s'",
  2071.                     g_szDisplayableAddress);
  2072.                 UpdateStatusBar(szBuff, 1, 0);
  2073.             }
  2074.  
  2075.             if (lpVarString)
  2076.                 LocalFree(lpVarString);
  2077.  
  2078.             break;
  2079.         }
  2080.  
  2081.         default:
  2082.             OutputDebugString("Unhandled LINECALLSTATE message\n");
  2083.             break;
  2084.     }
  2085. }
  2086.  
  2087. //**************************************************
  2088. // line API Wrapper Functions.
  2089. //**************************************************
  2090.  
  2091.  
  2092. //
  2093. //  FUNCTION: LPVOID CheckAndReAllocBuffer(LPVOID, size_t, LPCSTR)
  2094. //
  2095. //  PURPOSE: Checks and ReAllocates a buffer if necessary.
  2096. //
  2097. //  PARAMETERS:
  2098. //    lpBuffer          - Pointer to buffer to be checked.  Can be NULL.
  2099. //    sizeBufferMinimum - Minimum size that lpBuffer should be.
  2100. //    szApiPhrase       - Phrase to print if an error occurs.
  2101. //
  2102. //  RETURN VALUE:
  2103. //    Returns a pointer to a valid buffer that is guarenteed to be
  2104. //    at least sizeBufferMinimum size.
  2105. //    Returns NULL if an error occured.
  2106. //
  2107. //  COMMENTS:
  2108. //
  2109. //    This function is a helper function intended to make all of the 
  2110. //    line API Wrapper Functions much simplier.  It allocates (or
  2111. //    reallocates) a buffer of the requested size.
  2112. //
  2113. //    The returned pointer has been allocated with LocalAlloc,
  2114. //    so LocalFree has to be called on it when you're finished with it,
  2115. //    or there will be a memory leak.
  2116. //
  2117. //    Similarly, if a pointer is passed in, it *must* have been allocated
  2118. //    with LocalAlloc and it could potentially be LocalFree()d.
  2119. //
  2120. //    If lpBuffer == NULL, then a new buffer is allocated.  It is
  2121. //    normal to pass in NULL for this parameter the first time and only
  2122. //    pass in a pointer if the buffer needs to be reallocated.
  2123. //
  2124. //    szApiPhrase is used only for debugging purposes.
  2125. //
  2126. //    It is assumed that the buffer returned from this function will be used
  2127. //    to contain a variable sized structure.  Thus, the dwTotalSize field
  2128. //    is always filled in before returning the pointer.
  2129. //
  2130. //
  2131.  
  2132. LPVOID CheckAndReAllocBuffer(
  2133.     LPVOID lpBuffer, size_t sizeBufferMinimum, LPCSTR szApiPhrase)
  2134. {
  2135.     size_t sizeBuffer;
  2136.  
  2137.     if (lpBuffer == NULL)  // Allocate the buffer if necessary. 
  2138.     {
  2139.         sizeBuffer = sizeBufferMinimum;
  2140.         lpBuffer = (LPVOID) LocalAlloc(LPTR, sizeBuffer);
  2141.             
  2142.         if (lpBuffer == NULL)
  2143.         {
  2144.             OutputDebugString(szApiPhrase);
  2145.             OutputDebugLastError(GetLastError(),"LocalAlloc : ");
  2146.             HandleNoMem();
  2147.             return NULL;
  2148.         }
  2149.     }
  2150.     else // If the structure already exists, make sure its good.
  2151.     {
  2152.         sizeBuffer = LocalSize((HLOCAL) lpBuffer);
  2153.  
  2154.         if (sizeBuffer == 0) // Bad pointer?
  2155.         {
  2156.             OutputDebugString(szApiPhrase);
  2157.             OutputDebugLastError(GetLastError(),"LocalSize : ");
  2158.             return NULL;
  2159.         }
  2160.  
  2161.         // Was the buffer big enough for the structure?
  2162.         if (sizeBuffer < sizeBufferMinimum)
  2163.         {
  2164.             OutputDebugString(szApiPhrase);
  2165.             OutputDebugString("Reallocating structure\n");
  2166.             LocalFree(lpBuffer);
  2167.             return CheckAndReAllocBuffer(NULL, sizeBufferMinimum, szApiPhrase);
  2168.         }
  2169.  
  2170.         // Lets zero the buffer out.
  2171.         memset(lpBuffer, 0, sizeBuffer);
  2172.     }
  2173.                 
  2174.     ((LPVARSTRING) lpBuffer ) -> dwTotalSize = (DWORD) sizeBuffer;
  2175.     return lpBuffer;
  2176. }
  2177.  
  2178.  
  2179.  
  2180. //
  2181. //  FUNCTION: DWORD I_lineNegotiateAPIVersion(DWORD)
  2182. //
  2183. //  PURPOSE: Negotiate an API Version to use for a specific device.
  2184. //
  2185. //  PARAMETERS:
  2186. //    dwDeviceID - device to negotiate an API Version for.
  2187. //
  2188. //  RETURN VALUE:
  2189. //    Returns the API Version to use for this line if successful.
  2190. //    Returns 0 if negotiations fall through.
  2191. //
  2192. //  COMMENTS:
  2193. //
  2194. //    This wrapper function not only negotiates the API, but handles
  2195. //    LINEERR errors that can occur while negotiating.
  2196. //
  2197. //
  2198.  
  2199. DWORD I_lineNegotiateAPIVersion(DWORD dwDeviceID)
  2200. {
  2201.     LINEEXTENSIONID LineExtensionID;
  2202.     long lReturn;
  2203.     DWORD dwLocalAPIVersion;
  2204.     
  2205.     do
  2206.     {
  2207.         lReturn = lineNegotiateAPIVersion(g_hLineApp, dwDeviceID, 
  2208.             SAMPLE_TAPI_VERSION, SAMPLE_TAPI_VERSION,
  2209.             &dwLocalAPIVersion, &LineExtensionID);
  2210.  
  2211.         if (lReturn == LINEERR_INCOMPATIBLEAPIVERSION)
  2212.         {
  2213.             OutputDebugString(
  2214.                 "lineNegotiateAPIVersion, INCOMPATIBLEAPIVERSION.\n");
  2215.             return 0;
  2216.         }
  2217.  
  2218.         if (HandleLineErr(lReturn))
  2219.             continue;
  2220.         else
  2221.         {
  2222.             OutputDebugLineError(lReturn, 
  2223.                 "lineNegotiateAPIVersion unhandled error: ");
  2224.             return 0;
  2225.         }
  2226.     }
  2227.     while(lReturn != SUCCESS);
  2228.         
  2229.     return dwLocalAPIVersion;
  2230. }
  2231.  
  2232.  
  2233. //
  2234. //  FUNCTION: I_lineGetDevCaps(LPLINEDEVCAPS, DWORD , DWORD)
  2235. //
  2236. //  PURPOSE: Retrieve a LINEDEVCAPS structure for the specified line.
  2237. //
  2238. //  PARAMETERS:
  2239. //    lpLineDevCaps - Pointer to a LINEDEVCAPS structure to use.
  2240. //    dwDeviceID    - device to get the DevCaps for.
  2241. //    dwAPIVersion  - API Version to use while getting DevCaps.
  2242. //
  2243. //  RETURN VALUE:
  2244. //    Returns a pointer to a LINEDEVCAPS structure if successful.
  2245. //    Returns NULL if unsuccessful.
  2246. //
  2247. //  COMMENTS:
  2248. //
  2249. //    This function is a wrapper around lineGetDevCaps to make it easy
  2250. //    to handle the variable sized structure and any errors received.
  2251. //    
  2252. //    The returned structure has been allocated with LocalAlloc,
  2253. //    so LocalFree has to be called on it when you're finished with it,
  2254. //    or there will be a memory leak.
  2255. //
  2256. //    Similarly, if a lpLineDevCaps structure is passed in, it *must*
  2257. //    have been allocated with LocalAlloc and it could potentially be 
  2258. //    LocalFree()d.
  2259. //
  2260. //    If lpLineDevCaps == NULL, then a new structure is allocated.  It is
  2261. //    normal to pass in NULL for this parameter unless you want to use a 
  2262. //    lpLineDevCaps that has been returned by a previous I_lineGetDevCaps
  2263. //    call.
  2264. //
  2265. //
  2266.  
  2267. LPLINEDEVCAPS I_lineGetDevCaps(
  2268.     LPLINEDEVCAPS lpLineDevCaps,
  2269.     DWORD dwDeviceID, DWORD dwAPIVersion)
  2270. {
  2271.     size_t sizeofLineDevCaps = sizeof(LINEDEVCAPS) + 1024;
  2272.     long lReturn;
  2273.     
  2274.     // Continue this loop until the structure is big enough.
  2275.     while(TRUE)
  2276.     {
  2277.         // Make sure the buffer exists, is valid and big enough.
  2278.         lpLineDevCaps = 
  2279.             (LPLINEDEVCAPS) CheckAndReAllocBuffer(
  2280.                 (LPVOID) lpLineDevCaps, // Pointer to existing buffer, if any
  2281.                 sizeofLineDevCaps,      // Minimum size the buffer should be
  2282.                 "lineGetDevCaps");      // Phrase to tag errors, if any.
  2283.  
  2284.         if (lpLineDevCaps == NULL)
  2285.             return NULL;
  2286.  
  2287.         // Make the call to fill the structure.
  2288.         do
  2289.         {            
  2290.             lReturn = 
  2291.                 lineGetDevCaps(g_hLineApp, 
  2292.                     dwDeviceID, dwAPIVersion, 0, lpLineDevCaps);
  2293.  
  2294.             if (HandleLineErr(lReturn))
  2295.                 continue;
  2296.             else
  2297.             {
  2298.                 OutputDebugLineError(lReturn, 
  2299.                     "lineGetDevCaps unhandled error: ");
  2300.                 LocalFree(lpLineDevCaps);
  2301.                 return NULL;
  2302.             }
  2303.         }
  2304.         while (lReturn != SUCCESS);
  2305.  
  2306.         // If the buffer was big enough, then succeed.
  2307.         if ((lpLineDevCaps -> dwNeededSize) <= (lpLineDevCaps -> dwTotalSize))
  2308.             return lpLineDevCaps;
  2309.  
  2310.         // Buffer wasn't big enough.  Make it bigger and try again.
  2311.         sizeofLineDevCaps = lpLineDevCaps -> dwNeededSize;
  2312.     }
  2313. }
  2314.  
  2315.  
  2316. //
  2317. //  FUNCTION: I_lineGetAddressStatus(LPLINEADDRESSSTATUS, HLINE, DWORD)
  2318. //
  2319. //  PURPOSE: Retrieve a LINEADDRESSSTATUS structure for the specified line.
  2320.  
  2321.  
  2322. //
  2323. //  PARAMETERS:
  2324. //    lpLineAddressStatus - Pointer to a LINEADDRESSSTATUS structure to use.
  2325. //    hLine       - Handle of line to get the AddressStatus of.
  2326. //    dwAddressID - Address ID on the hLine to be used.
  2327. //
  2328. //  RETURN VALUE:
  2329. //    Returns a pointer to a LINEADDRESSSTATUS structure if successful.
  2330. //    Returns NULL if unsuccessful.
  2331. //
  2332. //  COMMENTS:
  2333. //
  2334. //    This function is a wrapper around lineGetAddressStatus to make it easy
  2335. //    to handle the variable sized structure and any errors received.
  2336. //
  2337. //    The returned structure has been allocated with LocalAlloc,
  2338. //    so LocalFree has to be called on it when you're finished with it,
  2339. //    or there will be a memory leak.
  2340. //
  2341. //    Similarly, if a lpLineAddressStatus structure is passed in, it *must*
  2342. //    have been allocated with LocalAlloc and it could potentially be 
  2343. //    LocalFree()d.
  2344. //
  2345. //    If lpLineAddressStatus == NULL, then a new structure is allocated.  It
  2346. //    is normal to pass in NULL for this parameter unless you want to use a
  2347. //    lpLineAddressStatus that has been returned by previous
  2348. //    I_lineGetAddressStatus call.
  2349. //
  2350. //
  2351.  
  2352. LPLINEADDRESSSTATUS I_lineGetAddressStatus(
  2353.     LPLINEADDRESSSTATUS lpLineAddressStatus,
  2354.     HLINE hLine, DWORD dwAddressID)
  2355. {
  2356.     size_t sizeofLineAddressStatus = sizeof(LINEADDRESSSTATUS) + 1024;
  2357.     long lReturn;
  2358.     
  2359.     // Continue this loop until the structure is big enough.
  2360.     while(TRUE)
  2361.     {
  2362.         // Make sure the buffer exists, is valid and big enough.
  2363.         lpLineAddressStatus = 
  2364.             (LPLINEADDRESSSTATUS) CheckAndReAllocBuffer(
  2365.                 (LPVOID) lpLineAddressStatus,
  2366.                 sizeofLineAddressStatus,     
  2367.                 "lineGetAddressStatus");     
  2368.  
  2369.         if (lpLineAddressStatus == NULL)
  2370.             return NULL;
  2371.  
  2372.         // Make the call to fill the structure.
  2373.         do
  2374.         {            
  2375.             lReturn = 
  2376.                 lineGetAddressStatus(hLine, dwAddressID, lpLineAddressStatus);
  2377.  
  2378.             if (HandleLineErr(lReturn))
  2379.                 continue;
  2380.             else
  2381.             {
  2382.                 OutputDebugLineError(lReturn, 
  2383.                     "lineGetAddressStatus unhandled error: ");
  2384.                 LocalFree(lpLineAddressStatus);
  2385.                 return NULL;
  2386.             }
  2387.         }
  2388.         while (lReturn != SUCCESS);
  2389.  
  2390.         // If the buffer was big enough, then succeed.
  2391.         if ((lpLineAddressStatus -> dwNeededSize) <= 
  2392.             (lpLineAddressStatus -> dwTotalSize))
  2393.         {
  2394.             return lpLineAddressStatus;
  2395.         }
  2396.         
  2397.         // Buffer wasn't big enough.  Make it bigger and try again.
  2398.         sizeofLineAddressStatus = lpLineAddressStatus -> dwNeededSize;
  2399.     }
  2400. }
  2401.  
  2402.  
  2403. //
  2404. //  FUNCTION: I_lineGetCallStatus(LPLINECALLSTATUS, HCALL)
  2405. //
  2406. //  PURPOSE: Retrieve a LINECALLSTATUS structure for the specified line.
  2407. //
  2408. //  PARAMETERS:
  2409. //    lpLineCallStatus - Pointer to a LINECALLSTATUS structure to use.
  2410. //    hCall - Handle of call to get the CallStatus of.
  2411. //
  2412. //  RETURN VALUE:
  2413. //    Returns a pointer to a LINECALLSTATUS structure if successful.
  2414. //    Returns NULL if unsuccessful.
  2415. //
  2416. //  COMMENTS:
  2417. //
  2418. //    This function is a wrapper around lineGetCallStatus to make it easy
  2419. //    to handle the variable sized structure and any errors received.
  2420. //
  2421. //    The returned structure has been allocated with LocalAlloc,
  2422. //    so LocalFree has to be called on it when you're finished with it,
  2423. //    or there will be a memory leak.
  2424. //
  2425. //    Similarly, if a lpLineCallStatus structure is passed in, it *must*
  2426. //    have been allocated with LocalAlloc and it could potentially be 
  2427. //    LocalFree()d.
  2428. //
  2429. //    If lpLineCallStatus == NULL, then a new structure is allocated.  It
  2430. //    is normal to pass in NULL for this parameter unless you want to use a
  2431. //    lpLineCallStatus that has been returned by previous I_lineGetCallStatus
  2432. //    call.
  2433. //
  2434. //
  2435.  
  2436. LPLINECALLSTATUS I_lineGetCallStatus(
  2437.     LPLINECALLSTATUS lpLineCallStatus,
  2438.     HCALL hCall)
  2439. {
  2440.     size_t sizeofLineCallStatus = sizeof(LINECALLSTATUS) + 1024;
  2441.     long lReturn;
  2442.     
  2443.     // Continue this loop until the structure is big enough.
  2444.     while(TRUE)
  2445.     {
  2446.         // Make sure the buffer exists, is valid and big enough.
  2447.         lpLineCallStatus = 
  2448.             (LPLINECALLSTATUS) CheckAndReAllocBuffer(
  2449.                 (LPVOID) lpLineCallStatus,
  2450.                 sizeofLineCallStatus,     
  2451.                 "lineGetCallStatus");     
  2452.  
  2453.         if (lpLineCallStatus == NULL)
  2454.             return NULL;
  2455.             
  2456.         // Make the call to fill the structure.
  2457.         do
  2458.         {
  2459.             lReturn = 
  2460.                 lineGetCallStatus(hCall, lpLineCallStatus);
  2461.  
  2462.             if (HandleLineErr(lReturn))
  2463.                 continue;
  2464.             else
  2465.             {
  2466.                 OutputDebugLineError(lReturn, 
  2467.                     "lineGetCallStatus unhandled error: ");
  2468.                 LocalFree(lpLineCallStatus);
  2469.                 return NULL;
  2470.             }
  2471.         }
  2472.         while (lReturn != SUCCESS);
  2473.  
  2474.         // If the buffer was big enough, then succeed.
  2475.         if ((lpLineCallStatus -> dwNeededSize) <= 
  2476.             (lpLineCallStatus -> dwTotalSize))
  2477.         {
  2478.             return lpLineCallStatus;
  2479.         }
  2480.  
  2481.         // Buffer wasn't big enough.  Make it bigger and try again.
  2482.         sizeofLineCallStatus = lpLineCallStatus -> dwNeededSize;
  2483.     }
  2484. }
  2485.  
  2486.  
  2487. //
  2488. //  FUNCTION: I_lineTranslateAddress
  2489. //              (LPLINETRANSLATEOUTPUT, DWORD, DWORD, LPCSTR)
  2490. //
  2491. //  PURPOSE: Retrieve a LINECALLSTATUS structure for the specified line.
  2492. //
  2493. //  PARAMETERS:
  2494. //    lpLineTranslateOutput - Pointer to a LINETRANSLATEOUTPUT structure.
  2495. //    dwDeviceID      - Device that we're translating for.
  2496. //    dwAPIVersion    - API Version to use.
  2497. //    lpszDialAddress - pointer to the DialAddress string to translate.
  2498. //
  2499. //  RETURN VALUE:
  2500. //    Returns a pointer to a LINETRANSLATEOUTPUT structure if successful.
  2501. //    Returns NULL if unsuccessful.
  2502. //
  2503. //  COMMENTS:
  2504. //
  2505. //    This function is a wrapper around lineGetTranslateOutput to make it
  2506. //    easy to handle the variable sized structure and any errors received.
  2507. //
  2508. //    The returned structure has been allocated with LocalAlloc,
  2509. //    so LocalFree has to be called on it when you're finished with it,
  2510. //    or there will be a memory leak.
  2511. //
  2512. //    Similarly, if a lpLineTranslateOutput structure is passed in, it
  2513. //    *must* have been allocated with LocalAlloc and it could potentially be 
  2514. //    LocalFree()d.
  2515. //
  2516. //    If lpLineTranslateOutput == NULL, then a new structure is allocated.
  2517. //    It is normal to pass in NULL for this parameter unless you want to use
  2518. //    a lpLineTranslateOutput that has been returned by previous 
  2519. //    I_lineTranslateOutput call.
  2520. //
  2521. //
  2522.  
  2523. LPLINETRANSLATEOUTPUT I_lineTranslateAddress(
  2524.     LPLINETRANSLATEOUTPUT lpLineTranslateOutput,
  2525.     DWORD dwDeviceID, DWORD dwAPIVersion,
  2526.     LPCSTR lpszDialAddress)
  2527. {
  2528.     size_t sizeofLineTranslateOutput = sizeof(LINETRANSLATEOUTPUT) + 1024;
  2529.     long lReturn;
  2530.     
  2531.     // Continue this loop until the structure is big enough.
  2532.     while(TRUE)
  2533.     {
  2534.         // Make sure the buffer exists, is valid and big enough.
  2535.         lpLineTranslateOutput = 
  2536.             (LPLINETRANSLATEOUTPUT) CheckAndReAllocBuffer(
  2537.                 (LPVOID) lpLineTranslateOutput,
  2538.                 sizeofLineTranslateOutput,
  2539.                 "lineTranslateOutput");
  2540.  
  2541.         if (lpLineTranslateOutput == NULL)
  2542.             return NULL;
  2543.  
  2544.         // Make the call to fill the structure.
  2545.         do
  2546.         {
  2547.             // Note that CALLWAITING is disabled 
  2548.             // (assuming the service provider can disable it)
  2549.             lReturn = 
  2550.                 lineTranslateAddress(g_hLineApp, dwDeviceID, dwAPIVersion,
  2551.                     lpszDialAddress, 0, 
  2552.                     LINETRANSLATEOPTION_CANCELCALLWAITING,
  2553.                     lpLineTranslateOutput);
  2554.  
  2555.             // If the address isn't translatable, notify the user.
  2556.             if (lReturn == LINEERR_INVALADDRESS)
  2557.                 MessageBox(g_hDlgParentWindow, 
  2558.                     "Unable to translate phone number","Warning",MB_OK);
  2559.  
  2560.             if (HandleLineErr(lReturn))
  2561.                 continue;
  2562.             else
  2563.             {
  2564.                 OutputDebugLineError(lReturn, 
  2565.                     "lineTranslateOutput unhandled error: ");
  2566.                 LocalFree(lpLineTranslateOutput);
  2567.                 return NULL;
  2568.             }
  2569.         }
  2570.         while (lReturn != SUCCESS);
  2571.  
  2572.         // If the buffer was big enough, then succeed.
  2573.         if ((lpLineTranslateOutput -> dwNeededSize) <= 
  2574.             (lpLineTranslateOutput -> dwTotalSize))
  2575.         {
  2576.             return lpLineTranslateOutput;
  2577.         }
  2578.  
  2579.         // Buffer wasn't big enough.  Make it bigger and try again.
  2580.         sizeofLineTranslateOutput = lpLineTranslateOutput -> dwNeededSize;
  2581.     }
  2582. }
  2583.  
  2584.  
  2585. //
  2586. //  FUNCTION: I_lineGetAddressCaps(LPLINEADDRESSCAPS, ..)
  2587. //
  2588. //  PURPOSE: Retrieve a LINEADDRESSCAPS structure for the specified line.
  2589. //
  2590. //  PARAMETERS:
  2591. //    lpLineAddressCaps - Pointer to a LINEADDRESSCAPS, or NULL.
  2592. //    dwDeviceID        - Device to get the address caps for.
  2593. //    dwAddressID       - This sample always assumes the first address.
  2594. //    dwAPIVersion      - API version negotiated for the device.
  2595. //    dwExtVersion      - Always 0 for this sample.
  2596. //
  2597. //  RETURN VALUE:
  2598. //    Returns a pointer to a LINEADDRESSCAPS structure if successful.
  2599. //    Returns NULL if unsuccessful.
  2600. //
  2601. //  COMMENTS:
  2602. //
  2603. //    This function is a wrapper around lineGetAddressCaps to make it easy
  2604. //    to handle the variable sized structure and any errors received.
  2605. //
  2606. //    The returned structure has been allocated with LocalAlloc,
  2607. //    so LocalFree has to be called on it when you're finished with it,
  2608. //    or there will be a memory leak.
  2609. //
  2610. //    Similarly, if a lpLineAddressCaps structure is passed in, it *must*
  2611. //    have been allocated with LocalAlloc and it could potentially be 
  2612. //    LocalFree()d.  It also *must* have the dwTotalSize field set.
  2613. //
  2614. //    If lpLineAddressCaps == NULL, then a new structure is allocated.  It
  2615. //    is normal to pass in NULL for this parameter unless you want to use a
  2616. //    lpLineCallStatus that has been returned by previous I_lineGetAddressCaps
  2617. //    call.
  2618. //
  2619. //
  2620.  
  2621. LPLINEADDRESSCAPS I_lineGetAddressCaps (
  2622.     LPLINEADDRESSCAPS lpLineAddressCaps,
  2623.     DWORD dwDeviceID, DWORD dwAddressID,
  2624.     DWORD dwAPIVersion, DWORD dwExtVersion)
  2625. {
  2626.     size_t sizeofLineAddressCaps = sizeof(LINEADDRESSCAPS) + 1024;
  2627.     long lReturn;
  2628.     
  2629.     // Continue this loop until the structure is big enough.
  2630.     while(TRUE)
  2631.     {
  2632.         // Make sure the buffer exists, is valid and big enough.
  2633.         lpLineAddressCaps = 
  2634.             (LPLINEADDRESSCAPS) CheckAndReAllocBuffer(
  2635.                 (LPVOID) lpLineAddressCaps,
  2636.                 sizeofLineAddressCaps,
  2637.                 "lineGetAddressCaps");
  2638.  
  2639.         if (lpLineAddressCaps == NULL)
  2640.             return NULL;
  2641.             
  2642.         // Make the call to fill the structure.
  2643.         do
  2644.         {
  2645.             lReturn = 
  2646.                 lineGetAddressCaps(g_hLineApp,
  2647.                     dwDeviceID, dwAddressID, dwAPIVersion, dwExtVersion,
  2648.                     lpLineAddressCaps);
  2649.  
  2650.             if (HandleLineErr(lReturn))
  2651.                 continue;
  2652.             else
  2653.             {
  2654.                 OutputDebugLineError(lReturn, 
  2655.                     "lineGetAddressCaps unhandled error: ");
  2656.                 LocalFree(lpLineAddressCaps);
  2657.                 return NULL;
  2658.             }
  2659.         }
  2660.         while (lReturn != SUCCESS);
  2661.  
  2662.         // If the buffer was big enough, then succeed.
  2663.         if ((lpLineAddressCaps -> dwNeededSize) <= 
  2664.             (lpLineAddressCaps -> dwTotalSize))
  2665.         {
  2666.             return lpLineAddressCaps;
  2667.         }
  2668.  
  2669.         // Buffer wasn't big enough.  Make it bigger and try again.
  2670.         sizeofLineAddressCaps = lpLineAddressCaps -> dwNeededSize;
  2671.     }
  2672. }
  2673.  
  2674.  
  2675.  
  2676. //**************************************************
  2677. // LINEERR Error Handlers
  2678. //**************************************************
  2679.  
  2680.  
  2681. //
  2682. //  FUNCTION: HandleLineErr(long)
  2683. //
  2684. //  PURPOSE: Handle several standard LINEERR errors
  2685. //
  2686. //  PARAMETERS:
  2687. //    lLineErr - Error code to be handled.
  2688. //
  2689. //  RETURN VALUE:
  2690. //    Return TRUE if lLineErr wasn't an error, or if the
  2691. //      error was successfully handled and cleared up.
  2692. //    Return FALSE if lLineErr was an unhandled error.
  2693. //
  2694. //  COMMENTS:
  2695. //
  2696. //    This is the main error handler for all TAPI line APIs.
  2697. //    It handles (by correcting or just notifying the user)
  2698. //    most of the errors that can occur while using TAPI line APIs.
  2699. //
  2700. //    Note that many errors still return FALSE (unhandled) even
  2701. //    if a dialog is displayed.  Often, the dialog is just notifying
  2702. //    the user why the action was canceled.
  2703. //    
  2704. //
  2705. //
  2706.  
  2707. BOOL HandleLineErr(long lLineErr)
  2708. {
  2709.     // lLineErr is really an async request ID, not an error.
  2710.     if (lLineErr > SUCCESS)
  2711.         return FALSE;
  2712.  
  2713.     // All we do is dispatch the correct error handler.
  2714.     switch(lLineErr)
  2715.     {
  2716.         case SUCCESS:
  2717.             return TRUE;
  2718.  
  2719.         case LINEERR_INVALCARD:
  2720.         case LINEERR_INVALLOCATION:
  2721.         case LINEERR_INIFILECORRUPT:
  2722.             return HandleIniFileCorrupt();
  2723.  
  2724.         case LINEERR_NODRIVER:
  2725.             return HandleNoDriver();
  2726.  
  2727.         case LINEERR_REINIT:
  2728.             return HandleReInit();
  2729.  
  2730.         case LINEERR_NOMULTIPLEINSTANCE:
  2731.             return HandleNoMultipleInstance();
  2732.  
  2733.         case LINEERR_NOMEM:
  2734.             return HandleNoMem();
  2735.  
  2736.         case LINEERR_OPERATIONFAILED:
  2737.             return HandleOperationFailed();
  2738.  
  2739.         case LINEERR_RESOURCEUNAVAIL:
  2740.             return HandleResourceUnavail();
  2741.  
  2742.         // Unhandled errors fail.
  2743.         default:
  2744.             return FALSE;
  2745.     }
  2746. }
  2747.  
  2748.  
  2749.  
  2750. //
  2751. //  FUNCTION: HandleIniFileCorrupt
  2752. //
  2753. //  PURPOSE: Handle INIFILECORRUPT error.
  2754. //
  2755. //  PARAMETERS:
  2756. //    none
  2757. //
  2758. //  RETURN VALUE:
  2759. //    TRUE  - error was corrected.
  2760. //    FALSE - error was not corrected.
  2761. //
  2762. //  COMMENTS:
  2763. //
  2764. //    This error shouldn't happen under Windows 95 anymore.  The TAPI.DLL
  2765. //    takes care of correcting this problem.  If it does happen, just
  2766. //    notify the user.
  2767. //
  2768.  
  2769. BOOL HandleIniFileCorrupt()
  2770. {
  2771.     if (IDCANCEL == MessageBox(g_hDlgParentWindow, 
  2772.         "Configuration information relating to Telephony "
  2773.         "services appears to be corrupt.\n"
  2774.         "This could be the first time you have used the Telephony services.\n"
  2775.         "Would you like to configure the Telephony services?",
  2776.         "Warning",MB_OKCANCEL))
  2777.     {
  2778.         return FALSE;
  2779.     }
  2780.  
  2781.     lineTranslateDialog(g_hLineApp, 0, SAMPLE_TAPI_VERSION, 
  2782.         g_hDlgParentWindow, NULL);
  2783.  
  2784.     return TRUE;
  2785. }
  2786.  
  2787.  
  2788. //
  2789. //  FUNCTION: HandleNoDriver
  2790. //
  2791. //  PURPOSE: Handle NODRIVER error.
  2792. //
  2793. //  PARAMETERS:
  2794. //    none
  2795. //
  2796. //  RETURN VALUE:
  2797. //    TRUE  - error was corrected.
  2798. //    FALSE - error was not corrected.
  2799. //
  2800. //  COMMENTS:
  2801. //
  2802. //
  2803.  
  2804. BOOL HandleNoDriver()
  2805. {
  2806.     int mbRet;
  2807.     mbRet = MessageBox(g_hDlgParentWindow, 
  2808.         "One of the components of the Telephony device driver is missing.\n"
  2809.             "Use the Control Panel to set up the driver properly.",
  2810.         "Warning",MB_OK);
  2811.  
  2812.     return FALSE;
  2813. }
  2814.  
  2815.  
  2816. //
  2817. //  FUNCTION: HandleNoMultipleInstance
  2818. //
  2819. //  PURPOSE: Handle NOMULTIPLEINSTANCE error.
  2820. //
  2821. //  PARAMETERS:
  2822. //    none
  2823. //
  2824. //  RETURN VALUE:
  2825. //    TRUE  - error was corrected.
  2826. //    FALSE - error was not corrected.
  2827. //
  2828. //  COMMENTS:
  2829. //
  2830. //
  2831.  
  2832. BOOL HandleNoMultipleInstance()
  2833. {
  2834.     MessageBox(g_hDlgParentWindow, 
  2835.         "You have two copies of the same Telephony driver installed.\n"
  2836.             "Use the Control Panel to remove one of the copies.",
  2837.         "Warning",MB_OK);
  2838.         
  2839.     return FALSE;
  2840. }
  2841.  
  2842.  
  2843. //
  2844. //  FUNCTION: HandleReInit
  2845. //
  2846. //  PURPOSE: Handle REINIT error.
  2847. //
  2848. //  PARAMETERS:
  2849. //    none
  2850. //
  2851. //  RETURN VALUE:
  2852. //    TRUE  - error was corrected.
  2853. //    FALSE - error was not corrected.
  2854. //
  2855. //  COMMENTS:
  2856. //
  2857. //
  2858.  
  2859. BOOL HandleReInit()
  2860. {
  2861.     ShutdownTAPI();
  2862.     return FALSE;
  2863. }
  2864.  
  2865.  
  2866. //
  2867. //  FUNCTION: HandleNoMem
  2868. //
  2869. //  PURPOSE: Handle NOMEM error.
  2870. //
  2871. //  PARAMETERS:
  2872. //    none
  2873. //
  2874. //  RETURN VALUE:
  2875. //    TRUE  - error was corrected.
  2876. //    FALSE - error was not corrected.
  2877. //
  2878. //  COMMENTS:
  2879. //    This is also called if I run out of memory for LocalAlloc()s
  2880. //
  2881. //
  2882.  
  2883. BOOL HandleNoMem()
  2884. {
  2885.     MessageBox(g_hDlgParentWindow,
  2886.         "Out of Memory error, canceling action.","Error", MB_OK);
  2887.     return FALSE;
  2888. }
  2889.  
  2890.  
  2891. //
  2892. //  FUNCTION: HandleOperationFailed
  2893. //
  2894. //  PURPOSE: Handle OPERATIONFAILED error.
  2895. //
  2896. //  PARAMETERS:
  2897. //    none
  2898. //
  2899. //  RETURN VALUE:
  2900. //    TRUE  - error was corrected.
  2901. //    FALSE - error was not corrected.
  2902. //
  2903. //  COMMENTS:
  2904. //
  2905. //
  2906.  
  2907. BOOL HandleOperationFailed()
  2908. {
  2909.     MessageBox(g_hDlgParentWindow,
  2910.         "TAPI Operation Failed for unknown reasons.",
  2911.         "Error", MB_OK);
  2912.     return FALSE;
  2913. }
  2914.  
  2915.  
  2916. //
  2917. //  FUNCTION: HandleResourceUnavail
  2918. //
  2919. //  PURPOSE: Handle RESOURCEUNAVAIL error.
  2920. //
  2921. //  PARAMETERS:
  2922. //    none
  2923. //
  2924. //  RETURN VALUE:
  2925. //    TRUE  - error was corrected.
  2926. //    FALSE - error was not corrected.
  2927. //
  2928. //  COMMENTS:
  2929. //
  2930. //
  2931.  
  2932. BOOL HandleResourceUnavail()
  2933. {
  2934.     int mbRet;
  2935.  
  2936.     mbRet = MessageBox(g_hDlgParentWindow,
  2937.         "A Telephony resource is temporarily unavaiable.  "
  2938.         "This could mean a short wait is necessary or "
  2939.         "that a non-TAPI application is using the line.",
  2940.         "Warning",MB_RETRYCANCEL);
  2941.  
  2942.     if (mbRet == IDRETRY)
  2943.         return TRUE;
  2944.  
  2945.     return FALSE;
  2946. }
  2947.  
  2948.  
  2949. //
  2950. //  FUNCTION: HandleNoDevicesInstalled
  2951. //
  2952. //  PURPOSE: Handle cases when we know NODEVICE error
  2953. //    is returned because there are no devices installed.
  2954. //
  2955. //  PARAMETERS:
  2956. //    none
  2957. //
  2958. //  RETURN VALUE:
  2959. //    TRUE  - error was corrected.
  2960. //    FALSE - error was not corrected.
  2961. //
  2962. //  COMMENTS:
  2963. //
  2964. //    This function is not part of standard error handling
  2965. //    but is only used when we know that the NODEVICE error
  2966. //    means that no devices are installed.
  2967. //
  2968. //
  2969.  
  2970. BOOL HandleNoDevicesInstalled()
  2971. {
  2972.     int mbRet;
  2973.     mbRet = MessageBox(g_hDlgParentWindow, 
  2974.        "There are no devices configured for Telephony use.\n"
  2975.        "Would you like to run the Modem Control Panel to add a modem driver?",
  2976.        "Warning",MB_YESNO);
  2977.  
  2978.     if (mbRet == IDYES)
  2979.     {
  2980.         if (LaunchModemControlPanelAdd())
  2981.             return TRUE;
  2982.     }
  2983.         
  2984.     return FALSE;
  2985. }
  2986.  
  2987.  
  2988. //
  2989. //  FUNCTION: LaunchModemControlPanelAdd
  2990. //
  2991. //  PURPOSE: Launch Add Modem Control Panel applet.
  2992. //
  2993. //  PARAMETERS:
  2994. //    none
  2995. //
  2996. //  RETURN VALUE:
  2997. //    TRUE  - Control Panel launched successfully.
  2998. //    FALSE - It didn't.
  2999. //
  3000. //  COMMENTS:
  3001. //
  3002. //
  3003.  
  3004. BOOL LaunchModemControlPanelAdd()
  3005. {
  3006.     PROCESS_INFORMATION piProcInfo;
  3007.     STARTUPINFO siStartupInfo;
  3008.  
  3009.     siStartupInfo.cb = sizeof(STARTUPINFO);
  3010.     siStartupInfo.lpReserved = NULL;
  3011.     siStartupInfo.lpDesktop = NULL;
  3012.     siStartupInfo.lpTitle = NULL;
  3013.     siStartupInfo.dwFlags = STARTF_USESHOWWINDOW;
  3014.     siStartupInfo.wShowWindow = SW_SHOWNORMAL;
  3015.     siStartupInfo.cbReserved2 = 0;
  3016.     siStartupInfo.lpReserved2 = NULL;
  3017.     
  3018.     // The string to launch the modem control panel is *VERY* likely
  3019.     // to change on NT.  If nothing else, this is 'contrl32' on NT
  3020.     // instead of 'control'.
  3021.     if (CreateProcess(
  3022.             NULL,
  3023.             "CONTROL.EXE MODEM.CPL,,ADD",
  3024.             NULL, NULL, FALSE, 
  3025.             NORMAL_PRIORITY_CLASS,
  3026.             NULL, NULL, 
  3027.             &siStartupInfo, 
  3028.             &piProcInfo))
  3029.     {
  3030.         CloseHandle(piProcInfo.hThread);
  3031.  
  3032.  
  3033.         // Control panel 'Add New Modem' has been launched.  Now we should
  3034.         // wait for it to go away before continueing.
  3035.  
  3036.         // If we WaitForSingleObject for the control panel to exit, then we
  3037.         // get into a deadlock situation if we need to respond to any messages
  3038.         // from the control panel.
  3039.         
  3040.         // If we use a PeekMessage loop to wait, we run into
  3041.         // message re-entrancy problems.  (The user can get back to our UI
  3042.         // and click 'dial' again).
  3043.  
  3044.         // Instead, we take the easy way out and return FALSE to abort
  3045.         // the current operation.
  3046.     
  3047.         CloseHandle(piProcInfo.hProcess);
  3048.     }
  3049.     else
  3050.     {
  3051.         OutputDebugLastError(GetLastError(), 
  3052.             "Unable to LaunchModemControlPanelAdd: ");
  3053.         MessageBox(g_hDlgParentWindow, 
  3054.             "Unable to launch the Modem Control Panel",
  3055.             "Warning", MB_OK);
  3056.     }
  3057.     
  3058.     return FALSE;
  3059. }
  3060.  
  3061.  
  3062. //
  3063. //  FUNCTION: WarningBox(LPCSTR)
  3064. //
  3065. //  PURPOSE: Prints a warning box when conditions remove a line in use.
  3066. //
  3067. //  PARAMETERS:
  3068. //    lpszMessage - String that specifies why the line was removed form use.
  3069. //    
  3070. //  RETURN VALUE:
  3071. //    none
  3072. //
  3073. //  COMMENTS:
  3074. //
  3075. //    If there is a call in progress on the line removed, then display a message
  3076. //    specifying why the line was removed and that the call is being canceled.
  3077. //
  3078. //
  3079.  
  3080. void WarningBox(LPCSTR lpszMessage)
  3081. {
  3082.     char szBuff[1024];
  3083.  
  3084.     strcpy(szBuff, lpszMessage);
  3085.  
  3086.     // If there is a call open, tell user we're going to close it.
  3087.     if (g_hCall)
  3088.         strcat(szBuff, "\nClosing existing call.");
  3089.  
  3090.     MessageBox(g_hDlgParentWindow, szBuff, "Warning", MB_OK);
  3091.  
  3092.     strcat(szBuff, "\r\n");
  3093.     OutputDebugString(szBuff);
  3094. }
  3095.  
  3096.  
  3097. //**************************************************
  3098. //
  3099. // All the functions from this point on are used solely by the "Dial" dialog.
  3100. // This dialog is used to get both the 'phone number' address, 
  3101. // the line device to be used as well as allow the user to configure
  3102. // dialing properties and the line device.
  3103. //
  3104. //**************************************************
  3105.  
  3106. //
  3107. //  FUNCTION: DWORD I_lineNegotiateLegacyAPIVersion(DWORD)
  3108. //
  3109. //  PURPOSE: Negotiate an API Version to use for a specific device.
  3110. //
  3111. //  PARAMETERS:
  3112. //    dwDeviceID - device to negotiate an API Version for.
  3113. //
  3114. //  RETURN VALUE:
  3115. //    Returns the API Version to use for this line if successful.
  3116. //    Returns 0 if negotiations fall through.
  3117. //
  3118. //  COMMENTS:
  3119. //
  3120. //    This wrapper is slightly different from the I_lineNegotiateAPIVersion.
  3121. //    This wrapper allows TapiComm to negotiate an API version between
  3122. //    1.3 and SAMPLE_TAPI_VERSION.  Normally, this sample is specific to 
  3123. //    API Version SAMPLE_TAPI_VERSION.  However, there are a few times when
  3124. //    TapiComm needs to get information from a service provider, but also knows
  3125. //    that a lower API Version would be ok.  This allows TapiComm to recognize
  3126. //    legacy service providers even though it can't use them.  1.3 is the
  3127. //    lowest API Version a legacy service provider should support.
  3128. //
  3129. //
  3130.  
  3131. DWORD I_lineNegotiateLegacyAPIVersion(DWORD dwDeviceID)
  3132. {
  3133.     LINEEXTENSIONID LineExtensionID;
  3134.     long lReturn;
  3135.     DWORD dwLocalAPIVersion;
  3136.     
  3137.     do
  3138.     {
  3139.         lReturn = lineNegotiateAPIVersion(g_hLineApp, dwDeviceID, 
  3140.             0x00010003, SAMPLE_TAPI_VERSION,
  3141.             &dwLocalAPIVersion, &LineExtensionID);
  3142.  
  3143.         if (lReturn == LINEERR_INCOMPATIBLEAPIVERSION)
  3144.         {
  3145.             OutputDebugString("INCOMPATIBLEAPIVERSION in Dial Dialog.\n");
  3146.             return 0;
  3147.         }
  3148.  
  3149.         if (HandleLineErr(lReturn))
  3150.             continue;
  3151.         else
  3152.         {
  3153.             OutputDebugLineError(lReturn, 
  3154.                 "lineNegotiateAPIVersion in Dial Dialog unhandled error: ");
  3155.             return 0;
  3156.         }
  3157.     }
  3158.     while(lReturn != SUCCESS);
  3159.         
  3160.     return dwLocalAPIVersion;
  3161. }
  3162.  
  3163.  
  3164. //
  3165. //  FUNCTION: long VerifyUsableLine(DWORD)
  3166. //
  3167. //  PURPOSE: Verifies that a specific line device is useable by TapiComm.
  3168. //
  3169. //  PARAMETERS:
  3170. //    dwDeviceID - The ID of the line device to be verified
  3171. //
  3172. //  RETURN VALUE:
  3173. //    Returns SUCCESS if dwDeviceID is a usable line device.
  3174. //    Returns a LINENOTUSEABLE_ constant otherwise.
  3175. //
  3176. //  COMMENTS:
  3177. //
  3178. //    VerifyUsableLine takes the give device ID and verifies step by step
  3179. //    that the device supports all the features that TapiComm requires.
  3180. //
  3181. //
  3182.  
  3183. long VerifyUsableLine(DWORD dwDeviceID)
  3184. {
  3185.     LPLINEDEVCAPS lpLineDevCaps = NULL;
  3186.     LPLINEADDRESSSTATUS lpLineAddressStatus = NULL;
  3187.     LPVARSTRING lpVarString = NULL;
  3188.     DWORD dwAPIVersion;
  3189.     long lReturn;
  3190.     long lUsableLine = SUCCESS;
  3191.     HLINE hLine = 0;
  3192.  
  3193.     OutputDebugPrintf("Testing Line ID '0x%lx'",dwDeviceID);
  3194.  
  3195.     // The line device must support an API Version that TapiComm does.
  3196.     dwAPIVersion = I_lineNegotiateAPIVersion(dwDeviceID);
  3197.     if (dwAPIVersion == 0)
  3198.         return LINENOTUSEABLE_ERROR;
  3199.  
  3200.     lpLineDevCaps = I_lineGetDevCaps(lpLineDevCaps,
  3201.         dwDeviceID, dwAPIVersion);
  3202.  
  3203.     if (lpLineDevCaps == NULL)
  3204.         return LINENOTUSEABLE_ERROR;
  3205.  
  3206.     // Must support LINEBEARERMODE_VOICE
  3207.     if (!(lpLineDevCaps->dwBearerModes & LINEBEARERMODE_VOICE ))
  3208.     {
  3209.         lUsableLine = LINENOTUSEABLE_NOVOICE;
  3210.         OutputDebugString("LINEBEARERMODE_VOICE not supported\n");
  3211.         goto DeleteBuffers;
  3212.     }
  3213.  
  3214.     // Must support LINEMEDIAMODE_DATAMODEM
  3215.     if (!(lpLineDevCaps->dwMediaModes & LINEMEDIAMODE_DATAMODEM))
  3216.     {
  3217.         lUsableLine = LINENOTUSEABLE_NODATAMODEM;
  3218.         OutputDebugString("LINEMEDIAMODE_DATAMODEM not supported\n");
  3219.         goto DeleteBuffers;
  3220.     }
  3221.  
  3222.     // Must be able to make calls
  3223.     if (!(lpLineDevCaps->dwLineFeatures & LINEFEATURE_MAKECALL))
  3224.     {
  3225.         lUsableLine = LINENOTUSEABLE_NOMAKECALL;
  3226.         OutputDebugString("LINEFEATURE_MAKECALL not supported\n");
  3227.         goto DeleteBuffers;
  3228.     }
  3229.  
  3230.     // It is necessary to open the line so we can check if 
  3231.     // there are any call appearances available.  Other TAPI
  3232.     // applications could be using all call appearances.
  3233.     // Opening the line also checks for other possible problems.
  3234.     do
  3235.     {
  3236.         lReturn = lineOpen(g_hLineApp, dwDeviceID, &hLine,
  3237.             dwAPIVersion, 0, 0,
  3238.             LINECALLPRIVILEGE_NONE, LINEMEDIAMODE_DATAMODEM,
  3239.             0);
  3240.  
  3241.         if(lReturn == LINEERR_ALLOCATED)
  3242.         {
  3243.             OutputDebugString(
  3244.                 "Line is already in use by a non-TAPI app or"
  3245.                 " another Service Provider.\n");
  3246.             lUsableLine = LINENOTUSEABLE_ALLOCATED;
  3247.             goto DeleteBuffers;
  3248.         }
  3249.  
  3250.         if (HandleLineErr(lReturn))
  3251.             continue;
  3252.         else
  3253.         {
  3254.             OutputDebugLineError(lReturn, "lineOpen unhandled error: ");
  3255.             lUsableLine = LINENOTUSEABLE_ERROR;
  3256.             goto DeleteBuffers;
  3257.         }
  3258.     }
  3259.     while(lReturn != SUCCESS);
  3260.  
  3261.     // Get LineAddressStatus to make sure the line isn't already in use.
  3262.     lpLineAddressStatus = 
  3263.         I_lineGetAddressStatus(lpLineAddressStatus, hLine, 0);
  3264.         
  3265.     if (lpLineAddressStatus == NULL)
  3266.     {
  3267.         lUsableLine = LINENOTUSEABLE_ERROR;
  3268.         goto DeleteBuffers;
  3269.     }
  3270.  
  3271.     // Are there any available call appearances (ie: is it in use)?
  3272.     if ( !((lpLineAddressStatus -> dwAddressFeatures) &
  3273.            LINEADDRFEATURE_MAKECALL) )
  3274.     {
  3275.         OutputDebugString("LINEADDRFEATURE_MAKECALL not available\n");
  3276.         lUsableLine = LINENOTUSEABLE_INUSE;
  3277.         goto DeleteBuffers;
  3278.     }
  3279.  
  3280.     // Make sure the "comm/datamodem" device class is supported
  3281.     // Note that we don't want any of the 'extra' information
  3282.     // normally returned in the VARSTRING structure.  All we care
  3283.     // about is if lineGetID succeeds.
  3284.     do
  3285.     {
  3286.         lpVarString = CheckAndReAllocBuffer((LPVOID) lpVarString,
  3287.             sizeof(VARSTRING),"VerifyUsableLine:lineGetID: ");
  3288.  
  3289.         if (lpVarString == NULL)
  3290.         {
  3291.             lUsableLine = LINENOTUSEABLE_ERROR;
  3292.             goto DeleteBuffers;
  3293.         }
  3294.  
  3295.         lReturn = lineGetID(hLine, 0, 0, LINECALLSELECT_LINE,
  3296.             lpVarString, "comm/datamodem");
  3297.  
  3298.         if (HandleLineErr(lReturn))
  3299.             continue;
  3300.         else
  3301.         {
  3302.             OutputDebugLineError(lReturn, 
  3303.                 "lineGetID unhandled error: ");
  3304.             lUsableLine = LINENOTUSEABLE_NOCOMMDATAMODEM;
  3305.             goto DeleteBuffers;
  3306.         }
  3307.     }
  3308.     while(lReturn != SUCCESS);
  3309.  
  3310.     OutputDebugString("Line is suitable and available for use.\n");
  3311.  
  3312.   DeleteBuffers:
  3313.  
  3314.     if (hLine)
  3315.         lineClose(hLine);
  3316.     if (lpLineAddressStatus)
  3317.         LocalFree(lpLineAddressStatus);
  3318.     if (lpLineDevCaps)
  3319.         LocalFree(lpLineDevCaps);
  3320.     if (lpVarString)
  3321.         LocalFree(lpVarString);
  3322.  
  3323.     return lUsableLine;
  3324. }
  3325.  
  3326.  
  3327. //
  3328. //  FUNCTION: void FillTAPILine(HWND)
  3329. //
  3330. //  PURPOSE: Fills the 'TAPI Line' control with the available line devices.
  3331. //
  3332. //  PARAMETERS: 
  3333. //    hwndDlg - handle to the current "Dial" dialog 
  3334. //
  3335. //  RETURN VALUE:
  3336. //    none
  3337. //
  3338. //  COMMENTS:
  3339. //
  3340. //    This function enumerates through all the TAPI line devices and
  3341. //    queries each for the device name.  The device name is then put into
  3342. //    the 'TAPI Line' control.  These device names are kept in order rather
  3343. //    than sorted.  This allows "Dial" to know which device ID the user 
  3344. //    selected just by the knowing the index of the selected string.
  3345. //
  3346. //    There are default values if there isn't a device name, if there is
  3347. //    an error on the device, or if the device name is an empty string.
  3348. //    The device name is also checked to make sure it is null terminated.
  3349. //
  3350. //    Note that a Legacy API Version is negotiated.  Since the fields in
  3351. //    the LINEDEVCAPS structure that we are interested in haven't moved, we
  3352. //    can negotiate a lower API Version than this sample is designed for
  3353. //    and still be able to access the necessary structure members.
  3354. //
  3355. //    The first line that is usable by TapiComm is selected as the 'default'
  3356. //    line.  Also note that if there was a previously selected line, this
  3357. //    remains the default line.  This would likely only occur if this
  3358. //    function is called after the dialog has initialized once; for example,
  3359. //    if a new line is added.
  3360. //
  3361. //
  3362.  
  3363. void FillTAPILine(HWND hwndDlg)
  3364. {
  3365.     DWORD dwDeviceID;
  3366.     DWORD dwAPIVersion;
  3367.     LPLINEDEVCAPS lpLineDevCaps = NULL;
  3368.     char szLineUnavail[] = "Line Unavailable";
  3369.     char szLineUnnamed[] = "Line Unnamed";
  3370.     char szLineNameEmpty[] = "Line Name is Empty";
  3371.     LPSTR lpszLineName;
  3372.     long lReturn;
  3373.     DWORD dwDefaultDevice = MAXDWORD;
  3374.  
  3375.     // Make sure the control is empty.  If it isn't,
  3376.     // hold onto the currently selected ID and then reset it.
  3377.     if (SendDlgItemMessage(hwndDlg, IDC_TAPILINE, CB_GETCOUNT, 0, 0))
  3378.     {
  3379.         dwDefaultDevice = SendDlgItemMessage(hwndDlg, IDC_TAPILINE, 
  3380.             CB_GETCURSEL, 0, 0);
  3381.         SendDlgItemMessage(hwndDlg, IDC_TAPILINE, CB_RESETCONTENT, 0, 0);
  3382.     }
  3383.  
  3384.     for (dwDeviceID = 0; dwDeviceID < g_dwNumDevs; dwDeviceID ++)
  3385.     {
  3386.         dwAPIVersion = I_lineNegotiateLegacyAPIVersion(dwDeviceID);
  3387.         if (dwAPIVersion)
  3388.         {
  3389.             lpLineDevCaps = I_lineGetDevCaps(lpLineDevCaps,
  3390.                 dwDeviceID, dwAPIVersion);
  3391.             if (lpLineDevCaps)
  3392.             {
  3393.                 if ((lpLineDevCaps -> dwLineNameSize) &&
  3394.                     (lpLineDevCaps -> dwLineNameOffset) &&
  3395.                     (lpLineDevCaps -> dwStringFormat == STRINGFORMAT_ASCII))
  3396.                 {
  3397.                     // This is the name of the device.
  3398.                     lpszLineName = ((char *) lpLineDevCaps) + 
  3399.                         lpLineDevCaps -> dwLineNameOffset;
  3400.  
  3401.                     if (lpszLineName[0] != '\0')
  3402.                     {
  3403.         // Reverse indented to make this fit
  3404.  
  3405.         // Make sure the device name is null terminated.
  3406.         if (lpszLineName[lpLineDevCaps->dwLineNameSize -1] != '\0')
  3407.         {
  3408.             // If the device name is not null terminated, null
  3409.             // terminate it.  Yes, this looses the end character.
  3410.             // Its a bug in the service provider.
  3411.             lpszLineName[lpLineDevCaps->dwLineNameSize-1] = '\0';
  3412.             OutputDebugPrintf(
  3413.                 "Device name for device 0x%lx is not null terminated.\n",
  3414.                 dwDeviceID);
  3415.         }
  3416.                     }
  3417.                     else // Line name started with a NULL.
  3418.                         lpszLineName = szLineNameEmpty;
  3419.                 }
  3420.                 else  // DevCaps doesn't have a valid line name.  Unnamed.
  3421.                     lpszLineName = szLineUnnamed;
  3422.             }
  3423.             else  // Couldn't GetDevCaps.  Line is unavail.
  3424.                 lpszLineName = szLineUnavail;
  3425.         }
  3426.         else  // Couldn't NegotiateAPIVersion.  Line is unavail.
  3427.             lpszLineName = szLineUnavail;
  3428.  
  3429.         // Put the device name into the control
  3430.         lReturn = SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
  3431.             CB_ADDSTRING, 0, (LPARAM) (LPCTSTR) lpszLineName);
  3432.  
  3433.         // If this line is usable and we don't have a default initial
  3434.         // line yet, make this the initial line.
  3435.         if ((lpszLineName != szLineUnavail) && 
  3436.             (dwDefaultDevice == MAXDWORD) &&
  3437.             (VerifyUsableLine(dwDeviceID) == SUCCESS))
  3438.         {
  3439.             dwDefaultDevice = dwDeviceID;
  3440.         }
  3441.     }
  3442.  
  3443.     if (lpLineDevCaps)
  3444.         LocalFree(lpLineDevCaps);
  3445.  
  3446.     if (dwDefaultDevice == MAXDWORD)
  3447.         dwDefaultDevice = 0;
  3448.  
  3449.     // Set the initial default line
  3450.     SendDlgItemMessage(hwndDlg, IDC_TAPILINE, 
  3451.         CB_SETCURSEL, dwDefaultDevice, 0);
  3452. }
  3453.  
  3454.  
  3455. //
  3456. //  FUNCTION: BOOL VerifyAndWarnUsableLine(HWND)
  3457. //
  3458. //  PURPOSE: Verifies the line device selected by the user.
  3459. //
  3460. //  PARAMETERS:
  3461. //    hwndDlg - The handle to the current "Dial" dialog.
  3462. //
  3463. //  RETURN VALUE:
  3464. //    Returns TRUE if the currently selected line device is useable
  3465. //      by TapiComm.  Returns FALSE if it isn't.
  3466. //
  3467. //  COMMENTS:
  3468. //
  3469. //    This function is very specific to the "Dial" dialog.  It gets
  3470. //    the device selected by the user from the 'TAPI Line' control and
  3471. //    VerifyUsableLine to make sure this line device is usable.  If the
  3472. //    line isn't useable, it notifies the user and disables the 'Dial'
  3473. //    button so that the user can't initiate a call with this line.
  3474. //
  3475. //    This function is also responsible for filling in the line specific
  3476. //    icon found on the "Dial" dialog.
  3477. //
  3478. //
  3479.  
  3480. BOOL VerifyAndWarnUsableLine(HWND hwndDlg)
  3481. {
  3482.     DWORD dwDeviceID;
  3483.     long lReturn;
  3484.     HICON hIcon = 0;
  3485.     HWND hControlWnd;
  3486.  
  3487.     // Get the selected line device.
  3488.     dwDeviceID = SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
  3489.                         CB_GETCURSEL, 0, 0);
  3490.  
  3491.     // Get the "comm" device icon associated with this line device.
  3492.     lReturn = lineGetIcon(dwDeviceID, "comm", &hIcon);
  3493.  
  3494.     if (lReturn == SUCCESS)
  3495.         SendDlgItemMessage(hwndDlg, IDC_LINEICON, STM_SETICON,
  3496.             (WPARAM) hIcon, 0);
  3497.     else
  3498.         // Any failure to get an icon makes us use the default icon.
  3499.         SendDlgItemMessage(hwndDlg, IDC_LINEICON, WM_SETTEXT,
  3500.             0, (LPARAM) (LPCTSTR) "TapiComm");
  3501.  
  3502. /*  // It turns out that TAPI will always return an icon, even if
  3503.     // the device class isn't supported by the TSP or even if the TSP
  3504.     // doesn't return any icons at all.  This code is unnecessary.
  3505.     // The only reason lineGetIcon would fail is due to resource problems.
  3506.  
  3507.     else
  3508.     {
  3509.         // If the line doesn't have a "comm" device icon, use its default one.
  3510.         lReturn = lineGetIcon(dwDeviceID, NULL, &hIcon);
  3511.         if (lReturn == SUCCESS)
  3512.         {
  3513.             OutputDebugString("Line doesn't support a \"comm\" icon.\n");
  3514.             SendDlgItemMessage(hwndDlg, IDC_LINEICON, STM_SETICON,
  3515.                 (WPARAM) hIcon, 0);
  3516.         }
  3517.         else
  3518.         {
  3519.             // If lineGetIcon fails, just use TapiComms icon.
  3520.             OutputDebugLineError(lReturn, "lineGetIcon: ");
  3521.             SendDlgItemMessage(hwndDlg, IDC_LINEICON, WM_SETTEXT,
  3522.                 0, (LPARAM) (LPCTSTR) "TapiComm");
  3523.         }
  3524.     }
  3525. */
  3526.  
  3527.     // Verify if the device is usable by TapiComm.
  3528.     lReturn = VerifyUsableLine(dwDeviceID);
  3529.  
  3530.     // Enable or disable the 'Dial' button, depending on if the line is ok.
  3531.     // Make sure there is a number to dial before enabling the button.
  3532.     hControlWnd = GetDlgItem(hwndDlg, IDC_DIAL);
  3533.     if (SendDlgItemMessage(hwndDlg, IDC_CANONICALNUMBER,
  3534.         WM_GETTEXTLENGTH, 0, 0) == 0)
  3535.     {
  3536.         EnableWindow(hControlWnd, FALSE);
  3537.     }
  3538.     else
  3539.         EnableWindow(hControlWnd, (lReturn == SUCCESS));
  3540.  
  3541.     // Any errors on this line prevent us from configuring it
  3542.     // or using dialing properties.
  3543.     if (lReturn == LINENOTUSEABLE_ERROR)
  3544.     {
  3545.         EnableWindow(GetDlgItem(hwndDlg, IDC_CONFIGURELINE), FALSE);
  3546.         EnableWindow(GetDlgItem(hwndDlg, IDC_DIALINGPROPERTIES), FALSE);
  3547.     }
  3548.     else
  3549.     {
  3550.         EnableWindow(GetDlgItem(hwndDlg, IDC_CONFIGURELINE), TRUE);
  3551.         if (SendDlgItemMessage(hwndDlg, IDC_USEDIALINGRULES, BM_GETCHECK, 0, 0))
  3552.             EnableWindow(GetDlgItem(hwndDlg, IDC_DIALINGPROPERTIES), TRUE);
  3553.     }
  3554.  
  3555.     switch(lReturn)
  3556.     {
  3557.         case SUCCESS:
  3558.             g_dwDeviceID = dwDeviceID;
  3559.             return TRUE;
  3560.  
  3561.         case LINENOTUSEABLE_ERROR:
  3562.             MessageBox(hwndDlg,
  3563.                 "The selected line is incompatible with the TapiComm sample",
  3564.                 "Warning",MB_OK);
  3565.             break;
  3566.         case LINENOTUSEABLE_NOVOICE:
  3567.             MessageBox(hwndDlg,
  3568.                 "The selected line doesn't support VOICE capabilities",
  3569.                 "Warning",MB_OK);
  3570.             break;
  3571.         case LINENOTUSEABLE_NODATAMODEM:
  3572.             MessageBox(hwndDlg,
  3573.                 "The selected line doesn't support DATAMODEM capabilities",
  3574.                 "Warning",MB_OK);
  3575.             break;
  3576.         case LINENOTUSEABLE_NOMAKECALL:
  3577.             MessageBox(hwndDlg,
  3578.                 "The selected line doesn't support MAKECALL capabilities",
  3579.                 "Warning",MB_OK);
  3580.             break;
  3581.         case LINENOTUSEABLE_ALLOCATED:
  3582.             MessageBox(hwndDlg,
  3583.                 "The selected line is already in use by a non-TAPI application",
  3584.                 "Warning",MB_OK);
  3585.             break;
  3586.         case LINENOTUSEABLE_INUSE:
  3587.             MessageBox(hwndDlg,
  3588.                 "The selected line is already in use by a TAPI application",
  3589.                 "Warning",MB_OK);
  3590.             break;
  3591.         
  3592.         case LINENOTUSEABLE_NOCOMMDATAMODEM:
  3593.             MessageBox(hwndDlg,
  3594.                 "The selected line doesn't support the COMM/DATAMODEM device class",
  3595.                 "Warning",MB_OK);
  3596.             break;
  3597.     }
  3598.  
  3599.     // g_dwDeviceID == MAXDWORD mean the selected device isn't usable.
  3600.     g_dwDeviceID = MAXDWORD;
  3601.     return FALSE;
  3602. }
  3603.  
  3604.  
  3605. //
  3606. //  FUNCTION: void FillCountryCodeList(HWND, DWORD)
  3607. //
  3608. //  PURPOSE: Fill the 'Country Code' control
  3609. //
  3610. //  PARAMETERS:
  3611. //    hwndDlg - handle to the current "Dial" dialog 
  3612. //    dwDefaultCountryID - ID of the 'default' country to be selected
  3613. //
  3614. //  RETURN VALUE:
  3615. //    none
  3616. //
  3617. //  COMMENTS:
  3618. //
  3619. //    This function fills the 'Country Code' control with country names.
  3620. //    The country code is appended to the end of the name and the names
  3621. //    are added to the control sorted.  Because the country code is
  3622. //    embedded in the string along with the country name, there is no need
  3623. //    for any of the country information structures to be kept around.  The 
  3624. //    country code can be extracted from the selected string at any time.
  3625. //
  3626. //
  3627.  
  3628. void FillCountryCodeList(HWND hwndDlg, DWORD dwDefaultCountryID)
  3629. {
  3630.     LPLINECOUNTRYLIST lpLineCountryList = NULL;
  3631.     DWORD dwSizeofCountryList = sizeof(LINECOUNTRYLIST);
  3632.     long lReturn;
  3633.     DWORD dwCountry;
  3634.     LPLINECOUNTRYENTRY lpLineCountryEntries;
  3635.     char szRenamedCountry[256];
  3636.  
  3637.     // Get the country information stored in TAPI
  3638.     do
  3639.     {
  3640.         lpLineCountryList = (LPLINECOUNTRYLIST) CheckAndReAllocBuffer(
  3641.             (LPVOID) lpLineCountryList, dwSizeofCountryList,
  3642.             "FillCountryCodeList");
  3643.  
  3644.         if (lpLineCountryList == NULL)
  3645.             return;
  3646.  
  3647.         lReturn = lineGetCountry (0, SAMPLE_TAPI_VERSION, lpLineCountryList);
  3648.  
  3649.         if (HandleLineErr(lReturn))
  3650.             ;
  3651.         else
  3652.         {
  3653.             OutputDebugLineError(lReturn, "lineGetCountry unhandled error: ");
  3654.             LocalFree(lpLineCountryList);
  3655.             return;
  3656.         }
  3657.  
  3658.         if ((lpLineCountryList -> dwNeededSize) >
  3659.             (lpLineCountryList -> dwTotalSize))
  3660.         {
  3661.             dwSizeofCountryList = lpLineCountryList ->dwNeededSize;
  3662.             lReturn = -1; // Lets loop again.
  3663.         }
  3664.     }
  3665.     while (lReturn != SUCCESS);
  3666.  
  3667.     // Find the first country entry
  3668.     lpLineCountryEntries = (LPLINECOUNTRYENTRY) 
  3669.         (((LPBYTE) lpLineCountryList) 
  3670.          + lpLineCountryList -> dwCountryListOffset);
  3671.  
  3672.     // Now enumerate through all the countries
  3673.     for (dwCountry = 0; 
  3674.          dwCountry < lpLineCountryList -> dwNumCountries; 
  3675.          dwCountry++)
  3676.     {
  3677.         // append the country code to the country name
  3678.         wsprintf(szRenamedCountry,"%s (%lu)",
  3679.             (((LPSTR) lpLineCountryList) +
  3680.                 lpLineCountryEntries[dwCountry].dwCountryNameOffset),
  3681.             lpLineCountryEntries[dwCountry].dwCountryCode);
  3682.  
  3683.         // Now put this country name / code string into the combobox
  3684.         lReturn = SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE, CB_ADDSTRING,
  3685.                     0, (LPARAM) (LPCTSTR) szRenamedCountry);
  3686.  
  3687.         // If this country is the default country, select it.
  3688.         if (lpLineCountryEntries[dwCountry].dwCountryID
  3689.             == dwDefaultCountryID)
  3690.         {
  3691.             SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE, CB_SETCURSEL, lReturn, 0);
  3692.         }
  3693.     }
  3694.     
  3695.     LocalFree(lpLineCountryList);
  3696.     return;
  3697. }
  3698.  
  3699.  
  3700. //
  3701. //  FUNCTION: void FillLocationInfo(HWND, LPSTR, LPDWORD, LPSTR)
  3702. //
  3703. //  PURPOSE: Fill (or refill) the 'Your Location' control
  3704. //
  3705. //  PARAMETERS:
  3706. //    hwndDlg - handle to the current "Dial" dialog 
  3707. //    lpszCurrentLocation - Name of current location, or NULL
  3708. //    lpdwCountryID - location to store the current country ID or NULL
  3709. //    lpszAreaCode - location to store the current area code or NULL
  3710. //
  3711. //  RETURN VALUE:
  3712. //    none
  3713. //
  3714. //  COMMENTS:
  3715. //
  3716. //    This function is moderately multipurpose.
  3717. //
  3718. //    If lpszCurrentLocation is NULL, then the 'Your Location' control
  3719. //    is filled with all the locations stored in TAPI and the TAPI 'default' 
  3720. //    location is selected.  This is done during initialization and
  3721. //    also after the 'Dialing Properties' dialog has been displayed.
  3722. //    This last is done because the user can change the current location
  3723. //    or add and delete locations while in the 'Dialing Properties' dialog.
  3724. //    
  3725. //    If lpszCurrentLocation is a valid string pointer, then it is assumed
  3726. //    that the 'Your Location' control is already filled and that the user
  3727. //    is selecting a specific location.  In this case, all of the existing
  3728. //    TAPI locations are enumerated until the specified location is found.
  3729. //    At this point, the specified location is set to the current location.
  3730. //    
  3731. //    In either case, if lpdwCountryID is not NULL, it is filled with the
  3732. //    country ID for the current location.  If lpszAreaCode is not NULL, it
  3733. //    is filled with the area code defined for the current location.  These
  3734. //    values can be used later to initialize other "Dial" controls.
  3735. //
  3736. //    This function also fills the 'Calling Card' control based on
  3737. //    the information stored in the current location.
  3738. //
  3739. //
  3740.  
  3741. void FillLocationInfo(HWND hwndDlg, LPSTR lpszCurrentLocation, 
  3742.     LPDWORD lpdwCountryID, LPSTR lpszAreaCode)
  3743. {
  3744.     LPLINETRANSLATECAPS lpTranslateCaps = NULL;
  3745.     DWORD dwSizeofTranslateCaps = sizeof(LINETRANSLATECAPS);
  3746.     long lReturn;
  3747.     DWORD dwCounter;
  3748.     LPLINELOCATIONENTRY lpLocationEntry;
  3749.     LPLINECARDENTRY lpLineCardEntry = NULL;
  3750.     DWORD dwPreferredCardID = MAXDWORD;
  3751.  
  3752.     // First, get the TRANSLATECAPS
  3753.     do
  3754.     {
  3755.         lpTranslateCaps = (LPLINETRANSLATECAPS) CheckAndReAllocBuffer(
  3756.             (LPVOID) lpTranslateCaps, dwSizeofTranslateCaps,
  3757.             "FillLocationInfo");
  3758.  
  3759.         if (lpTranslateCaps == NULL)
  3760.             return;
  3761.  
  3762.         lReturn = lineGetTranslateCaps(g_hLineApp, SAMPLE_TAPI_VERSION, 
  3763.                     lpTranslateCaps);
  3764.  
  3765.         if (HandleLineErr(lReturn))
  3766.             ;
  3767.         else
  3768.         {
  3769.             OutputDebugLineError(lReturn, 
  3770.                 "lineGetTranslateCaps unhandled error: ");
  3771.             LocalFree(lpTranslateCaps);
  3772.             return;
  3773.         }
  3774.  
  3775.         if ((lpTranslateCaps -> dwNeededSize) >
  3776.             (lpTranslateCaps -> dwTotalSize))
  3777.         {
  3778.             dwSizeofTranslateCaps = lpTranslateCaps ->dwNeededSize;
  3779.             lReturn = -1; // Lets loop again.
  3780.         }
  3781.     }
  3782.     while(lReturn != SUCCESS);
  3783.  
  3784.     // Find the location information in the TRANSLATECAPS
  3785.     lpLocationEntry = (LPLINELOCATIONENTRY)
  3786.         (((LPBYTE) lpTranslateCaps) + lpTranslateCaps->dwLocationListOffset);
  3787.  
  3788.     // If lpszCurrentLocation, then make that location 'current'
  3789.     if (lpszCurrentLocation)
  3790.     {
  3791.         // loop through all locations, looking for a location match
  3792.         for(dwCounter = 0; 
  3793.             dwCounter < lpTranslateCaps -> dwNumLocations;
  3794.             dwCounter++)
  3795.         {
  3796.             if (strcmp((((LPSTR) lpTranslateCaps) + 
  3797.                             lpLocationEntry[dwCounter].dwLocationNameOffset),
  3798.                         lpszCurrentLocation)
  3799.                 == 0)
  3800.             {
  3801.                 // Found it!  Set the current location.
  3802.                 lineSetCurrentLocation(g_hLineApp, 
  3803.                     lpLocationEntry[dwCounter].dwPermanentLocationID);
  3804.  
  3805.                 // Set the return values.
  3806.                 if (lpdwCountryID)
  3807.                     *lpdwCountryID = lpLocationEntry[dwCounter].dwCountryID;
  3808.  
  3809.                 if (lpszAreaCode)
  3810.                     strcpy(lpszAreaCode, (((LPSTR) lpTranslateCaps) + 
  3811.                             lpLocationEntry[dwCounter].dwCityCodeOffset));
  3812.  
  3813.                 // Store the preferred card ID for later use.
  3814.                 dwPreferredCardID = lpLocationEntry[dwCounter].dwPreferredCardID;
  3815.                 break;
  3816.             }
  3817.         }
  3818.  
  3819.         // Was a match for lpszCurrentLocation found?
  3820.         if (dwPreferredCardID == MAXDWORD)
  3821.         {
  3822.             OutputDebugString("lpszCurrentLocation not found\n");
  3823.             SendDlgItemMessage(hwndDlg, IDC_CALLINGCARD, WM_SETTEXT, 0, 
  3824.                 (LPARAM) (LPCSTR) "Invalid Location Selected");
  3825.             LocalFree(lpTranslateCaps);
  3826.             return;
  3827.         }
  3828.     }
  3829.     else // fill the combobox and use the TAPI 'current' location.
  3830.     {
  3831.         // First empty the combobox
  3832.         SendDlgItemMessage(hwndDlg, IDC_LOCATION, CB_RESETCONTENT, 0, 0);
  3833.     
  3834.         // enumerate all the locations
  3835.         for(dwCounter = 0; 
  3836.             dwCounter < lpTranslateCaps -> dwNumLocations;
  3837.             dwCounter++)
  3838.         {
  3839.             // Put each one into the combobox
  3840.             lReturn = SendDlgItemMessage(hwndDlg, IDC_LOCATION, CB_ADDSTRING,
  3841.                 0, (LPARAM) (((LPBYTE) lpTranslateCaps) + 
  3842.                     lpLocationEntry[dwCounter].dwLocationNameOffset));
  3843.         
  3844.             // Is this location the 'current' location?
  3845.             if (lpLocationEntry[dwCounter].dwPermanentLocationID ==
  3846.                 lpTranslateCaps->dwCurrentLocationID)
  3847.             {
  3848.                 // Return the requested information
  3849.                 if (lpdwCountryID)
  3850.                     *lpdwCountryID = lpLocationEntry[dwCounter].dwCountryID;
  3851.  
  3852.                 if (lpszAreaCode)
  3853.                     strcpy(lpszAreaCode, (((LPSTR) lpTranslateCaps) + 
  3854.                             lpLocationEntry[dwCounter].dwCityCodeOffset));
  3855.  
  3856.                 // Set this to be the active location.
  3857.                 SendDlgItemMessage(hwndDlg, IDC_LOCATION, CB_SETCURSEL, lReturn, 0);
  3858.                 dwPreferredCardID = lpLocationEntry[dwCounter].dwPreferredCardID;
  3859.             }
  3860.         }
  3861.     }
  3862.  
  3863.     // Now locate the prefered card and display it.
  3864.  
  3865.     lpLineCardEntry = (LPLINECARDENTRY)
  3866.         (((LPBYTE) lpTranslateCaps) + lpTranslateCaps->dwCardListOffset);
  3867.  
  3868.     for(dwCounter = 0; 
  3869.         dwCounter < lpTranslateCaps -> dwNumCards;
  3870.         dwCounter++)
  3871.     {
  3872.         if (lpLineCardEntry[dwCounter].dwPermanentCardID == dwPreferredCardID)
  3873.         {
  3874.             SendDlgItemMessage(hwndDlg, IDC_CALLINGCARD, WM_SETTEXT, 0, 
  3875.                 (LPARAM) (((LPBYTE) lpTranslateCaps) + 
  3876.                     lpLineCardEntry[dwCounter].dwCardNameOffset));
  3877.             break;
  3878.         }
  3879.     }
  3880.  
  3881.     LocalFree(lpTranslateCaps);
  3882. }
  3883.  
  3884.  
  3885.  
  3886. //
  3887. //  FUNCTION: void UseDialingRules(HWND)
  3888. //
  3889. //  PURPOSE: Enable/disable Dialing Rule controls
  3890. //
  3891. //  PARAMETERS:
  3892. //    hwndDlg - handle to the current "Dial" dialog 
  3893. //
  3894. //  RETURN VALUE:
  3895. //    none
  3896. //
  3897. //  COMMENTS:
  3898. //
  3899. //    The sole purpose of this function is to enable or disable
  3900. //    the controls that apply to dialing rules if the 
  3901. //    "Use Country Code and Area Code" checkbox is checked or unchecked,
  3902. //    as appropriate.
  3903. //
  3904. //
  3905.  
  3906. void UseDialingRules(HWND hwndDlg)
  3907. {
  3908.     HWND hControl;
  3909.     BOOL bEnableWindow;
  3910.  
  3911.     bEnableWindow = SendDlgItemMessage(hwndDlg,
  3912.         IDC_USEDIALINGRULES, BM_GETCHECK, 0, 0);
  3913.  
  3914.     hControl = GetDlgItem(hwndDlg, IDC_STATICCOUNTRYCODE);
  3915.     EnableWindow(hControl, bEnableWindow);
  3916.  
  3917.     hControl = GetDlgItem(hwndDlg, IDC_COUNTRYCODE);
  3918.     EnableWindow(hControl, bEnableWindow);
  3919.  
  3920.     hControl = GetDlgItem(hwndDlg, IDC_STATICAREACODE);
  3921.     EnableWindow(hControl, bEnableWindow);
  3922.  
  3923.     hControl = GetDlgItem(hwndDlg, IDC_AREACODE);
  3924.     EnableWindow(hControl, bEnableWindow);
  3925.  
  3926.     hControl = GetDlgItem(hwndDlg, IDC_STATICLOCATION);
  3927.     EnableWindow(hControl, bEnableWindow);
  3928.  
  3929.     hControl = GetDlgItem(hwndDlg, IDC_LOCATION);
  3930.     EnableWindow(hControl, bEnableWindow);
  3931.  
  3932.     hControl = GetDlgItem(hwndDlg, IDC_STATICCALLINGCARD);
  3933.     EnableWindow(hControl, bEnableWindow);
  3934.  
  3935.     hControl = GetDlgItem(hwndDlg, IDC_CALLINGCARD);
  3936.     EnableWindow(hControl, bEnableWindow);
  3937.  
  3938.     if (IsWindowEnabled(GetDlgItem(hwndDlg, IDC_CONFIGURELINE)))
  3939.     {
  3940.         hControl = GetDlgItem(hwndDlg, IDC_DIALINGPROPERTIES);
  3941.         EnableWindow(hControl, bEnableWindow);
  3942.     }
  3943. }
  3944.  
  3945.  
  3946. //
  3947. //  FUNCTION: void DisplayPhoneNumber(HWND)
  3948. //
  3949. //  PURPOSE: Create, Translate and Display the Phone Number
  3950. //
  3951. //  PARAMETERS:
  3952. //    hwndDlg - handle to the current "Dial" dialog 
  3953. //
  3954. //  RETURN VALUE:
  3955. //    none
  3956. //
  3957. //  COMMENTS:
  3958. //
  3959. //    This function uses the information stored in many other controls
  3960. //    to build the phone number, translate it, and display it.  Also
  3961. //    makes sure the Dial button is enabled or disabled, based on if the
  3962. //    number can be dialed or not.
  3963. //
  3964. //    There are actually three phone numbers generated during this
  3965. //    process:  canonical, dialable and displayable.  Normally, only the
  3966. //    displayable number is shown to the user; the other two numbers are
  3967. //    to be used by the program internally.  However, for demonstration
  3968. //    purposes (and because it is cool for developers to see these numbers),
  3969. //    all three numbers are displayed.
  3970. //
  3971.  
  3972. void DisplayPhoneNumber(HWND hwndDlg)
  3973. {
  3974.     char szPreTranslatedNumber[1024] = "";
  3975.     int  nPreTranslatedSize = 0;
  3976.     char szTempBuffer[512];
  3977.     int  i;
  3978.     DWORD dwDeviceID;
  3979.     LPLINETRANSLATEOUTPUT lpLineTranslateOutput = NULL;
  3980.  
  3981.     // Disable the 'dial' button if there isn't a number to dial
  3982.     if (0 == SendDlgItemMessage(hwndDlg, IDC_PHONENUMBER, 
  3983.             WM_GETTEXTLENGTH, 0, 0))
  3984.     {
  3985.         SendDlgItemMessage(hwndDlg, IDC_CANONICALNUMBER, WM_SETTEXT, 0,
  3986.             (LPARAM) NULL);
  3987.         SendDlgItemMessage(hwndDlg, IDC_DIALABLENUMBER, WM_SETTEXT, 0,
  3988.             (LPARAM) NULL);
  3989.         SendDlgItemMessage(hwndDlg, IDC_DISPLAYABLENUMBER, WM_SETTEXT, 0,
  3990.             (LPARAM) (LPCTSTR) "No Phone Number");
  3991.  
  3992.         EnableWindow(GetDlgItem(hwndDlg, IDC_DIAL), FALSE);
  3993.         return;
  3994.     }
  3995.  
  3996.     // If we use the dialing rules, lets make canonical format.
  3997.     // Canonical format is explained in the TAPI documentation and the
  3998.     // string format needs to be followed very strictly.
  3999.     if (SendDlgItemMessage(hwndDlg, IDC_USEDIALINGRULES,
  4000.         BM_GETCHECK, 0, 0))
  4001.     {
  4002.         // First character *has* to be the plus sign.
  4003.         szPreTranslatedNumber[0] = '+';
  4004.         nPreTranslatedSize = 1;
  4005.  
  4006.         // The country code *has* to be next.
  4007.         // Country code was stored in the string with the country
  4008.         // name and needs to be extracted at this point.
  4009.         i = SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE,
  4010.                 CB_GETCURSEL, 0, 0);
  4011.         SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE,
  4012.             CB_GETLBTEXT, (WPARAM) i, (LPARAM) (LPCTSTR) szTempBuffer);
  4013.  
  4014.         // Country code is at the end of the string, surounded by parens.
  4015.         // This makes it easy to identify the country code.
  4016.         i = strlen(szTempBuffer);
  4017.         while(szTempBuffer[--i] != '(');
  4018.  
  4019.         while(szTempBuffer[++i] != ')') 
  4020.             szPreTranslatedNumber[nPreTranslatedSize++] = szTempBuffer[i];
  4021.  
  4022.         // Next is the area code.
  4023.         i = SendDlgItemMessage(hwndDlg, IDC_AREACODE, WM_GETTEXT,
  4024.                 510, (LPARAM) (LPCTSTR) szTempBuffer);
  4025.  
  4026.         // Note that the area code is optional.  If it is included,
  4027.         // then it has to be preceeded by *exactly* one space and it
  4028.         // *has* to be surrounded by parens.
  4029.         if (i)
  4030.             nPreTranslatedSize +=
  4031.                 wsprintf(&szPreTranslatedNumber[nPreTranslatedSize],
  4032.                     " (%s)", szTempBuffer);
  4033.  
  4034.         // There has to be *exactly* one space before the rest of the number.
  4035.         szPreTranslatedNumber[nPreTranslatedSize++] = ' ';
  4036.  
  4037.         // At this point, the phone number is appended to the
  4038.         // canonical number.  The next step is the same whether canonical
  4039.         // format is used or not; just the prepended area code and 
  4040.         // country code are different.
  4041.     }
  4042.  
  4043.     SendDlgItemMessage(hwndDlg, IDC_PHONENUMBER, WM_GETTEXT,
  4044.         510, (LPARAM) (LPCTSTR) szTempBuffer);
  4045.  
  4046.     strcat(&szPreTranslatedNumber[nPreTranslatedSize], szTempBuffer);
  4047.  
  4048.     dwDeviceID = SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
  4049.                         CB_GETCURSEL, 0, 0);
  4050.  
  4051.     // Translate the address!
  4052.     lpLineTranslateOutput = I_lineTranslateAddress(
  4053.         lpLineTranslateOutput, dwDeviceID, SAMPLE_TAPI_VERSION,
  4054.         szPreTranslatedNumber);
  4055.  
  4056.     // Unable to translate it?
  4057.     if (lpLineTranslateOutput == NULL)
  4058.     {
  4059.         SendDlgItemMessage(hwndDlg, IDC_CANONICALNUMBER, WM_SETTEXT, 0,
  4060.             (LPARAM) NULL);
  4061.         SendDlgItemMessage(hwndDlg, IDC_DIALABLENUMBER, WM_SETTEXT, 0,
  4062.             (LPARAM) NULL);
  4063.         SendDlgItemMessage(hwndDlg, IDC_DISPLAYABLENUMBER, WM_SETTEXT, 0,
  4064.             (LPARAM) (LPCTSTR) "Invalid Phone Number or Area Code");
  4065.  
  4066.         EnableWindow(GetDlgItem(hwndDlg, IDC_DIAL), FALSE);
  4067.         return;
  4068.     }
  4069.  
  4070.     // Is the selected device useable with TapiComm?
  4071.     if (g_dwDeviceID != MAXDWORD)
  4072.         EnableWindow(GetDlgItem(hwndDlg, IDC_DIAL), TRUE);
  4073.  
  4074.     // Fill the appropriate phone number controls.
  4075.     SendDlgItemMessage(hwndDlg, IDC_CANONICALNUMBER, WM_SETTEXT, 0,
  4076.         (LPARAM) (LPCTSTR) szPreTranslatedNumber);
  4077.  
  4078.     SendDlgItemMessage(hwndDlg, IDC_DIALABLENUMBER, WM_SETTEXT, 0,
  4079.         (LPARAM) ((LPSTR) lpLineTranslateOutput +
  4080.             lpLineTranslateOutput -> dwDialableStringOffset));
  4081.  
  4082.     SendDlgItemMessage(hwndDlg, IDC_DISPLAYABLENUMBER, WM_SETTEXT, 0,
  4083.         (LPARAM) ((LPSTR) lpLineTranslateOutput +
  4084.             lpLineTranslateOutput -> dwDisplayableStringOffset));
  4085.  
  4086.     LocalFree(lpLineTranslateOutput);
  4087.  
  4088. }
  4089.  
  4090.  
  4091. //
  4092. //  FUNCTION: void PreConfigureDevice(HWND, DWORD)
  4093. //
  4094. //  PURPOSE: 
  4095. //
  4096. //  PARAMETERS:
  4097. //    hwndDlg - handle to the current "Dial" dialog 
  4098. //    dwDeviceID - line device to be configured
  4099. //
  4100. //  RETURN VALUE:
  4101. //    none
  4102. //
  4103. //  COMMENTS:
  4104. //
  4105. //    At one point, PreConfigureDevice used lineConfigDialog to
  4106. //    configure the device.  This has the unfortunate effect of configuring
  4107. //    the device immediately, even if it is in use by another TAPI app.
  4108. //    This can be really bad if data communications are already in
  4109. //    progress (like with RAS).
  4110. //
  4111. //    Now, PreConfigureDevice uses lineConfigDialogEdit to give the
  4112. //    user the configuration UI, but it doesn't actually do anything to
  4113. //    the line device.  TapiComm stores the configuration information so
  4114. //    that it can be set later, just before making the call.
  4115. //
  4116. //
  4117.  
  4118. void PreConfigureDevice(HWND hwndDlg, DWORD dwDeviceID)
  4119. {
  4120.     long lReturn;
  4121.     LPVARSTRING lpVarString = NULL;
  4122.     DWORD dwSizeofVarString = sizeof(VARSTRING);
  4123.  
  4124.     // If there isn't already any device configuration information,
  4125.     // then we need to get some.
  4126.     if (g_lpDeviceConfig == NULL)
  4127.     {
  4128.         do
  4129.         {
  4130.             lpVarString = (LPVARSTRING) CheckAndReAllocBuffer(
  4131.                 (LPVOID) lpVarString, dwSizeofVarString,
  4132.                 "PreConfigureDevice - lineGetDevConfig: ");
  4133.  
  4134.             if (lpVarString == NULL)
  4135.                 return;
  4136.  
  4137.             lReturn = lineGetDevConfig(dwDeviceID, lpVarString, 
  4138.                 "comm/datamodem");
  4139.  
  4140.             if (HandleLineErr(lReturn))
  4141.                 ;
  4142.             else
  4143.             {
  4144.                 OutputDebugLineError(lReturn, 
  4145.                     "lineGetDevCaps unhandled error: ");
  4146.                 LocalFree(lpVarString);
  4147.                 return;
  4148.             }
  4149.  
  4150.             if ((lpVarString -> dwNeededSize) > (lpVarString -> dwTotalSize))
  4151.             {
  4152.                 dwSizeofVarString = lpVarString -> dwNeededSize;
  4153.                 lReturn = -1; // Lets loop again.
  4154.             }
  4155.         }
  4156.         while (lReturn != SUCCESS);
  4157.  
  4158.         g_dwSizeDeviceConfig = lpVarString -> dwStringSize;
  4159.  
  4160.         // The extra byte allocated is in case dwStringSize is 0.
  4161.         g_lpDeviceConfig = CheckAndReAllocBuffer(
  4162.                 g_lpDeviceConfig, g_dwSizeDeviceConfig+1,
  4163.                 "PreConfigureDevice - Allocate device config: ");
  4164.  
  4165.         if (!g_lpDeviceConfig)
  4166.         {
  4167.             LocalFree(lpVarString);
  4168.             return;
  4169.         }
  4170.  
  4171.         memcpy(g_lpDeviceConfig, 
  4172.             ((LPBYTE) lpVarString + lpVarString -> dwStringOffset),
  4173.             g_dwSizeDeviceConfig);
  4174.     }
  4175.  
  4176.     // Next make the lineConfigDialogEdit call.
  4177.  
  4178.     // Note that we determine the initial size of the VARSTRING
  4179.     // structure based on the known size of the existing configuration
  4180.     // information.  I make the assumption that this configuration
  4181.     // information is very unlikely to grow by more than 5K or by
  4182.     // more than 5 times.  This is a *very* conservative number.
  4183.     // We do *not* want lineConfigDialogEdit to fail just because there 
  4184.     // wasn't enough room to stored the data.  This would require the user
  4185.     // to go through configuration again and that would be annoying.
  4186.  
  4187.     dwSizeofVarString = 5 * g_dwSizeDeviceConfig + 5000;
  4188.  
  4189.     do
  4190.     {
  4191.         lpVarString = (LPVARSTRING) CheckAndReAllocBuffer(
  4192.             (LPVOID) lpVarString, dwSizeofVarString,
  4193.             "PreConfigureDevice - lineConfigDialogEdit: ");
  4194.  
  4195.         if (lpVarString == NULL)
  4196.             return;
  4197.  
  4198.         lReturn = lineConfigDialogEdit(dwDeviceID, hwndDlg, "comm/datamodem",
  4199.             g_lpDeviceConfig, g_dwSizeDeviceConfig, lpVarString);
  4200.  
  4201.         if (HandleLineErr(lReturn))
  4202.             ;
  4203.         else
  4204.         {
  4205.             OutputDebugLineError(lReturn, 
  4206.                 "lineConfigDialogEdit unhandled error: ");
  4207.             LocalFree(lpVarString);
  4208.             return;
  4209.         }
  4210.  
  4211.         if ((lpVarString -> dwNeededSize) > (lpVarString -> dwTotalSize))
  4212.         {
  4213.             // We had been conservative about making sure the structure was 
  4214.             // big enough.  Unfortunately, not conservative enough.  Hopefully, 
  4215.             // this will not happen a second time because we are *DOUBLING* 
  4216.             // the NeededSize.
  4217.             MessageBox(hwndDlg, 
  4218.                 "Internal Error: Unable to set line configuration.\n"
  4219.                 "Please try again.",
  4220.                 "Oops", MB_OK);
  4221.             dwSizeofVarString = (lpVarString -> dwNeededSize) * 2;
  4222.             lReturn = -1; // Lets loop again.
  4223.         }
  4224.     }
  4225.     while (lReturn != SUCCESS);
  4226.  
  4227.     // Store the configuration information into a global structure
  4228.     // so it can be set at a later time.
  4229.     g_dwSizeDeviceConfig = lpVarString -> dwStringSize;
  4230.     g_lpDeviceConfig = CheckAndReAllocBuffer(
  4231.             g_lpDeviceConfig, g_dwSizeDeviceConfig+1,
  4232.             "PreConfigureDevice - Reallocate device config: ");
  4233.  
  4234.     if (!g_lpDeviceConfig)
  4235.     {
  4236.         LocalFree(lpVarString);
  4237.         return;
  4238.     }
  4239.  
  4240.     memcpy(g_lpDeviceConfig, 
  4241.         ((LPBYTE) lpVarString + lpVarString -> dwStringOffset),
  4242.         g_dwSizeDeviceConfig);
  4243.  
  4244.     LocalFree(lpVarString);
  4245. }
  4246.  
  4247.  
  4248. //
  4249. //  FUNCTION: BOOL GetAddressToDial
  4250. //
  4251. //  PURPOSE: Get an address to dial from the user.
  4252. //
  4253. //  PARAMETERS:
  4254. //    none
  4255. //
  4256. //  RETURN VALUE:
  4257. //    TRUE if a valid device and phone number have been entered by
  4258. //    the user.  FALSE if the user canceled the dialing process.
  4259. //
  4260. //  COMMENTS:
  4261. //
  4262. //    All this function does is launch the "Dial" dialog.
  4263. //
  4264. //
  4265.  
  4266. BOOL GetAddressToDial()
  4267. {
  4268.     BOOL bRet;
  4269.  
  4270.     UpdateStatusBar("Getting Number to Dial",1,0);
  4271.     bRet = DialogBoxParam(hInst, "DialDialog", g_hDlgParentWindow, 
  4272.         DialDialogProc, 0);
  4273.     g_hDialog = NULL;
  4274.     g_hDlgParentWindow = g_hWndMainWindow;
  4275.  
  4276.     if (bRet == FALSE)
  4277.         UpdateStatusBar("Dial aborted",1,0);
  4278.  
  4279.     return bRet;
  4280. }
  4281.  
  4282.  
  4283. //
  4284. //  FUNCTION: DialDialogProc(HWND, UINT, WPARAM, LPARAM)
  4285. //
  4286. //  PURPOSE: Dialog callback procedure for the dialing dialog
  4287. //
  4288. //  PARAMETERS:
  4289. //    hwndDlg - Dialog calling the callback.
  4290. //    uMsg    - Dialog message.
  4291. //    wParam  - uMsg specific.
  4292. //    lParam  - uMsg specific.
  4293. //
  4294. //  RETURN VALUE:
  4295. //    returns 0 - command handled.
  4296. //    returns non-0 - command unhandled
  4297. //
  4298. //  COMMENTS:
  4299. //
  4300. //    This is the dialog to get the phone number and line device 
  4301. //    from the user.  All the relavent information is stored in global
  4302. //    variables to be used later if the dialog returns successfully.
  4303. //
  4304. //
  4305.  
  4306.  
  4307. BOOL CALLBACK DialDialogProc(
  4308.     HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  4309. {
  4310.     // Static variables to store the information from last time the
  4311.     // "Dial" dialog was displayed.  That way the phone number can be
  4312.     // typed once but used several times.
  4313.  
  4314.     static char szCountryName[512] = "";
  4315.     static char szAreaCode[256] = "Not Valid";
  4316.     static char szPhoneNumber[512] = "Not Valid";
  4317.     static DWORD dwUsedDeviceID = MAXDWORD;
  4318.     static BOOL bUsedCountryAndArea = FALSE;
  4319.     static BOOL bHistoryValid = FALSE;
  4320.  
  4321.     switch(uMsg)
  4322.     {
  4323.         case WM_INITDIALOG:
  4324.         {
  4325.             DWORD dwCountryID = 0;
  4326.  
  4327.             // Store the Dialog Window so it can be dismissed if necessary
  4328.             g_hDialog = hwndDlg;
  4329.  
  4330.             // This dialog should be parent to all dialogs.
  4331.             g_hDlgParentWindow = hwndDlg;
  4332.  
  4333.             // Initialize the Dialog Box. Lots to do here.
  4334.  
  4335.             FillTAPILine(hwndDlg);
  4336.             if (g_lpDeviceConfig)
  4337.             {
  4338.                 LocalFree(g_lpDeviceConfig);
  4339.                 g_lpDeviceConfig = NULL;
  4340.             }
  4341.  
  4342.             // If there is a valid history, use it to initialize the controls.
  4343.             if (bHistoryValid)
  4344.             {
  4345.                 FillLocationInfo(hwndDlg, NULL, NULL, NULL);
  4346.                 FillCountryCodeList(hwndDlg, 0);
  4347.  
  4348.                 SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE, CB_SELECTSTRING,
  4349.                     (WPARAM) -1, (LPARAM) (LPCTSTR) szCountryName);
  4350.  
  4351.                 SendDlgItemMessage(hwndDlg, IDC_PHONENUMBER, WM_SETTEXT, 0,
  4352.                     (LPARAM) (LPCTSTR) szPhoneNumber);
  4353.                     
  4354.                 SendDlgItemMessage(hwndDlg, IDC_USEDIALINGRULES, 
  4355.                     BM_SETCHECK, (WPARAM) bUsedCountryAndArea, 0);
  4356.  
  4357.                 SendDlgItemMessage(hwndDlg, IDC_TAPILINE, CB_SETCURSEL,
  4358.                     g_dwDeviceID, 0);
  4359.             }
  4360.             else
  4361.             {
  4362.                 FillLocationInfo(hwndDlg, NULL, &dwCountryID, szAreaCode);
  4363.                 FillCountryCodeList(hwndDlg, dwCountryID);
  4364.                 SendDlgItemMessage(hwndDlg, IDC_USEDIALINGRULES,
  4365.                     BM_SETCHECK, 1, 0);
  4366.             }
  4367.  
  4368.             SendDlgItemMessage(hwndDlg, IDC_AREACODE, WM_SETTEXT, 
  4369.                 0, (LPARAM) (LPCTSTR) szAreaCode);
  4370.  
  4371.             UseDialingRules(hwndDlg);
  4372.             DisplayPhoneNumber(hwndDlg);
  4373.             VerifyAndWarnUsableLine(hwndDlg);
  4374.  
  4375.             return TRUE;
  4376.         }
  4377.  
  4378.         case WM_COMMAND:
  4379.         {
  4380.             switch(LOWORD(wParam))
  4381.             {
  4382.                 case IDC_TAPILINE:
  4383.                     if (HIWORD(wParam) == CBN_SELENDOK)
  4384.                     {
  4385.                         if (g_lpDeviceConfig)
  4386.                         {
  4387.                             LocalFree(g_lpDeviceConfig);
  4388.                             g_lpDeviceConfig = NULL;
  4389.                         }
  4390.                         DisplayPhoneNumber(hwndDlg);
  4391.                         VerifyAndWarnUsableLine(hwndDlg);
  4392.                     }
  4393.                     return TRUE;
  4394.  
  4395.                 case IDC_CONFIGURELINE:
  4396.                 {
  4397.                     DWORD dwDeviceID;
  4398.                     dwDeviceID = SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
  4399.                         CB_GETCURSEL, 0, 0);
  4400.                     PreConfigureDevice(hwndDlg, dwDeviceID);
  4401.                     DisplayPhoneNumber(hwndDlg);
  4402.                     return TRUE;
  4403.                 }
  4404.  
  4405.                 case IDC_COUNTRYCODE:
  4406.                     if (HIWORD(wParam) == CBN_SELENDOK)
  4407.                         DisplayPhoneNumber(hwndDlg);
  4408.                     return TRUE;
  4409.  
  4410.                 case IDC_AREACODE:
  4411.                 case IDC_PHONENUMBER:
  4412.                     if (HIWORD(wParam) == EN_CHANGE)
  4413.                         DisplayPhoneNumber(hwndDlg);
  4414.                     return TRUE;
  4415.  
  4416.                 case IDC_USEDIALINGRULES:
  4417.                     if (HIWORD(wParam) == BN_CLICKED)
  4418.                     {
  4419.                         UseDialingRules(hwndDlg);
  4420.                         DisplayPhoneNumber(hwndDlg);
  4421.                     }
  4422.                     return TRUE;
  4423.  
  4424.                 case IDC_LOCATION:
  4425.                     if (HIWORD(wParam) == CBN_CLOSEUP)
  4426.                     {
  4427.                         char szCurrentLocation[1024];
  4428.                         int nCurrentSelection;
  4429.  
  4430.                         nCurrentSelection = SendDlgItemMessage(hwndDlg, 
  4431.                             IDC_LOCATION, CB_GETCURSEL, 0, 0);
  4432.                         SendDlgItemMessage(hwndDlg, IDC_LOCATION,
  4433.                             CB_GETLBTEXT, nCurrentSelection, 
  4434.                             (LPARAM) (LPCTSTR) szCurrentLocation);
  4435.  
  4436.                         // If the user selected a 'location', make it current.
  4437.                         FillLocationInfo(hwndDlg, szCurrentLocation, NULL, NULL);
  4438.                         DisplayPhoneNumber(hwndDlg);
  4439.                     }
  4440.                     return TRUE;
  4441.  
  4442.                 case IDC_DIALINGPROPERTIES:
  4443.                 {
  4444.                     char szAddress[1024];
  4445.                     DWORD dwDeviceID;
  4446.                     long lReturn;
  4447.  
  4448.                     dwDeviceID = SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
  4449.                             CB_GETCURSEL, 0, 0);
  4450.  
  4451.                     SendDlgItemMessage(hwndDlg, IDC_CANONICALNUMBER,
  4452.                         WM_GETTEXT, 1023, (LPARAM) (LPCTSTR) szAddress);
  4453.                       
  4454.                     lReturn = lineTranslateDialog(g_hLineApp, dwDeviceID,
  4455.                         SAMPLE_TAPI_VERSION, hwndDlg, szAddress);
  4456.  
  4457.                     if (lReturn != SUCCESS)
  4458.                         OutputDebugLineError(lReturn,"lineTranslateDialog: ");
  4459.  
  4460.                     // The user could have changed the default location, or
  4461.                     // added or removed a location while in the 'Dialing
  4462.                     // Properties' dialog.  Refill the Location Info.
  4463.                     FillLocationInfo(hwndDlg, NULL, NULL, NULL);
  4464.                     DisplayPhoneNumber(hwndDlg);
  4465.  
  4466.                     return TRUE;
  4467.                 }
  4468.  
  4469.                 case IDCANCEL:
  4470.                     EndDialog(hwndDlg, FALSE);
  4471.                     return TRUE;
  4472.  
  4473.                 case IDC_DIAL:
  4474.                 {
  4475.                     // The Dial button has to be enabled and the line has
  4476.                     // to be currently usable to continue.
  4477.                     if (!(IsWindowEnabled((HWND)lParam) &&
  4478.                           VerifyAndWarnUsableLine(hwndDlg)))
  4479.                         return TRUE;
  4480.  
  4481.                     DisplayPhoneNumber(hwndDlg);
  4482.  
  4483.                     // Get the displayable and dialable numbers and store
  4484.                     // them in global variables to be used while dialing.
  4485.                     SendDlgItemMessage(hwndDlg, IDC_DISPLAYABLENUMBER,
  4486.                         WM_GETTEXT, 1023, (LPARAM) (LPCTSTR) g_szDisplayableAddress);
  4487.  
  4488.                     SendDlgItemMessage(hwndDlg, IDC_DIALABLENUMBER,
  4489.                         WM_GETTEXT, 1023, (LPARAM) (LPCTSTR) g_szDialableAddress);
  4490.  
  4491.                     // Store all the relavent information in static
  4492.                     // variables so they will be available the next time a
  4493.                     // number is dialed.
  4494.                     SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE,
  4495.                         WM_GETTEXT, 511, (LPARAM) (LPCTSTR) szCountryName);
  4496.  
  4497.                     SendDlgItemMessage(hwndDlg, IDC_AREACODE,
  4498.                         WM_GETTEXT, 255, (LPARAM) (LPCTSTR) szAreaCode);
  4499.  
  4500.                     SendDlgItemMessage(hwndDlg, IDC_PHONENUMBER,
  4501.                         WM_GETTEXT, 511, (LPARAM) (LPCTSTR) szPhoneNumber);
  4502.  
  4503.                     bUsedCountryAndArea = (BOOL) SendDlgItemMessage(hwndDlg,
  4504.                         IDC_USEDIALINGRULES, BM_GETCHECK, 0, 0);
  4505.  
  4506.                     bHistoryValid = TRUE;
  4507.  
  4508.                     EndDialog(hwndDlg, TRUE);
  4509.                     return TRUE;
  4510.                 }
  4511.  
  4512.  
  4513.                 // This message is actually posted to the dialog from the
  4514.                 // lineCallbackFunc when it receives a 
  4515.                 // LINEDEVSTATE_TRANSLATECHANGE message.  Notify the user and
  4516.                 // retranslate the number.  Also refill the Location Info
  4517.                 // since this could have been generated by a location change.
  4518.                 case IDC_CONFIGURATIONCHANGED:
  4519.                 {
  4520.                     FillLocationInfo(hwndDlg, NULL, NULL, NULL);
  4521.                     DisplayPhoneNumber(hwndDlg);
  4522.  
  4523.                     MessageBox(hwndDlg, 
  4524.                         "Location Configuration has been changed.",
  4525.                         "Warning",MB_OK);
  4526.  
  4527.                     return TRUE;
  4528.                 }
  4529.  
  4530.                 // If we get a LINE_CREATE message, all that needs to be done
  4531.                 // is to reset this controls contents.  The selected line
  4532.                 // won't change and no lines will be removed.
  4533.                 case IDC_LINECREATE:
  4534.                 {
  4535.                     FillTAPILine(hwndDlg);
  4536.                     return TRUE;
  4537.                 }
  4538.  
  4539.                 default:
  4540.                     break;
  4541.             }
  4542.  
  4543.             break;
  4544.         }
  4545.  
  4546.         // This dialog has the DS_CONTEXTMENU flag so that the right mouse 
  4547.         // button will send this message.  Bring up the appropriate help page.
  4548.         case WM_CONTEXTMENU:
  4549.         {
  4550.  
  4551.             if (hwndDlg != (HWND) wParam)
  4552.                 WinHelp ((HWND)wParam,
  4553.                     "TAPICOMM.HLP",
  4554.                      HELP_CONTEXTMENU,
  4555.                     (DWORD)(LPVOID) g_adwSampleMenuHelpIDs);
  4556.             break;
  4557.         }
  4558.  
  4559.         // Bring up the appropriate help page.
  4560.         case WM_HELP:
  4561.         {
  4562.             LPHELPINFO lphi;
  4563.  
  4564.             lphi = (LPHELPINFO)lParam;
  4565.             if ((lphi->iContextType == HELPINFO_WINDOW) &&
  4566.                 (hwndDlg != lphi->hItemHandle) &&
  4567.                 (lphi->iCtrlId < IDC_NOHELPCONTROLS))
  4568.             {
  4569.                 WinHelp (lphi->hItemHandle,
  4570.                     "TAPICOMM.HLP",
  4571.                     HELP_WM_HELP,
  4572.                     (DWORD)(LPVOID) g_adwSampleMenuHelpIDs);
  4573.             }
  4574.         
  4575.             return TRUE; 
  4576.         }
  4577.  
  4578.         default:
  4579.             break;
  4580.     }
  4581.  
  4582.     return FALSE;
  4583. }
  4584.