TapiCode.C
上传用户:woweijixie
上传日期:2018-12-11
资源大小:131k
文件大小:146k
源码类别:

TAPI编程

开发平台:

Visual C++

  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  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. #include <tapi.h>
  73. #include <windows.h>
  74. #include <string.h>
  75. #include "globals.h"
  76. #include "TapiInfo.h"
  77. #include "TapiCode.h"
  78. #include "CommCode.h"   
  79. #include "resource.h"
  80. #include "statbar.h"
  81. #include "toolbar.h"
  82. // All TAPI line functions return 0 for SUCCESS, so define it.
  83. #define SUCCESS 0
  84. // Possible return error for resynchronization functions.
  85. #define WAITERR_WAITABORTED  1
  86. #define WAITERR_WAITTIMEDOUT 2
  87. // Reasons why a line device might not be usable by TapiComm.
  88. #define LINENOTUSEABLE_ERROR            1
  89. #define LINENOTUSEABLE_NOVOICE          2
  90. #define LINENOTUSEABLE_NODATAMODEM      3
  91. #define LINENOTUSEABLE_NOMAKECALL       4
  92. #define LINENOTUSEABLE_ALLOCATED        5
  93. #define LINENOTUSEABLE_INUSE            6
  94. #define LINENOTUSEABLE_NOCOMMDATAMODEM  7
  95. // Constant used in WaitForCallState when any new
  96. // callstate message is acceptable.
  97. #define I_LINECALLSTATE_ANY 0
  98.  // Wait up to 30 seconds for an async completion.
  99. #define WAITTIMEOUT 30000
  100. // TAPI version that this sample is designed to use.
  101. #define SAMPLE_TAPI_VERSION 0x00010004
  102. // Global TAPI variables.
  103. HWND     g_hWndMainWindow = NULL;   // Apps main window.
  104. HWND     g_hDlgParentWindow = NULL; // This will be the parent of all dialogs.
  105. HLINEAPP g_hLineApp = NULL;
  106. DWORD    g_dwNumDevs = 0;
  107. // Global variable that holds the handle to a TAPI dialog
  108. // that needs to be dismissed if line conditions change.
  109. HWND g_hDialog = NULL;
  110. // Global flags to prevent re-entrancy problems.
  111. BOOL g_bShuttingDown = FALSE;
  112. BOOL g_bStoppingCall = FALSE;
  113. BOOL g_bInitializing = FALSE;
  114. // This sample only supports one call in progress at a time.
  115. BOOL g_bTapiInUse = FALSE;
  116. // Data needed per call.  This sample only supports one call.
  117. HCALL g_hCall = NULL;
  118. HLINE g_hLine = NULL;
  119. DWORD g_dwDeviceID = 0;
  120. DWORD g_dwAPIVersion = 0;
  121. DWORD g_dwCallState = 0;
  122. char  g_szDisplayableAddress[1024] = "";
  123. char  g_szDialableAddress[1024] = "";
  124. BOOL  g_bConnected = FALSE;
  125. LPVOID g_lpDeviceConfig = NULL;
  126. DWORD g_dwSizeDeviceConfig;
  127. // Global variables to allow us to do various waits.
  128. BOOL  g_bReplyRecieved;
  129. DWORD g_dwRequestedID;
  130. long  g_lAsyncReply;
  131. BOOL  g_bCallStateReceived;
  132. // Structures needed to handle special non-dialable characters.
  133. #define g_sizeofNonDialable (sizeof(g_sNonDialable)/sizeof(g_sNonDialable[0]))
  134. typedef struct {
  135.     LONG lError;
  136.     DWORD dwDevCapFlag;
  137.     LPSTR szToken;
  138.     LPSTR szMsg;
  139. } NONDIALTOKENS;
  140. NONDIALTOKENS g_sNonDialable[] = {
  141.     {LINEERR_DIALBILLING,  LINEDEVCAPFLAGS_DIALBILLING,  "$", 
  142.             "Wait for the credit card bong tone" },
  143.     {LINEERR_DIALDIALTONE, LINEDEVCAPFLAGS_DIALDIALTONE, "W", 
  144.             "Wait for the second dial tone" },
  145.     {LINEERR_DIALDIALTONE, LINEDEVCAPFLAGS_DIALDIALTONE, "w", 
  146.             "Wait for the second dial tone" },
  147.     {LINEERR_DIALQUIET,    LINEDEVCAPFLAGS_DIALQUIET,    "@", 
  148.             "Wait for the remote end to answer" },
  149.     {LINEERR_DIALPROMPT,   0,                            "?", 
  150.             "Press OK when you are ready to continue dialing"},
  151. };
  152. // "Dial" dialog controls and their associated help page IDs
  153. DWORD g_adwSampleMenuHelpIDs[] = 
  154. {
  155.     IDC_COUNTRYCODE          , IDC_COUNTRYCODE,
  156.     IDC_STATICCOUNTRYCODE    , IDC_COUNTRYCODE,
  157.     IDC_AREACODE             , IDC_AREACODE,
  158.     IDC_STATICAREACODE       , IDC_AREACODE,
  159.     IDC_PHONENUMBER          , IDC_PHONENUMBER,
  160.     IDC_STATICPHONENUMBER    , IDC_PHONENUMBER,
  161.     IDC_USEDIALINGRULES      , IDC_USEDIALINGRULES,
  162.     IDC_LOCATION             , IDC_LOCATION,
  163.     IDC_STATICLOCATION       , IDC_LOCATION,
  164.     IDC_CALLINGCARD          , IDC_CALLINGCARD,
  165.     IDC_STATICCALLINGCARD    , IDC_CALLINGCARD,
  166.     IDC_DIALINGPROPERTIES    , IDC_DIALINGPROPERTIES,
  167.     IDC_TAPILINE             , IDC_TAPILINE,
  168.     IDC_STATICTAPILINE       , IDC_TAPILINE,
  169.     IDC_CONFIGURELINE        , IDC_CONFIGURELINE,
  170.     IDC_CANONICALNUMBER      , IDC_CANONICALNUMBER,
  171.     IDC_STATICCANONICAL      , IDC_CANONICALNUMBER,
  172.     IDC_DIALABLENUMBER       , IDC_DIALABLENUMBER,
  173.     IDC_STATICDIALABLE       , IDC_DIALABLENUMBER,
  174.     IDC_DISPLAYABLENUMBER    , IDC_DISPLAYABLENUMBER,
  175.     IDC_STATICDISPLAYABLE    , IDC_DISPLAYABLENUMBER,
  176.     IDC_DIAL                 , IDC_DIAL,
  177.     IDC_LINEICON             , IDC_LINEICON,
  178.     //IDC_STATICWHERETODIAL    , IDC_STATICWHERETODIAL,
  179.     //IDC_STATICHOWTODIAL      , IDC_STATICHOWTODIAL,
  180.     //IDC_STATICCONNECTUSING   , IDC_STATICCONNECTUSING,
  181.     //IDC_STATICPHONENUMBER    , IDC_PHONENUMBER,
  182.     0,0
  183. };
  184. //**************************************************
  185. // Prototypes for functions used only in this module.
  186. //**************************************************
  187. BOOL DialCallInParts (
  188.     LPLINEDEVCAPS lpLineDevCaps,
  189.     LPCSTR lpszAddress,
  190.     LPCSTR lpszDisplayableAddress);
  191. LPLINECALLPARAMS CreateCallParams (
  192.     LPLINECALLPARAMS lpCallParams,
  193.     LPCSTR lpszDisplayableAddress);
  194. DWORD I_lineNegotiateAPIVersion (
  195.     DWORD dwDeviceID);
  196. LPVOID CheckAndReAllocBuffer(
  197.     LPVOID lpBuffer, size_t sizeBufferMinimum, 
  198.     LPCSTR szApiPhrase);
  199. LPLINEDEVCAPS I_lineGetDevCaps (
  200.     LPLINEDEVCAPS lpLineDevCaps,
  201.     DWORD dwDeviceID,
  202.     DWORD dwAPIVersion);
  203. LPLINEADDRESSSTATUS I_lineGetAddressStatus (
  204.     LPLINEADDRESSSTATUS lpLineAddressStatus,
  205.     HLINE hLine,
  206.     DWORD dwAddressID);
  207. LPLINETRANSLATEOUTPUT I_lineTranslateAddress (
  208.     LPLINETRANSLATEOUTPUT lpLineTranslateOutput,
  209.     DWORD dwDeviceID,
  210.     DWORD dwAPIVersion,
  211.     LPCSTR lpszDialAddress);
  212. LPLINECALLSTATUS I_lineGetCallStatus (
  213.     LPLINECALLSTATUS lpLineCallStatus,
  214.     HCALL hCall);
  215. LPLINEADDRESSCAPS I_lineGetAddressCaps (
  216.     LPLINEADDRESSCAPS lpLineAddressCaps,
  217.     DWORD dwDeviceID, DWORD dwAddressID,
  218.     DWORD dwAPIVersion, DWORD dwExtVersion);
  219. long WaitForCallState (DWORD dwNewCallState);
  220. long WaitForReply (long lRequestID);
  221. void CALLBACK lineCallbackFunc(
  222.     DWORD hDevice, DWORD dwMsg, DWORD dwCallbackInstance, 
  223.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);
  224. void DoLineReply(
  225.     DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
  226.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);
  227. void DoLineClose(
  228.     DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
  229.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);
  230. void DoLineDevState(
  231.     DWORD dwDevice, DWORD dwsg, DWORD dwCallbackInstance,
  232.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);
  233. void DoLineCallState(
  234.     DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
  235.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);
  236. void DoLineCreate(
  237.     DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
  238.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);
  239. BOOL HandleLineErr(long lLineErr);
  240. BOOL HandleIniFileCorrupt();
  241. BOOL HandleNoDriver();
  242. BOOL HandleNoDevicesInstalled();
  243. BOOL HandleReInit();
  244. BOOL HandleNoMultipleInstance();
  245. BOOL HandleNoMem();
  246. BOOL HandleOperationFailed();
  247. BOOL HandleResourceUnavail();
  248. BOOL LaunchModemControlPanelAdd();
  249. void WarningBox(LPCSTR lpszMessage);
  250. BOOL CALLBACK DialDialogProc(
  251.     HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  252. BOOL GetAddressToDial();
  253. DWORD I_lineNegotiateLegacyAPIVersion(DWORD dwDeviceID);
  254. long VerifyUsableLine(DWORD dwDeviceID);
  255. void FillTAPILine(HWND hwndDlg);
  256. BOOL VerifyAndWarnUsableLine(HWND hwndDlg);
  257. void FillCountryCodeList(HWND hwndDlg, DWORD dwDefaultCountryID);
  258. void FillLocationInfo(HWND hwndDlg, LPSTR lpszCurrentLocation, 
  259.     LPDWORD lpdwCountryID, LPSTR lpszAreaCode);
  260. void UseDialingRules(HWND hwndDlg);
  261. void DisplayPhoneNumber(HWND hwndDlg);
  262. void PreConfigureDevice(HWND hwndDlg, DWORD dwDeviceID);
  263. //**************************************************
  264. // Entry points from the UI
  265. //**************************************************
  266. //
  267. //  FUNCTION: BOOL InitializeTAPI(HWND)
  268. //
  269. //  PURPOSE: Initializes TAPI
  270. //
  271. //  PARAMETERS:
  272. //    hWndParent - Window to use as parent of any dialogs.
  273. //
  274. //  RETURN VALUE:
  275. //    Always returns 0 - command handled.
  276. //
  277. //  COMMENTS:
  278. //
  279. //    This is the API that initializes the app with TAPI.
  280. //    If NULL is passed for the hWndParent, then its assumed
  281. //    that re-initialization has occurred and the previous hWnd
  282. //    is used.
  283. //
  284. //
  285. BOOL InitializeTAPI(HWND hWndParent)
  286. {
  287.     long lReturn;
  288.     BOOL bTryReInit = TRUE;
  289.     // If we're already initialized, then initialization succeeds.
  290.     if (g_hLineApp)
  291.         return TRUE;
  292.     // If we're in the middle of initializing, then fail, we're not done.
  293.     if (g_bInitializing)
  294.         return FALSE;
  295.     g_bInitializing = TRUE;
  296.     // Initialize TAPI
  297.     do
  298.     {
  299.         lReturn = lineInitialize(&g_hLineApp, hInst, 
  300.             lineCallbackFunc, "TapiComm", &g_dwNumDevs);
  301.         // If we get this error, its because some other app has yet
  302.         // to respond to the REINIT message.  Wait 5 seconds and try
  303.         // again.  If it still doesn't respond, tell the user.
  304.         if (lReturn == LINEERR_REINIT)
  305.         {
  306.             if (bTryReInit)
  307.             {
  308.                 MSG msg; 
  309.                 DWORD dwTimeStarted;
  310.                 dwTimeStarted = GetTickCount();
  311.                 while(GetTickCount() - dwTimeStarted < 5000)
  312.                 {
  313.                     if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
  314.                     {
  315.                         TranslateMessage(&msg);
  316.                         DispatchMessage(&msg);
  317.                     }
  318.                 }
  319.             
  320.                 bTryReInit = FALSE;
  321.                 continue;
  322.             }
  323.             else
  324.             {
  325.                 MessageBox(g_hDlgParentWindow,
  326.                     "A change to the system configuration requires that "
  327.                     "all Telephony applications relinquish their use of "
  328.                     "Telephony before any can progress.  "
  329.                     "Some have not yet done so."
  330.                     ,"Warning",MB_OK);
  331.                 g_bInitializing = FALSE;
  332.                 return FALSE;
  333.             }
  334.         }
  335.         if (lReturn == LINEERR_NODEVICE)
  336.         {
  337.             if (HandleNoDevicesInstalled())
  338.                 continue;
  339.             else
  340.             {
  341.                 OutputDebugString("No devices installed.n");
  342.                 g_bInitializing = FALSE;
  343.                 return FALSE;
  344.             }
  345.         }
  346.         if (HandleLineErr(lReturn))
  347.             continue;
  348.         else
  349.         {
  350.             OutputDebugLineError(lReturn, 
  351.                 "lineInitialize unhandled error: ");
  352.             g_bInitializing = FALSE;
  353.             return FALSE;
  354.         }
  355.     }
  356.     while(lReturn != SUCCESS);
  357.     // if hWndParent is a valid hWnd, we keep it as the parent for
  358.     // all dialogs.
  359.     if (IsWindow(hWndParent))
  360.     {
  361.         g_hDlgParentWindow = g_hWndMainWindow = hWndParent;
  362.     }
  363.     else
  364.     {
  365.         // Has the old g_hWndMainWindow gone away?
  366.         if (!IsWindow(g_hWndMainWindow))
  367.         {
  368.             OutputDebugString("Main window unavailable.n");
  369.             g_hDlgParentWindow = g_hWndMainWindow = NULL;
  370.         }
  371.     }
  372.     g_hCall = NULL;
  373.     g_hLine = NULL;
  374.     OutputDebugString("Tapi initialized.n");
  375.     g_bInitializing = FALSE;
  376.     return TRUE;
  377. }
  378. //
  379. //  FUNCTION: BOOL ShutdownTAPI()
  380. //
  381. //  PURPOSE: Shuts down all use of TAPI
  382. //
  383. //  PARAMETERS:
  384. //    None
  385. //
  386. //  RETURN VALUE:
  387. //    True if TAPI successfully shut down.
  388. //
  389. //  COMMENTS:
  390. //
  391. //    If ShutdownTAPI fails, then its likely either a problem
  392. //    with the service provider (and might require a system
  393. //    reboot to correct) or the application ran out of memory.
  394. //
  395. //
  396. BOOL ShutdownTAPI()
  397. {
  398.     long lReturn;
  399.     // If we aren't initialized, then Shutdown is unnecessary.
  400.     if (g_hLineApp == NULL)
  401.         return TRUE;
  402.     // Prevent ShutdownTAPI re-entrancy problems.
  403.     if (g_bShuttingDown)
  404.         return TRUE;
  405.     g_bShuttingDown = TRUE;
  406.     HangupCall();
  407.     
  408.     do
  409.     {
  410.         lReturn = lineShutdown(g_hLineApp);
  411.         if (HandleLineErr(lReturn))
  412.             continue;
  413.         else
  414.         {
  415.             OutputDebugLineError(lReturn, "lineShutdown unhandled error: ");
  416.             break;
  417.         }
  418.     }
  419.     while(lReturn != SUCCESS);
  420.     g_bTapiInUse = FALSE;
  421.     g_bConnected = FALSE;
  422.     g_hLineApp = NULL;
  423.     g_hCall = NULL;
  424.     g_hLine = NULL;
  425.     g_bShuttingDown = FALSE;
  426.     OutputDebugString("TAPI uninitialized.n");
  427.     return TRUE;
  428. }
  429. //
  430. //  FUNCTION: BOOL HangupCall()
  431. //
  432. //  PURPOSE: Hangup the call in progress if it exists.
  433. //
  434. //  PARAMETERS:
  435. //    none
  436. //
  437. //  RETURN VALUE:
  438. //    TRUE if call hung up successfully.
  439. //
  440. //  COMMENTS:
  441. //
  442. //    If HangupCall fails, then its likely either a problem
  443. //    with the service provider (and might require a system
  444. //    reboot to correct) or the application ran out of memory.
  445. //
  446. //
  447. BOOL HangupCall()
  448. {         
  449.     LPLINECALLSTATUS pLineCallStatus = NULL;
  450.     long lReturn;
  451.     // Prevent HangupCall re-entrancy problems.
  452.     if (g_bStoppingCall)
  453.         return TRUE;
  454.     // if the 'Call' dialog is up, dismiss it.
  455.     if (g_hDialog)
  456.         PostMessage(g_hDialog, WM_COMMAND, IDCANCEL, 0);
  457.     // If Tapi is not being used right now, then the call is hung up.
  458.     if (!g_bTapiInUse)
  459.         return TRUE;
  460.     g_bStoppingCall = TRUE;
  461.     OutputDebugString("Stopping Call in progressn");
  462.     // Disable the 'hangup call' user interface.
  463.     EnableHangupCall(g_hWndMainWindow, FALSE);
  464.     // Stop any data communications on the comm port.
  465.     StopComm();
  466.     // If there is a call in progress, drop and deallocate it.
  467.     if (g_hCall)
  468.     {
  469.         // I_lineGetCallStatus returns a LocalAlloc()d buffer
  470.         pLineCallStatus = I_lineGetCallStatus(pLineCallStatus, g_hCall);
  471.         if (pLineCallStatus == NULL)
  472.         {
  473.             ShutdownTAPI();
  474.             g_bStoppingCall = FALSE;
  475.             return FALSE;
  476.         }
  477.         // Only drop the call when the line is not IDLE.
  478.         if (!((pLineCallStatus -> dwCallState) & LINECALLSTATE_IDLE))
  479.         {
  480.             do
  481.             {
  482.                 lReturn = WaitForReply(lineDrop(g_hCall, NULL, 0));
  483.                 if (lReturn == WAITERR_WAITTIMEDOUT)
  484.                 {
  485.                     OutputDebugString("Call timed out in WaitForReply.n");
  486.                     break;
  487.                 }
  488.                 if (lReturn == WAITERR_WAITABORTED)
  489.                 {
  490.                     OutputDebugString("lineDrop: WAITERR_WAITABORTED.n");
  491.                     break;
  492.                 }
  493.                 // Was the call already in IDLE?
  494.                 if (lReturn == LINEERR_INVALCALLSTATE)
  495.                     break;
  496.                 if (HandleLineErr(lReturn))
  497.                     continue;
  498.                 else
  499.                 {
  500.                     OutputDebugLineError(lReturn, 
  501.                         "lineDrop unhandled error: ");
  502.                     break;
  503.                 }
  504.             }
  505.             while(lReturn != SUCCESS);
  506.             // Wait for the dropped call to go IDLE before continuing.
  507.             lReturn = WaitForCallState(LINECALLSTATE_IDLE);
  508.             if (lReturn == WAITERR_WAITTIMEDOUT)
  509.                 OutputDebugString("Call timed out waiting for IDLE state.n");
  510.             if (lReturn == WAITERR_WAITABORTED)
  511.                 OutputDebugString(
  512.                     "WAITERR_WAITABORTED while waiting for IDLE state.n");
  513.             OutputDebugString("Call Dropped.n");
  514.         }
  515.         // The call is now idle.  Deallocate it!
  516.         do
  517.         {
  518.             lReturn = lineDeallocateCall(g_hCall);
  519.             if (HandleLineErr(lReturn))
  520.                 continue;
  521.             else
  522.             {
  523.                 OutputDebugLineError(lReturn,
  524.                     "lineDeallocateCall unhandled error: ");
  525.                 break;
  526.             }
  527.         }
  528.         while(lReturn != SUCCESS);
  529.             
  530.         OutputDebugString("Call Deallocated.n");
  531.     }
  532.     // if we have a line open, close it.
  533.     if (g_hLine)
  534.     {
  535.         do
  536.         {
  537.             lReturn = lineClose(g_hLine);
  538.             if (HandleLineErr(lReturn))
  539.                 continue;
  540.             else
  541.             {
  542.                 OutputDebugLineError(lReturn, 
  543.                     "lineClose unhandled error: ");
  544.                 break;
  545.             }
  546.         }
  547.         while(lReturn != SUCCESS);
  548.         
  549.         OutputDebugString("Line Closed.n");
  550.     }
  551.     // Call and Line are taken care of.  Finish cleaning up.
  552.     // If there is device configuration information, free the memory.
  553.     if (g_lpDeviceConfig)
  554.         LocalFree(g_lpDeviceConfig);
  555.     g_lpDeviceConfig = NULL;
  556.     g_hCall = NULL;
  557.     g_hLine = NULL;
  558.     g_bConnected = FALSE;
  559.     g_bTapiInUse = FALSE;
  560.     g_bStoppingCall = FALSE; // allow HangupCall to be called again.
  561.     OutputDebugString("Call stoppedn");
  562.     // Update the user interface.
  563.     UpdateStatusBar("Ready to make a call.",1,0);
  564.     EnableMakeCall(g_hWndMainWindow, TRUE);
  565.     // Need to free LocalAlloc()d buffer returned from I_lineGetCallStatus
  566.     if (pLineCallStatus)
  567.         LocalFree(pLineCallStatus);  
  568.         
  569.     return TRUE;
  570. }
  571. //
  572. //  FUNCTION: PostHangupCall()
  573. //
  574. //  PURPOSE: Posts a message to the main TAPI thread to hangup the call.
  575. //
  576. //  PARAMETERS:
  577. //    none
  578. //
  579. //  RETURN VALUE:
  580. //    none
  581. //
  582. //  COMMENTS:
  583. //
  584. //    TAPI is thread specific, meaning that only the thread that does the
  585. //    lineInitialize can get asynchronous messages through the callback.
  586. //    Since the HangupCall can potentially go into a loop waiting for 
  587. //    specific events, any other threads that call HangupCall can cause
  588. //    timing confusion.  Best to just have other threads 'ask' the main thread
  589. //    to hangup the call.
  590. //
  591. void PostHangupCall()
  592. {
  593.     PostMessage(g_hWndMainWindow, WM_COMMAND, IDM_HANGUPCALL, 0);
  594. }
  595. //
  596. //  FUNCTION: DialCall()
  597. //
  598. //  PURPOSE: Get a number from the user and dial it.
  599. //
  600. //  PARAMETERS:
  601. //    none
  602. //
  603. //  RETURN VALUE:
  604. //    TRUE if able to get a number, find a line, and dial successfully.
  605. //
  606. //  COMMENTS:
  607. //
  608. //    This function makes several assumptions:
  609. //    - The number dialed will always explicitly come from the user.
  610. //    - There will only be one outgoing address per line.
  611. //
  612. BOOL DialCall()
  613. {
  614.     long lReturn;
  615.     LPLINEADDRESSSTATUS lpLineAddressStatus = NULL;
  616.     LPLINEDEVCAPS lpLineDevCaps = NULL;
  617.     if (g_bTapiInUse)
  618.     {
  619.         OutputDebugString("A call is already being handledn");
  620.         return FALSE;
  621.     }
  622.     // If TAPI isn't initialized, its either because we couldn't initialize
  623.     // at startup (and this might have been corrected by now), or because
  624.     // a REINIT event was received.  In either case, try to init now.
  625.     if (!g_hLineApp)
  626.     {
  627.         if (!InitializeTAPI(NULL))
  628.             return FALSE;
  629.     }
  630.     // If there are no line devices installed on the machine, lets give
  631.     // the user the opportunity to install one.
  632.     if (g_dwNumDevs < 1)
  633.     {
  634.         if (!HandleNoDevicesInstalled())
  635.             return FALSE;
  636.     }
  637.     // We now have a call active.  Prevent future calls.
  638.     g_bTapiInUse = TRUE;
  639.     EnableMakeCall(g_hWndMainWindow, FALSE);
  640.     // Get a phone number from the user.
  641.     // Phone number will be placed in global variables if successful
  642.     if (!GetAddressToDial())
  643.     {
  644.         HangupCall();
  645.         goto DeleteBuffers;
  646.     }
  647.     // Negotiate the API version to use for this device.
  648.     g_dwAPIVersion = I_lineNegotiateAPIVersion(g_dwDeviceID);
  649.     if (g_dwAPIVersion == 0)
  650.     {
  651.         MessageBox(g_hDlgParentWindow,
  652.             "Line Version unsupported by this Sample",
  653.             "Unable to Use Line",MB_OK);
  654.         HangupCall();
  655.         goto DeleteBuffers;
  656.     }
  657.     // Need to check the DevCaps to make sure this line is usable.
  658.     // The 'Dial' dialog checks also, but better safe than sorry.
  659.     lpLineDevCaps = I_lineGetDevCaps(lpLineDevCaps,
  660.         g_dwDeviceID, g_dwAPIVersion);
  661.     if (lpLineDevCaps == NULL)
  662.     {
  663.         HangupCall();
  664.         MessageBox(g_hDlgParentWindow,
  665.             "Error on Requested line",
  666.             "Unable to Use Line",MB_OK);
  667.         goto DeleteBuffers;
  668.     }
  669.     if (!(lpLineDevCaps->dwBearerModes & LINEBEARERMODE_VOICE ))
  670.     {
  671.         HangupCall();
  672.         MessageBox(g_hDlgParentWindow,
  673.             "Error on Requested line",
  674.             "The selected line doesn't support VOICE capabilities",
  675.             MB_OK);
  676.         goto DeleteBuffers;
  677.     }
  678.     if (!(lpLineDevCaps->dwMediaModes & LINEMEDIAMODE_DATAMODEM))
  679.     {
  680.         HangupCall();
  681.         MessageBox(g_hDlgParentWindow,
  682.             "Error on Requested line",
  683.             "The selected line doesn't support DATAMODEM capabilities",
  684.             MB_OK);
  685.         goto DeleteBuffers;
  686.     }
  687.     // Does this line have the capability to make calls?
  688.     // It is possible that some lines can't make outbound calls.
  689.     if (!(lpLineDevCaps->dwLineFeatures & LINEFEATURE_MAKECALL))
  690.     {
  691.         HangupCall();
  692.         MessageBox(g_hDlgParentWindow,
  693.             "Error on Requested line",
  694.             "The selected line doesn't support MAKECALL capabilities",
  695.             MB_OK);
  696.         goto DeleteBuffers;
  697.     }
  698.     // Open the Line for an outgoing DATAMODEM call.
  699.     do
  700.     {
  701.         lReturn = lineOpen(g_hLineApp, g_dwDeviceID, &g_hLine,
  702.             g_dwAPIVersion, 0, 0,
  703.             LINECALLPRIVILEGE_NONE, LINEMEDIAMODE_DATAMODEM,
  704.             0);
  705.         if(lReturn == LINEERR_ALLOCATED)
  706.         {
  707.             HangupCall();
  708.             MessageBox(g_hDlgParentWindow,
  709.                 "Line is already in use by a non-TAPI application "
  710.                 "or by another TAPI Service Provider.",
  711.                 "Unable to Use Line",MB_OK);
  712.             goto DeleteBuffers;
  713.         }
  714.         if (HandleLineErr(lReturn))
  715.             continue;
  716.         else
  717.         {
  718.             OutputDebugLineError(lReturn, "lineOpen unhandled error: ");
  719.             MessageBox(g_hDlgParentWindow,
  720.                 "Error on Requested line",
  721.                 "Unable to Use Line",MB_OK);
  722.             HangupCall();
  723.             goto DeleteBuffers;
  724.         }
  725.     }
  726.     while(lReturn != SUCCESS);
  727.     // Tell the service provider that we want all notifications that
  728.     // have anything to do with this line.
  729.     do
  730.     {
  731.         // Set the messages we are interested in.
  732.         // Note that while most applications aren't really interested
  733.         // in dealing with all of the possible messages, its interesting
  734.         // to see which come through the callback for testing purposes.
  735.         lReturn = lineSetStatusMessages(g_hLine, 
  736.             LINEDEVSTATE_OTHER          |
  737.             LINEDEVSTATE_RINGING        |
  738.             LINEDEVSTATE_CONNECTED      |  // Important state!
  739.             LINEDEVSTATE_DISCONNECTED   |  // Important state!
  740.             LINEDEVSTATE_MSGWAITON      |
  741.             LINEDEVSTATE_MSGWAITOFF     |
  742.             LINEDEVSTATE_INSERVICE      |
  743.             LINEDEVSTATE_OUTOFSERVICE   |  // Important state!
  744.             LINEDEVSTATE_MAINTENANCE    |  // Important state!
  745.             LINEDEVSTATE_OPEN           |
  746.             LINEDEVSTATE_CLOSE          |
  747.             LINEDEVSTATE_NUMCALLS       |
  748.             LINEDEVSTATE_NUMCOMPLETIONS |
  749.             LINEDEVSTATE_TERMINALS      |
  750.             LINEDEVSTATE_ROAMMODE       |
  751.             LINEDEVSTATE_BATTERY        |
  752.             LINEDEVSTATE_SIGNAL         |
  753.             LINEDEVSTATE_DEVSPECIFIC    |
  754.             LINEDEVSTATE_REINIT         |  // Not allowed to disable this.
  755.             LINEDEVSTATE_LOCK           |
  756.             LINEDEVSTATE_CAPSCHANGE     |
  757.             LINEDEVSTATE_CONFIGCHANGE   |
  758.             LINEDEVSTATE_COMPLCANCEL    ,
  759.             LINEADDRESSSTATE_OTHER      |
  760.             LINEADDRESSSTATE_DEVSPECIFIC|
  761.             LINEADDRESSSTATE_INUSEZERO  |
  762.             LINEADDRESSSTATE_INUSEONE   |
  763.             LINEADDRESSSTATE_INUSEMANY  |
  764.             LINEADDRESSSTATE_NUMCALLS   |
  765.             LINEADDRESSSTATE_FORWARD    |
  766.             LINEADDRESSSTATE_TERMINALS  |
  767.             LINEADDRESSSTATE_CAPSCHANGE);
  768.         if (HandleLineErr(lReturn))
  769.             continue;
  770.         else
  771.         {
  772.             // If we do get an unhandled problem, we don't care.
  773.             // We just won't get notifications.
  774.             OutputDebugLineError(lReturn,
  775.                 "lineSetStatusMessages unhandled error: ");
  776.             break;
  777.         }
  778.     }
  779.     while(lReturn != SUCCESS);
  780.     // Get LineAddressStatus so we can make sure the line
  781.     // isn't already in use by a TAPI application.
  782.     lpLineAddressStatus = 
  783.         I_lineGetAddressStatus(lpLineAddressStatus, g_hLine, 0);
  784.         
  785.     if (lpLineAddressStatus == NULL)
  786.     {
  787.         HangupCall();
  788.         MessageBox(g_hDlgParentWindow,
  789.             "Error on Requested line",
  790.             "Unable to Use Line",MB_OK);
  791.         goto DeleteBuffers;
  792.     }
  793.     // MAKECALL will be set if there are any available call appearances
  794.     if ( ! ((lpLineAddressStatus -> dwAddressFeatures) &
  795.             LINEADDRFEATURE_MAKECALL) )
  796.     {
  797.         OutputDebugString("This line is not available to place a call.n");
  798.         HangupCall();
  799.         MessageBox(g_hDlgParentWindow,
  800.             "Requested line is already in use",
  801.             "Unable to Use Line",MB_OK);
  802.         goto DeleteBuffers;
  803.     }
  804.     // If the line was configured in the 'Dial' dialog, then
  805.     // we need to actually complete the configuration.
  806.     if (g_lpDeviceConfig)
  807.         lineSetDevConfig(g_dwDeviceID, g_lpDeviceConfig,
  808.             g_dwSizeDeviceConfig, "comm/datamodem");
  809.     // Start dialing the number
  810.     if (DialCallInParts(lpLineDevCaps, g_szDialableAddress,
  811.             g_szDisplayableAddress))
  812.     {
  813.         OutputDebugString("DialCallInParts succeeded.n");
  814.     }
  815.     else
  816.     {
  817.         OutputDebugString("DialCallInParts failed.n");
  818.         HangupCall();
  819.         goto DeleteBuffers;
  820.     }
  821. DeleteBuffers:
  822.     if (lpLineAddressStatus)
  823.         LocalFree(lpLineAddressStatus);
  824.     if (lpLineDevCaps)
  825.         LocalFree(lpLineDevCaps);
  826.     if (g_bTapiInUse)
  827.         EnableHangupCall(g_hWndMainWindow, TRUE);
  828.     return g_bTapiInUse;
  829. }
  830. //**************************************************
  831. // These APIs are specific to this module 
  832. //**************************************************
  833. //
  834. //  FUNCTION: DialCallInParts(LPLINEDEVCAPS, LPCSTR, LPCSTR)
  835. //
  836. //  PURPOSE: Dials the call, handling special characters.
  837. //
  838. //  PARAMETERS:
  839. //    lpLineDevCaps - LINEDEVCAPS for the line to be used.
  840. //    lpszAddress   - Address to Dial.
  841. //    lpszDisplayableAddress - Displayable Address.
  842. //
  843. //  RETURN VALUE:
  844. //    Returns TRUE if we successfully Dial.
  845. //
  846. //  COMMENTS:
  847. //
  848. //    This function dials the Address and handles any
  849. //    special characters in the address that the service provider
  850. //    can't handle.  It requires input from the user to handle
  851. //    these characters; this can cause problems for fully automated
  852. //    dialing.
  853. //
  854. //    Note that we can return TRUE, even if we don't reach a
  855. //    CONNECTED state.  DIalCallInParts returns as soon as the
  856. //    Address is fully dialed or when an error occurs.
  857. //
  858. //
  859. BOOL DialCallInParts(LPLINEDEVCAPS lpLineDevCaps,
  860.     LPCSTR lpszAddress, LPCSTR lpszDisplayableAddress)
  861. {
  862.     LPLINECALLPARAMS  lpCallParams = NULL;
  863.     LPLINEADDRESSCAPS lpAddressCaps = NULL;
  864.     LPLINECALLSTATUS  lpLineCallStatus = NULL;
  865.     long lReturn;
  866.     int i;
  867.     DWORD dwDevCapFlags;
  868.     char szFilter[1+sizeof(g_sNonDialable)] = "";
  869.     BOOL bFirstDial = TRUE;
  870.                                
  871.     // Variables to handle Dialable Substring dialing.
  872.     LPSTR lpDS; // This is just so we can free lpszDialableSubstring later.
  873.     LPSTR lpszDialableSubstring;
  874.     int nAddressLength = 0;
  875.     int nCurrentAddress = 0;
  876.     char chUnhandledCharacter;
  877.     // Get the capabilities for the line device we're going to use.
  878.     lpAddressCaps = I_lineGetAddressCaps(lpAddressCaps,
  879.         g_dwDeviceID, 0, g_dwAPIVersion, 0);
  880.     if (lpAddressCaps == NULL)
  881.         return FALSE;
  882.     // Setup our CallParams for DATAMODEM settings.
  883.     lpCallParams = CreateCallParams (lpCallParams, lpszDisplayableAddress);
  884.     if (lpCallParams == NULL)
  885.         return FALSE;
  886.     // Determine which special characters the service provider
  887.     // does *not* handle so we can handle them manually.
  888.     // Keep list of unhandled characters in szFilter.
  889.     
  890.     dwDevCapFlags = lpLineDevCaps -> dwDevCapFlags;  // SP handled characters.
  891.     for (i = 0; i < g_sizeofNonDialable ; i++)
  892.     {
  893.         if ((dwDevCapFlags & g_sNonDialable[i].dwDevCapFlag) == 0)
  894.         {
  895.             strcat(szFilter, g_sNonDialable[i].szToken);
  896.         }
  897.     }
  898.     // szFilter now contains the set of tokens which delimit dialable substrings
  899.     
  900.     // Setup the strings for substring dialing.
  901.                            
  902.     nAddressLength = strlen(lpszAddress);
  903.     lpDS = lpszDialableSubstring = (LPSTR) LocalAlloc(LPTR, nAddressLength + 1);
  904.     if (lpszDialableSubstring == NULL)
  905.     {
  906.         OutputDebugLastError(GetLastError(), "LocalAlloc failed: ");
  907.         HandleNoMem();
  908.         goto errExit;
  909.     }
  910.     // Lets start dialing substrings!
  911.     while (nCurrentAddress < nAddressLength)
  912.     {
  913.   retryAfterError:
  914.         // Find the next undialable character
  915.         i = strcspn(&lpszAddress[nCurrentAddress], szFilter);
  916.         // Was there one before the end of the Address string?
  917.         if (i + nCurrentAddress < nAddressLength)
  918.         {
  919.             // Make sure this device can handle partial dial.
  920.             if (! (lpAddressCaps -> dwAddrCapFlags & 
  921.                    LINEADDRCAPFLAGS_PARTIALDIAL))
  922.             {
  923.                 MessageBox(g_hDlgParentWindow,
  924.                     "This line doesn't support partial dialing.n",
  925.                     "Warning",MB_OK);
  926.                 goto errExit;
  927.             }
  928.             // Remember what the unhandled character is so we can handle it.
  929.             chUnhandledCharacter = lpszAddress[nCurrentAddress+i];
  930.             
  931.             // Copy the dialable string to the Substring.
  932.             memcpy(lpszDialableSubstring, &lpszAddress[nCurrentAddress], i);
  933.             // Terminate the substring with a ';' to signify the partial dial.
  934.             lpszDialableSubstring[i] = ';';
  935.             lpszDialableSubstring[i+1] = '';
  936.             
  937.             // Increment the address for next iteration.
  938.             nCurrentAddress += i + 1;
  939.         }
  940.         else // No more partial dials.  Dial the rest of the Address.
  941.         {
  942.             lpszDialableSubstring = (LPSTR) &lpszAddress[nCurrentAddress];
  943.             chUnhandledCharacter = 0;
  944.             nCurrentAddress = nAddressLength;
  945.         }
  946.         
  947.         do
  948.         {                   
  949.             if (bFirstDial)
  950.                 lReturn = WaitForReply( 
  951.                     lineMakeCall(g_hLine, &g_hCall, lpszDialableSubstring,
  952.                         0, lpCallParams) );
  953.             else
  954.                 lReturn = WaitForReply( 
  955.                     lineDial(g_hCall, lpszDialableSubstring, 0) );
  956.             switch(lReturn)
  957.             {
  958.                 // We should not have received these errors because of the
  959.                 // prefiltering strategy, but there may be some ill-behaved
  960.                 // service providers which do not correctly set their
  961.                 // devcapflags.  Add the character corresponding to the error
  962.                 // to the filter set and retry dialing.
  963.                 //
  964.                 case LINEERR_DIALBILLING:
  965.                 case LINEERR_DIALDIALTONE:
  966.                 case LINEERR_DIALQUIET:
  967.                 case LINEERR_DIALPROMPT:
  968.                 {
  969.                     OutputDebugString("Service Provider incorrectly sets dwDevCapFlagsn");
  970.                     for (i = 0; i < g_sizeofNonDialable; i++)
  971.                         if (lReturn == g_sNonDialable[i].lError)
  972.                         {
  973.                             strcat(szFilter, g_sNonDialable[i].szToken);
  974.                         }
  975.                     goto retryAfterError;
  976.                 }
  977.                 case WAITERR_WAITABORTED:
  978.                     OutputDebugString("While Dialing, WaitForReply aborted.n");
  979.                     goto errExit;
  980.             
  981.             }
  982.             if (HandleLineErr(lReturn))
  983.                 continue;
  984.             else
  985.             {
  986.                 if (bFirstDial)
  987.                     OutputDebugLineError(lReturn, "lineMakeCall unhandled error: ");
  988.                 else
  989.                     OutputDebugLineError(lReturn, "lineDial unhandled error: ");
  990.                 goto errExit;
  991.             }
  992.         }
  993.         while (lReturn != SUCCESS);
  994.         
  995.         bFirstDial = FALSE;
  996.                                 
  997.         // The dial was successful; now handle characters the service
  998.         // provider didn't (if any).
  999.         if (chUnhandledCharacter)
  1000.         {
  1001.             LPSTR lpMsg = "";
  1002.             // First, wait until we know we can continue dialing.  While the
  1003.             // last string is still pending to be dialed, we can't dial another.
  1004.             while(TRUE)
  1005.             {
  1006.                 lpLineCallStatus = I_lineGetCallStatus(lpLineCallStatus, g_hCall);
  1007.                 if (lpLineCallStatus == NULL)
  1008.                     goto errExit;
  1009.                 // Does CallStatus say we can dial now?
  1010.                 if ((lpLineCallStatus->dwCallFeatures) & LINECALLFEATURE_DIAL)
  1011.                 {
  1012.                     OutputDebugString("Ok to continue dialing.n");
  1013.                     break;
  1014.                 }
  1015.                 
  1016.                 // We can't dial yet, so wait for a CALLSTATE message
  1017.                 OutputDebugString("Waiting for dialing to be enabled.n");
  1018.                 if (WaitForCallState(I_LINECALLSTATE_ANY) != SUCCESS)
  1019.                     goto errExit;
  1020.             }
  1021.             for (i = 0; i < g_sizeofNonDialable; i++)
  1022.                 if (chUnhandledCharacter == g_sNonDialable[i].szToken[0])
  1023.                     lpMsg = g_sNonDialable[i].szMsg;
  1024.                     
  1025.             MessageBox(g_hDlgParentWindow, lpMsg, "Dialing Paused", MB_OK);
  1026.         }
  1027.         
  1028.     } // continue dialing until we dial all Dialable Substrings.
  1029.     LocalFree(lpCallParams);
  1030.     LocalFree(lpDS);
  1031.     LocalFree(lpAddressCaps);
  1032.     if (lpLineCallStatus)
  1033.         LocalFree(lpLineCallStatus);
  1034.     
  1035.     return TRUE;
  1036.     
  1037.   errExit:
  1038.         // if lineMakeCall has already been successfully called, there's a call in progress. 
  1039.         // let the invoking routine shut down the call.
  1040.         // if the invoker did not clean up the call, it should be done here.
  1041.     if (lpLineCallStatus)
  1042.         LocalFree(lpLineCallStatus);
  1043.     if (lpDS)
  1044.         LocalFree(lpDS);
  1045.     if (lpCallParams)
  1046.         LocalFree(lpCallParams);
  1047.     if (lpAddressCaps)
  1048.         LocalFree(lpAddressCaps);
  1049.     return FALSE;
  1050. }   
  1051. //
  1052. //  FUNCTION: CreateCallParams(LPLINECALLPARAMS, LPCSTR)
  1053. //
  1054. //  PURPOSE: Allocates and fills a LINECALLPARAMS structure
  1055. //
  1056. //  PARAMETERS:
  1057. //    lpCallParams - 
  1058. //    lpszDisplayableAddress - 
  1059. //
  1060. //  RETURN VALUE:
  1061. //    Returns a LPLINECALLPARAMS ready to use for dialing DATAMODEM calls.
  1062. //    Returns NULL if unable to allocate the structure.
  1063. //
  1064. //  COMMENTS:
  1065. //
  1066. //    If a non-NULL lpCallParams is passed in, it must have been allocated
  1067. //    with LocalAlloc, and can potentially be freed and reallocated.  It must
  1068. //    also have the dwTotalSize field correctly set.
  1069. //
  1070. //
  1071. LPLINECALLPARAMS CreateCallParams (
  1072.     LPLINECALLPARAMS lpCallParams, LPCSTR lpszDisplayableAddress)
  1073. {
  1074.     size_t sizeDisplayableAddress;
  1075.     if (lpszDisplayableAddress == NULL)
  1076.         lpszDisplayableAddress = "";
  1077.         
  1078.     sizeDisplayableAddress = strlen(lpszDisplayableAddress) + 1;
  1079.                           
  1080.     lpCallParams = (LPLINECALLPARAMS) CheckAndReAllocBuffer(
  1081.         (LPVOID) lpCallParams, 
  1082.         sizeof(LINECALLPARAMS) + sizeDisplayableAddress,
  1083.         "CreateCallParams: ");
  1084.     if (lpCallParams == NULL)
  1085.         return NULL;
  1086.     
  1087.     // This is where we configure the line for DATAMODEM usage.
  1088.     lpCallParams -> dwBearerMode = LINEBEARERMODE_VOICE;
  1089.     lpCallParams -> dwMediaMode  = LINEMEDIAMODE_DATAMODEM;
  1090.     // This specifies that we want to use only IDLE calls and
  1091.     // don't want to cut into a call that might not be IDLE (ie, in use).
  1092.     lpCallParams -> dwCallParamFlags = LINECALLPARAMFLAGS_IDLE;
  1093.                                     
  1094.     // if there are multiple addresses on line, use first anyway.
  1095.     // It will take a more complex application than a simple tty app
  1096.     // to use multiple addresses on a line anyway.
  1097.     lpCallParams -> dwAddressMode = LINEADDRESSMODE_ADDRESSID;
  1098.     lpCallParams -> dwAddressID = 0;
  1099.     // Since we don't know where we originated, leave these blank.
  1100.     lpCallParams -> dwOrigAddressSize = 0;
  1101.     lpCallParams -> dwOrigAddressOffset = 0;
  1102.     
  1103.     // Unimodem ignores these values.
  1104.     (lpCallParams -> DialParams) . dwDialSpeed = 0;
  1105.     (lpCallParams -> DialParams) . dwDigitDuration = 0;
  1106.     (lpCallParams -> DialParams) . dwDialPause = 0;
  1107.     (lpCallParams -> DialParams) . dwWaitForDialtone = 0;
  1108.     
  1109.     // Address we are dialing.
  1110.     lpCallParams -> dwDisplayableAddressOffset = sizeof(LINECALLPARAMS);
  1111.     lpCallParams -> dwDisplayableAddressSize = sizeDisplayableAddress;
  1112.     strcpy((LPSTR)lpCallParams + sizeof(LINECALLPARAMS),
  1113.            lpszDisplayableAddress);
  1114.     return lpCallParams;
  1115. }
  1116. //
  1117. //  FUNCTION: long WaitForReply(long)
  1118. //
  1119. //  PURPOSE: Resynchronize by waiting for a LINE_REPLY 
  1120. //
  1121. //  PARAMETERS:
  1122. //    lRequestID - The asynchronous request ID that we're
  1123. //                 on a LINE_REPLY for.
  1124. //
  1125. //  RETURN VALUE:
  1126. //    - 0 if LINE_REPLY responded with a success.
  1127. //    - LINEERR constant if LINE_REPLY responded with a LINEERR
  1128. //    - 1 if the line was shut down before LINE_REPLY is received.
  1129. //
  1130. //  COMMENTS:
  1131. //
  1132. //    This function allows us to resynchronize an asynchronous
  1133. //    TAPI line call by waiting for the LINE_REPLY message.  It
  1134. //    waits until a LINE_REPLY is received or the line is shut down.
  1135. //
  1136. //    Note that this could cause re-entrancy problems as
  1137. //    well as mess with any message preprocessing that might
  1138. //    occur on this thread (such as TranslateAccelerator).
  1139. //
  1140. //    This function should to be called from the thread that did
  1141. //    lineInitialize, or the PeekMessage is on the wrong thread
  1142. //    and the synchronization is not guaranteed to work.  Also note
  1143. //    that if another PeekMessage loop is entered while waiting,
  1144. //    this could also cause synchronization problems.
  1145. //
  1146. //    One more note.  This function can potentially be re-entered
  1147. //    if the call is dropped for any reason while waiting.  If this
  1148. //    happens, just drop out and assume the wait has been canceled.  
  1149. //    This is signaled by setting bReentered to FALSE when the function 
  1150. //    is entered and TRUE when it is left.  If bReentered is ever TRUE 
  1151. //    during the function, then the function was re-entered.
  1152. //
  1153. //    This function times out and returns WAITERR_WAITTIMEDOUT
  1154. //    after WAITTIMEOUT milliseconds have elapsed.
  1155. //
  1156. //
  1157. long WaitForReply (long lRequestID)
  1158. {
  1159.     static BOOL bReentered;
  1160.     bReentered = FALSE;
  1161.     if (lRequestID > SUCCESS)
  1162.     {
  1163.         MSG msg; 
  1164.         DWORD dwTimeStarted;
  1165.         g_bReplyRecieved = FALSE;
  1166.         g_dwRequestedID = (DWORD) lRequestID;
  1167.         // Initializing this just in case there is a bug
  1168.         // that sets g_bReplyRecieved without setting the reply value.
  1169.         g_lAsyncReply = LINEERR_OPERATIONFAILED;
  1170.         dwTimeStarted = GetTickCount();
  1171.         while(!g_bReplyRecieved)
  1172.         {
  1173.             if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
  1174.             {
  1175.                 TranslateMessage(&msg);
  1176.                 DispatchMessage(&msg);
  1177.             }
  1178.             // This should only occur if the line is shut down while waiting.
  1179.             if (!g_bTapiInUse || bReentered)
  1180.             {
  1181.                 bReentered = TRUE;
  1182.                 return WAITERR_WAITABORTED;
  1183.             }
  1184.             // Its a really bad idea to timeout a wait for a LINE_REPLY.
  1185.             // If we are execting a LINE_REPLY, we should wait till we get
  1186.             // it; it might take a long time to dial (for example).
  1187.             // If 5 seconds go by without a reply, it might be a good idea
  1188.             // to display a dialog box to tell the user that a
  1189.             // wait is in progress and to give the user the capability to
  1190.             // abort the wait.
  1191.         }
  1192.         bReentered = TRUE;
  1193.         return g_lAsyncReply;
  1194.     }
  1195.     bReentered = TRUE;
  1196.     return lRequestID;
  1197. }
  1198. //
  1199. //  FUNCTION: long WaitForCallState(DWORD)
  1200. //
  1201. //  PURPOSE: Wait for the line to reach a specific CallState.
  1202. //
  1203. //  PARAMETERS:
  1204. //    dwDesiredCallState - specific CallState to wait for.
  1205. //
  1206. //  RETURN VALUE:
  1207. //    Returns 0 (SUCCESS) when we reach the Desired CallState.
  1208. //    Returns WAITERR_WAITTIMEDOUT if timed out.
  1209. //    Returns WAITERR_WAITABORTED if call was closed while waiting.
  1210. //
  1211. //  COMMENTS:
  1212. //
  1213. //    This function allows us to synchronously wait for a line
  1214. //    to reach a specific LINESTATE or until the line is shut down.
  1215. //
  1216. //    Note that this could cause re-entrancy problems as
  1217. //    well as mess with any message preprocessing that might
  1218. //    occur on this thread (such as TranslateAccelerator).
  1219. //
  1220. //    One more note.  This function can potentially be re-entered
  1221. //    if the call is dropped for any reason while waiting.  If this
  1222. //    happens, just drop out and assume the wait has been canceled.  
  1223. //    This is signaled by setting bReentered to FALSE when the function 
  1224. //    is entered and TRUE when it is left.  If bReentered is ever TRUE 
  1225. //    during the function, then the function was re-entered.
  1226. //
  1227. //    This function should to be called from the thread that did
  1228. //    lineInitialize, or the PeekMessage is on the wrong thread
  1229. //    and the synchronization is not guaranteed to work.  Also note
  1230. //    that if another PeekMessage loop is entered while waiting,
  1231. //    this could also cause synchronization problems.
  1232. //
  1233. //    If the constant value I_LINECALLSTATE_ANY is used for the 
  1234. //    dwDesiredCallState, then WaitForCallState will return SUCCESS
  1235. //    upon receiving any CALLSTATE messages.
  1236. //    
  1237. //
  1238. //
  1239. long WaitForCallState(DWORD dwDesiredCallState)
  1240. {
  1241.     MSG msg;
  1242.     DWORD dwTimeStarted;
  1243.     static BOOL bReentered;
  1244.     bReentered = FALSE;
  1245.     dwTimeStarted = GetTickCount();
  1246.     g_bCallStateReceived = FALSE;
  1247.     while ((dwDesiredCallState == I_LINECALLSTATE_ANY) || 
  1248.            (g_dwCallState != dwDesiredCallState))
  1249.     {
  1250.         if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
  1251.         {
  1252.             TranslateMessage(&msg);
  1253.             DispatchMessage(&msg);
  1254.         }
  1255.         // If we are waiting for any call state and get one, succeed.
  1256.         if ((dwDesiredCallState == I_LINECALLSTATE_ANY) && 
  1257.             g_bCallStateReceived)
  1258.         {
  1259.             break;
  1260.         }
  1261.         // This should only occur if the line is shut down while waiting.
  1262.         if (!g_bTapiInUse || bReentered)
  1263.         {
  1264.             bReentered = TRUE;
  1265.             OutputDebugString("WAITABORTEDn");
  1266.             return WAITERR_WAITABORTED;
  1267.         }
  1268.         // If we don't get the reply in a reasonable time, we time out.
  1269.         if (GetTickCount() - dwTimeStarted > WAITTIMEOUT)
  1270.         {
  1271.             bReentered = TRUE;
  1272.             OutputDebugString("WAITTIMEDOUTn");
  1273.             return WAITERR_WAITTIMEDOUT;
  1274.         }
  1275.     }
  1276.     bReentered = TRUE;
  1277.     return SUCCESS;
  1278. }
  1279. //**************************************************
  1280. // lineCallback Function and Handlers.
  1281. //**************************************************
  1282. //
  1283. //  FUNCTION: lineCallbackFunc(..)
  1284. //
  1285. //  PURPOSE: Receive asynchronous TAPI events
  1286. //
  1287. //  PARAMETERS:
  1288. //    dwDevice  - Device associated with the event, if any
  1289. //    dwMsg     - TAPI event that occurred.
  1290. //    dwCallbackInstance - User defined data supplied when opening the line.
  1291. //    dwParam1  - dwMsg specific information
  1292. //    dwParam2  - dwMsg specific information
  1293. //    dwParam3  - dwMsg specific information
  1294. //
  1295. //  RETURN VALUE:
  1296. //    none
  1297. //
  1298. //  COMMENTS:
  1299. //    This is the function where all asynchronous events will come.
  1300. //    Almost all events will be specific to an open line, but a few
  1301. //    will be general TAPI events (such as LINE_REINIT).
  1302. //
  1303. //    Its important to note that this callback will *ALWAYS* be
  1304. //    called in the context of the thread that does the lineInitialize.
  1305. //    Even if another thread (such as the COMM threads) calls the API
  1306. //    that would result in the callback being called, it will be called
  1307. //    in the context of the main thread (since in this sample, the main
  1308. //    thread does the lineInitialize).
  1309. //
  1310. //
  1311. void CALLBACK lineCallbackFunc(
  1312.     DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance, 
  1313.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  1314. {
  1315.     OutputDebugLineCallback(
  1316.         dwDevice, dwMsg, dwCallbackInstance, 
  1317.         dwParam1, dwParam2, dwParam3);
  1318.     // All we do is dispatch the dwMsg to the correct handler.
  1319.     switch(dwMsg)
  1320.     {
  1321.         case LINE_CALLSTATE:
  1322.             DoLineCallState(dwDevice, dwMsg, dwCallbackInstance,
  1323.                 dwParam1, dwParam2, dwParam3);
  1324.             break;
  1325.         case LINE_CLOSE:
  1326.             DoLineClose(dwDevice, dwMsg, dwCallbackInstance,
  1327.                 dwParam1, dwParam2, dwParam3);
  1328.             break;
  1329.         case LINE_LINEDEVSTATE:
  1330.             DoLineDevState(dwDevice, dwMsg, dwCallbackInstance,
  1331.                 dwParam1, dwParam2, dwParam3);
  1332.             break;
  1333.         case LINE_REPLY:
  1334.             DoLineReply(dwDevice, dwMsg, dwCallbackInstance,
  1335.                 dwParam1, dwParam2, dwParam3);
  1336.             break;
  1337.         case LINE_CREATE:
  1338.             DoLineCreate(dwDevice, dwMsg, dwCallbackInstance,
  1339.                 dwParam1, dwParam2, dwParam3);
  1340.             break;
  1341.         default:
  1342.             OutputDebugString("lineCallbackFunc message ignoredn");
  1343.             break;
  1344.     }
  1345.     return;
  1346. }
  1347. //
  1348. //  FUNCTION: DoLineReply(..)
  1349. //
  1350. //  PURPOSE: Handle LINE_REPLY asynchronous messages.
  1351. //
  1352. //  PARAMETERS:
  1353. //    dwDevice  - Line Handle associated with this LINE_REPLY.
  1354. //    dwMsg     - Should always be LINE_REPLY.
  1355. //    dwCallbackInstance - Unused by this sample.
  1356. //    dwParam1  - Asynchronous request ID.
  1357. //    dwParam2  - success or LINEERR error value.
  1358. //    dwParam3  - Unused.
  1359. //
  1360. //  RETURN VALUE:
  1361. //    none
  1362. //
  1363. //  COMMENTS:
  1364. //
  1365. //    All line API calls that return an asynchronous request ID
  1366. //    will eventually cause a LINE_REPLY message.  Handle it.
  1367. //
  1368. //    This sample assumes only one call at time, and that we wait
  1369. //    for a LINE_REPLY before making any other line API calls.
  1370. //
  1371. //    The only exception to the above is that we might shut down
  1372. //    the line before receiving a LINE_REPLY.
  1373. //
  1374. //
  1375. void DoLineReply(
  1376.     DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
  1377.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  1378. {
  1379.     if ((long) dwParam2 != SUCCESS)
  1380.         OutputDebugLineError((long) dwParam2, "LINE_REPLY error: ");
  1381.     else
  1382.         OutputDebugString("LINE_REPLY: successfully replied.n");
  1383.     // If we are currently waiting for this async Request ID
  1384.     // then set the global variables to acknowledge it.
  1385.     if (g_dwRequestedID == dwParam1)
  1386.     {
  1387.         g_bReplyRecieved = TRUE;
  1388.         g_lAsyncReply = (long) dwParam2;
  1389.     }
  1390. }
  1391. //
  1392. //  FUNCTION: DoLineClose(..)
  1393. //
  1394. //  PURPOSE: Handle LINE_CLOSE asynchronous messages.
  1395. //
  1396. //  PARAMETERS:
  1397. //    dwDevice  - Line Handle that was closed.
  1398. //    dwMsg     - Should always be LINE_CLOSE.
  1399. //    dwCallbackInstance - Unused by this sample.
  1400. //    dwParam1  - Unused.
  1401. //    dwParam2  - Unused.
  1402. //    dwParam3  - Unused.
  1403. //
  1404. //  RETURN VALUE:
  1405. //    none
  1406. //
  1407. //  COMMENTS:
  1408. //
  1409. //    This message is sent when something outside our app shuts
  1410. //    down a line in use.
  1411. //
  1412. //    The hLine (and any hCall on this line) are no longer valid.
  1413. //
  1414. //
  1415. void DoLineClose(
  1416.     DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
  1417.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  1418. {
  1419.     // Line has been shut down.  Clean up our internal variables.
  1420.     g_hLine = NULL;
  1421.     g_hCall = NULL;
  1422.     UpdateStatusBar("Call was shut down.",1,0);
  1423.     MessageBox(g_hDlgParentWindow,
  1424.         "Call was shut down.","Warning",MB_OK);
  1425.     HangupCall();
  1426. }
  1427. //
  1428. //  FUNCTION: DoLineDevState(..)
  1429. //
  1430. //  PURPOSE: Handle LINE_LINEDEVSTATE asynchronous messages.
  1431. //
  1432. //  PARAMETERS:
  1433. //    dwDevice  - Line Handle that was closed.
  1434. //    dwMsg     - Should always be LINE_LINEDEVSTATE.
  1435. //    dwCallbackInstance - Unused by this sample.
  1436. //    dwParam1  - LINEDEVSTATE constant.
  1437. //    dwParam2  - Depends on dwParam1.
  1438. //    dwParam3  - Depends on dwParam1.
  1439. //
  1440. //  RETURN VALUE:
  1441. //    none
  1442. //
  1443. //  COMMENTS:
  1444. //
  1445. //    The LINE_LINEDEVSTATE message is received if the state of the line
  1446. //    changes.  Examples are RINGING, MAINTENANCE, MSGWAITON.  Very few of
  1447. //    these are relevant to this sample.
  1448. //
  1449. //    Assuming that any LINEDEVSTATE that removes the line from use by TAPI
  1450. //    will also send a LINE_CLOSE message.
  1451. //
  1452. //
  1453. void DoLineDevState(
  1454.     DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
  1455.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  1456. {
  1457.     switch(dwParam1)
  1458.     {
  1459.         case LINEDEVSTATE_RINGING:
  1460.             UpdateStatusBar("Line Ringing",1,0);
  1461.             OutputDebugString("Line Ringing.n");
  1462.             break;
  1463.         case LINEDEVSTATE_REINIT:
  1464.         // This is an important case!  Usually means that a service provider
  1465.         // has changed in such a way that requires TAPI to REINIT.  
  1466.         // Note that there are both 'soft' REINITs and 'hard' REINITs.
  1467.         // Soft REINITs don't actually require a full shutdown but is instead
  1468.         // just an informational change that historically required a REINIT
  1469.         // to force the application to deal with.  TAPI API Version 1.3 apps
  1470.         // will still need to do a full REINIT for both hard and soft REINITs.
  1471.             switch(dwParam2)
  1472.             {
  1473.                 // This is the hard REINIT.  No reason given, just REINIT.
  1474.                 // TAPI is waiting for everyone to shutdown.
  1475.                 // Our response is to immediately shutdown any calls,
  1476.                 // shutdown our use of TAPI and notify the user.
  1477.                 case 0:
  1478.                     ShutdownTAPI();
  1479.                     WarningBox("Tapi line configuration has changed.");
  1480.                     break;
  1481.                 case LINE_CREATE:
  1482.                     OutputDebugString("Soft REINIT: LINE_CREATE.n");
  1483.                     DoLineCreate(dwDevice, dwParam2, dwCallbackInstance,
  1484.                         dwParam3, 0, 0);
  1485.                     break;
  1486.                 case LINE_LINEDEVSTATE:
  1487.                     OutputDebugString("Soft REINIT: LINE_LINEDEVSTATE.n");
  1488.                     DoLineDevState(dwDevice, dwParam2, dwCallbackInstance,
  1489.                         dwParam3, 0, 0);
  1490.                     break;
  1491.                 // There might be other reasons to send a soft reinit.
  1492.                 // No need to to shutdown for these.
  1493.                 default:
  1494.                     OutputDebugString("Ignoring soft REINITn");
  1495.                     break;
  1496.             }
  1497.             break;
  1498.         case LINEDEVSTATE_OUTOFSERVICE:
  1499.             WarningBox("Line selected is now Out of Service.");
  1500.             HangupCall();
  1501.             break;
  1502.         case LINEDEVSTATE_DISCONNECTED:
  1503.             WarningBox("Line selected is now disconnected.");
  1504.             HangupCall();
  1505.             break;
  1506.         case LINEDEVSTATE_MAINTENANCE:
  1507.             WarningBox("Line selected is now out for maintenance.");
  1508.             HangupCall();
  1509.             break;
  1510.         case LINEDEVSTATE_TRANSLATECHANGE:
  1511.             if (g_hDialog)
  1512.                 PostMessage(g_hDialog, WM_COMMAND, IDC_CONFIGURATIONCHANGED, 0);
  1513.             break;
  1514.         case LINEDEVSTATE_REMOVED:
  1515.             OutputDebugString("A Line device has been removed;"
  1516.                 " no action taken.n");
  1517.             break;
  1518.         default:
  1519.             OutputDebugString("Unhandled LINEDEVSTATE messagen");
  1520.     }
  1521. }
  1522. //
  1523. //  FUNCTION: DoLineCreate(..)
  1524. //
  1525. //  PURPOSE: Handle LINE_LINECREATE asynchronous messages.
  1526. //
  1527. //  PARAMETERS:
  1528. //    dwDevice  - Unused.
  1529. //    dwMsg     - Should always be LINE_CREATE.
  1530. //    dwCallbackInstance - Unused.
  1531. //    dwParam1  - dwDeviceID of new Line created.
  1532. //    dwParam2  - Unused.
  1533. //    dwParam3  - Unused.
  1534. //
  1535. //  RETURN VALUE:
  1536. //    none
  1537. //
  1538. //  COMMENTS:
  1539. //
  1540. //    This message is new for Windows 95.  It is sent when a new line is
  1541. //    added to the system.  This allows us to handle new lines without having
  1542. //    to REINIT.  This allows for much more graceful Plug and Play.
  1543. //
  1544. //    This sample just changes the number of devices available and can use
  1545. //    it next time a call is made.  It also tells the "Dial" dialog.
  1546. //
  1547. //
  1548. void DoLineCreate(
  1549.     DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
  1550.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  1551. {
  1552.     // dwParam1 is the Device ID of the new line.  
  1553.     // Add one to get the number of total devices.
  1554.     if (g_dwNumDevs <= dwParam1)
  1555.         g_dwNumDevs = dwParam1+1;
  1556.     if (g_hDialog)
  1557.         PostMessage(g_hDialog, WM_COMMAND, IDC_LINECREATE, 0);
  1558. }
  1559. //
  1560. //  FUNCTION: DoLineCallState(..)
  1561. //
  1562. //  PURPOSE: Handle LINE_CALLSTATE asynchronous messages.
  1563. //
  1564. //  PARAMETERS:
  1565. //    dwDevice  - Handle to Call who's state is changing.
  1566. //    dwMsg     - Should always be LINE_CALLSTATE.
  1567. //    dwCallbackInstance - Unused by this sample.
  1568. //    dwParam1  - LINECALLSTATE constant specifying state change.
  1569. //    dwParam2  - Specific to dwParam1.
  1570. //    dwParam3  - LINECALLPRIVILEGE change, if any.
  1571. //
  1572. //  RETURN VALUE:
  1573. //    none
  1574. //
  1575. //  COMMENTS:
  1576. //
  1577. //    This message is received whenever a call changes state.  Lots of
  1578. //    things we do, ranging from notifying the user to closing the line
  1579. //    to actually connecting to the target of our phone call.
  1580. //
  1581. //    What we do is usually obvious based on the call state change.
  1582. //
  1583. void DoLineCallState(
  1584.     DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
  1585.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  1586. {
  1587.     // Error if this CALLSTATE doesn't apply to our call in progress.
  1588.     if ((HCALL) dwDevice != g_hCall)
  1589.     {
  1590.         OutputDebugPrintf("LINE_CALLSTATE: Unknown device ID '0x%lx'.",
  1591.             dwDevice);
  1592.         return;
  1593.     }
  1594.     // This sets the global g_dwCallState variable so if we are waiting
  1595.     // for a specific call state change, we will know when it happens.
  1596.     g_dwCallState = dwParam1;
  1597.     g_bCallStateReceived = TRUE;
  1598.     // dwParam3 contains changes to LINECALLPRIVILEGE, if there are any.
  1599.     switch (dwParam3)
  1600.     {
  1601.         case 0:
  1602.             break; // no change to call state
  1603.          // close line if we are made monitor.  Shouldn't happen!
  1604.          case LINECALLPRIVILEGE_MONITOR:
  1605.             OutputDebugString("line given monitor privilege; closingn");
  1606.             HangupCall();
  1607.             return;
  1608.          // close line if we are made owner.  Shouldn't happen!
  1609.         case LINECALLPRIVILEGE_OWNER:
  1610.             OutputDebugString("line given owner privilege; closingn");
  1611.             HangupCall();
  1612.             break;
  1613.         default: // Shouldn't happen!  All cases handled.
  1614.             OutputDebugString("Unknown LINECALLPRIVILEGE message: closingn");
  1615.             HangupCall();
  1616.             return;
  1617.     }
  1618.     // dwParam1 is the specific CALLSTATE change that is occurring.
  1619.     switch (dwParam1)
  1620.     {
  1621.         case LINECALLSTATE_DIALTONE:
  1622.             UpdateStatusBar("Dial Tone",1,0);
  1623.             OutputDebugString("Dial Tonen");
  1624.             break;
  1625.         case LINECALLSTATE_DIALING:
  1626.             UpdateStatusBar("Dialing Call",1,0);
  1627.             OutputDebugString("Dialingn");
  1628.             break;
  1629.         case LINECALLSTATE_PROCEEDING:
  1630.             UpdateStatusBar("Call is Proceeding",1,0);
  1631.             OutputDebugString("Proceedingn");
  1632.             break;
  1633.         case LINECALLSTATE_RINGBACK:
  1634.             UpdateStatusBar("RingBack",1,0);
  1635.             OutputDebugString("RingBackn");
  1636.             break;
  1637.         case LINECALLSTATE_BUSY:
  1638.             UpdateStatusBar("Line is busy",1,0);
  1639.             OutputDebugString("Line busy, shutting downn");
  1640.             HangupCall();
  1641.             break;
  1642.         case LINECALLSTATE_IDLE:
  1643.             UpdateStatusBar("Line is idle",1,0);
  1644.             OutputDebugString("Line idlen");
  1645.             HangupCall();
  1646.             break;
  1647.         case LINECALLSTATE_SPECIALINFO:
  1648.             UpdateStatusBar(
  1649.                 "Special Info, probably couldn't dial number",1,0);
  1650.             OutputDebugString(
  1651.                 "Special Info, probably couldn't dial numbern");
  1652.             HangupCall();
  1653.             break;
  1654.         case LINECALLSTATE_DISCONNECTED:
  1655.         {
  1656.             LPSTR pszReasonDisconnected;
  1657.             switch (dwParam2)
  1658.             {
  1659.                 case LINEDISCONNECTMODE_NORMAL:
  1660.                     pszReasonDisconnected = "Remote Party Disconnected";
  1661.                     break;
  1662.                 case LINEDISCONNECTMODE_UNKNOWN:
  1663.                     pszReasonDisconnected = "Disconnected: Unknown reason";
  1664.                     break;
  1665.                 case LINEDISCONNECTMODE_REJECT:
  1666.                     pszReasonDisconnected = "Remote Party rejected call";
  1667.                     break;
  1668.                 case LINEDISCONNECTMODE_PICKUP:
  1669.                     pszReasonDisconnected = 
  1670.                         "Disconnected: Local phone picked up";
  1671.                     break;
  1672.                 case LINEDISCONNECTMODE_FORWARDED:
  1673.                     pszReasonDisconnected = "Disconnected: Forwarded";
  1674.                     break;
  1675.                 case LINEDISCONNECTMODE_BUSY:
  1676.                     pszReasonDisconnected = "Disconnected: Busy";
  1677.                     break;
  1678.                 case LINEDISCONNECTMODE_NOANSWER:
  1679.                     pszReasonDisconnected = "Disconnected: No Answer";
  1680.                     break;
  1681.                 case LINEDISCONNECTMODE_BADADDRESS:
  1682.                     pszReasonDisconnected = "Disconnected: Bad Address";
  1683.                     break;
  1684.                 case LINEDISCONNECTMODE_UNREACHABLE:
  1685.                     pszReasonDisconnected = "Disconnected: Unreachable";
  1686.                     break;
  1687.                 case LINEDISCONNECTMODE_CONGESTION:
  1688.                     pszReasonDisconnected = "Disconnected: Congestion";
  1689.                     break;
  1690.                 case LINEDISCONNECTMODE_INCOMPATIBLE:
  1691.                     pszReasonDisconnected = "Disconnected: Incompatible";
  1692.                     break;
  1693.                 case LINEDISCONNECTMODE_UNAVAIL:
  1694.                     pszReasonDisconnected = "Disconnected: Unavail";
  1695.                     break;
  1696.                 case LINEDISCONNECTMODE_NODIALTONE:
  1697.                     pszReasonDisconnected = "Disconnected: No Dial Tone";
  1698.                     break;
  1699.                 default:
  1700.                     pszReasonDisconnected = 
  1701.                         "Disconnected: LINECALLSTATE; Bad Reason";
  1702.                     break;
  1703.             }
  1704.             UpdateStatusBar(pszReasonDisconnected,1,0);
  1705.             OutputDebugString(pszReasonDisconnected);
  1706.             OutputDebugString("n");
  1707.             HangupCall();
  1708.             break;
  1709.         }
  1710.         
  1711.         case LINECALLSTATE_CONNECTED:  // CONNECTED!!!
  1712.         {
  1713.             LPVARSTRING lpVarString = NULL;
  1714.             DWORD dwSizeofVarString = sizeof(VARSTRING) + 1024;
  1715.             HANDLE hCommFile = NULL;
  1716.             long lReturn;
  1717.             // Very first, make sure this isn't a duplicated message.
  1718.             // A CALLSTATE message can be sent whenever there is a
  1719.             // change to the capabilities of a line, meaning that it is
  1720.             // possible to receive multiple CONNECTED messages per call.
  1721.             // The CONNECTED CALLSTATE message is the only one in TapiComm
  1722.             // where it would cause problems if it where sent more
  1723.             // than once.
  1724.             if (g_bConnected)
  1725.                 break;
  1726.             g_bConnected = TRUE;
  1727.             // Get the handle to the comm port from the driver so we can start
  1728.             // communicating.  This is returned in a LPVARSTRING structure.
  1729.             do
  1730.             {
  1731.                 // Allocate the VARSTRING structure
  1732.                 lpVarString = CheckAndReAllocBuffer((LPVOID) lpVarString,
  1733.                     dwSizeofVarString,"lineGetID: ");
  1734.                 if (lpVarString == NULL)
  1735.                     goto ErrorConnecting;
  1736.                 // Fill the VARSTRING structure
  1737.                 lReturn = lineGetID(0, 0, g_hCall, LINECALLSELECT_CALL,
  1738.                     lpVarString, "comm/datamodem");
  1739.                 if (HandleLineErr(lReturn))
  1740.                     ; // Still need to check if structure was big enough.
  1741.                 else
  1742.                 {
  1743.                     OutputDebugLineError(lReturn, 
  1744.                         "lineGetID unhandled error: ");
  1745.                     goto ErrorConnecting;
  1746.                 }
  1747.                 // If the VARSTRING wasn't big enough, loop again.
  1748.                 if ((lpVarString -> dwNeededSize) > (lpVarString -> dwTotalSize))
  1749.                 {
  1750.                     dwSizeofVarString = lpVarString -> dwNeededSize;
  1751.                     lReturn = -1; // Lets loop again.
  1752.                 }
  1753.             }
  1754.             while(lReturn != SUCCESS);
  1755.             OutputDebugString("Connected!  Starting communications!n");
  1756.             // Again, the handle to the comm port is contained in a
  1757.             // LPVARSTRING structure.  Thus, the handle is the very first
  1758.             // thing after the end of the structure.  Note that the name of
  1759.             // the comm port is right after the handle, but I don't want it.
  1760.             hCommFile = 
  1761.                 *((LPHANDLE)((LPBYTE)lpVarString +
  1762.                     lpVarString -> dwStringOffset));
  1763.             // Started communications!
  1764.             if (StartComm(hCommFile))
  1765.             {
  1766.                 char szBuff[300];
  1767.                 wsprintf(szBuff,"Connected to '%s'",g_szDisplayableAddress);
  1768.                 UpdateStatusBar(szBuff, 1, 0);
  1769.                 LocalFree(lpVarString);
  1770.                 break;
  1771.             }
  1772.             // Couldn't start communications.  Clean up instead.
  1773.           ErrorConnecting:
  1774.             // Its very important that we close all Win32 handles.
  1775.             // The CommCode module is responsible for closing the hCommFile
  1776.             // handle if it succeeds in starting communications.
  1777.             if (hCommFile)
  1778.                 CloseHandle(hCommFile);
  1779.             HangupCall();
  1780.             {
  1781.                 char szBuff[300];
  1782.                 wsprintf(szBuff,"Failed to Connect to '%s'",
  1783.                     g_szDisplayableAddress);
  1784.                 UpdateStatusBar(szBuff, 1, 0);
  1785.             }
  1786.             if (lpVarString)
  1787.                 LocalFree(lpVarString);
  1788.             break;
  1789.         }
  1790.         default:
  1791.             OutputDebugString("Unhandled LINECALLSTATE messagen");
  1792.             break;
  1793.     }
  1794. }
  1795. //**************************************************
  1796. // line API Wrapper Functions.
  1797. //**************************************************
  1798. //
  1799. //  FUNCTION: LPVOID CheckAndReAllocBuffer(LPVOID, size_t, LPCSTR)
  1800. //
  1801. //  PURPOSE: Checks and ReAllocates a buffer if necessary.
  1802. //
  1803. //  PARAMETERS:
  1804. //    lpBuffer          - Pointer to buffer to be checked.  Can be NULL.
  1805. //    sizeBufferMinimum - Minimum size that lpBuffer should be.
  1806. //    szApiPhrase       - Phrase to print if an error occurs.
  1807. //
  1808. //  RETURN VALUE:
  1809. //    Returns a pointer to a valid buffer that is guarenteed to be
  1810. //    at least sizeBufferMinimum size.
  1811. //    Returns NULL if an error occured.
  1812. //
  1813. //  COMMENTS:
  1814. //
  1815. //    This function is a helper function intended to make all of the 
  1816. //    line API Wrapper Functions much simplier.  It allocates (or
  1817. //    reallocates) a buffer of the requested size.
  1818. //
  1819. //    The returned pointer has been allocated with LocalAlloc,
  1820. //    so LocalFree has to be called on it when you're finished with it,
  1821. //    or there will be a memory leak.
  1822. //
  1823. //    Similarly, if a pointer is passed in, it *must* have been allocated
  1824. //    with LocalAlloc and it could potentially be LocalFree()d.
  1825. //
  1826. //    If lpBuffer == NULL, then a new buffer is allocated.  It is
  1827. //    normal to pass in NULL for this parameter the first time and only
  1828. //    pass in a pointer if the buffer needs to be reallocated.
  1829. //
  1830. //    szApiPhrase is used only for debugging purposes.
  1831. //
  1832. //    It is assumed that the buffer returned from this function will be used
  1833. //    to contain a variable sized structure.  Thus, the dwTotalSize field
  1834. //    is always filled in before returning the pointer.
  1835. //
  1836. //
  1837. LPVOID CheckAndReAllocBuffer(
  1838.     LPVOID lpBuffer, size_t sizeBufferMinimum, LPCSTR szApiPhrase)
  1839. {
  1840.     size_t sizeBuffer;
  1841.     if (lpBuffer == NULL)  // Allocate the buffer if necessary. 
  1842.     {
  1843.         sizeBuffer = sizeBufferMinimum;
  1844.         lpBuffer = (LPVOID) LocalAlloc(LPTR, sizeBuffer);
  1845.             
  1846.         if (lpBuffer == NULL)
  1847.         {
  1848.             OutputDebugString(szApiPhrase);
  1849.             OutputDebugLastError(GetLastError(),"LocalAlloc : ");
  1850.             HandleNoMem();
  1851.             return NULL;
  1852.         }
  1853.     }
  1854.     else // If the structure already exists, make sure its good.
  1855.     {
  1856.         sizeBuffer = LocalSize((HLOCAL) lpBuffer);
  1857.         if (sizeBuffer == 0) // Bad pointer?
  1858.         {
  1859.             OutputDebugString(szApiPhrase);
  1860.             OutputDebugLastError(GetLastError(),"LocalSize : ");
  1861.             return NULL;
  1862.         }
  1863.         // Was the buffer big enough for the structure?
  1864.         if (sizeBuffer < sizeBufferMinimum)
  1865.         {
  1866.             OutputDebugString(szApiPhrase);
  1867.             OutputDebugString("Reallocating structuren");
  1868.             LocalFree(lpBuffer);
  1869.             return CheckAndReAllocBuffer(NULL, sizeBufferMinimum, szApiPhrase);
  1870.         }
  1871.         // Lets zero the buffer out.
  1872.         memset(lpBuffer, 0, sizeBuffer);
  1873.     }
  1874.                 
  1875.     ((LPVARSTRING) lpBuffer ) -> dwTotalSize = (DWORD) sizeBuffer;
  1876.     return lpBuffer;
  1877. }
  1878. //
  1879. //  FUNCTION: DWORD I_lineNegotiateAPIVersion(DWORD)
  1880. //
  1881. //  PURPOSE: Negotiate an API Version to use for a specific device.
  1882. //
  1883. //  PARAMETERS:
  1884. //    dwDeviceID - device to negotiate an API Version for.
  1885. //
  1886. //  RETURN VALUE:
  1887. //    Returns the API Version to use for this line if successful.
  1888. //    Returns 0 if negotiations fall through.
  1889. //
  1890. //  COMMENTS:
  1891. //
  1892. //    This wrapper function not only negotiates the API, but handles
  1893. //    LINEERR errors that can occur while negotiating.
  1894. //
  1895. //
  1896. DWORD I_lineNegotiateAPIVersion(DWORD dwDeviceID)
  1897. {
  1898.     LINEEXTENSIONID LineExtensionID;
  1899.     long lReturn;
  1900.     DWORD dwLocalAPIVersion;
  1901.     
  1902.     do
  1903.     {
  1904.         lReturn = lineNegotiateAPIVersion(g_hLineApp, dwDeviceID, 
  1905.             SAMPLE_TAPI_VERSION, SAMPLE_TAPI_VERSION,
  1906.             &dwLocalAPIVersion, &LineExtensionID);
  1907.         if (lReturn == LINEERR_INCOMPATIBLEAPIVERSION)
  1908.         {
  1909.             OutputDebugString(
  1910.                 "lineNegotiateAPIVersion, INCOMPATIBLEAPIVERSION.n");
  1911.             return 0;
  1912.         }
  1913.         if (HandleLineErr(lReturn))
  1914.             continue;
  1915.         else
  1916.         {
  1917.             OutputDebugLineError(lReturn, 
  1918.                 "lineNegotiateAPIVersion unhandled error: ");
  1919.             return 0;
  1920.         }
  1921.     }
  1922.     while(lReturn != SUCCESS);
  1923.         
  1924.     return dwLocalAPIVersion;
  1925. }
  1926. //
  1927. //  FUNCTION: I_lineGetDevCaps(LPLINEDEVCAPS, DWORD , DWORD)
  1928. //
  1929. //  PURPOSE: Retrieve a LINEDEVCAPS structure for the specified line.
  1930. //
  1931. //  PARAMETERS:
  1932. //    lpLineDevCaps - Pointer to a LINEDEVCAPS structure to use.
  1933. //    dwDeviceID    - device to get the DevCaps for.
  1934. //    dwAPIVersion  - API Version to use while getting DevCaps.
  1935. //
  1936. //  RETURN VALUE:
  1937. //    Returns a pointer to a LINEDEVCAPS structure if successful.
  1938. //    Returns NULL if unsuccessful.
  1939. //
  1940. //  COMMENTS:
  1941. //
  1942. //    This function is a wrapper around lineGetDevCaps to make it easy
  1943. //    to handle the variable sized structure and any errors received.
  1944. //    
  1945. //    The returned structure has been allocated with LocalAlloc,
  1946. //    so LocalFree has to be called on it when you're finished with it,
  1947. //    or there will be a memory leak.
  1948. //
  1949. //    Similarly, if a lpLineDevCaps structure is passed in, it *must*
  1950. //    have been allocated with LocalAlloc and it could potentially be 
  1951. //    LocalFree()d.
  1952. //
  1953. //    If lpLineDevCaps == NULL, then a new structure is allocated.  It is
  1954. //    normal to pass in NULL for this parameter unless you want to use a 
  1955. //    lpLineDevCaps that has been returned by a previous I_lineGetDevCaps
  1956. //    call.
  1957. //
  1958. //
  1959. LPLINEDEVCAPS I_lineGetDevCaps(
  1960.     LPLINEDEVCAPS lpLineDevCaps,
  1961.     DWORD dwDeviceID, DWORD dwAPIVersion)
  1962. {
  1963.     size_t sizeofLineDevCaps = sizeof(LINEDEVCAPS) + 1024;
  1964.     long lReturn;
  1965.     
  1966.     // Continue this loop until the structure is big enough.
  1967.     while(TRUE)
  1968.     {
  1969.         // Make sure the buffer exists, is valid and big enough.
  1970.         lpLineDevCaps = 
  1971.             (LPLINEDEVCAPS) CheckAndReAllocBuffer(
  1972.                 (LPVOID) lpLineDevCaps, // Pointer to existing buffer, if any
  1973.                 sizeofLineDevCaps,      // Minimum size the buffer should be
  1974.                 "lineGetDevCaps");      // Phrase to tag errors, if any.
  1975.         if (lpLineDevCaps == NULL)
  1976.             return NULL;
  1977.         // Make the call to fill the structure.
  1978.         do
  1979.         {            
  1980.             lReturn = 
  1981.                 lineGetDevCaps(g_hLineApp, 
  1982.                     dwDeviceID, dwAPIVersion, 0, lpLineDevCaps);