TAPIUTILS.CPP
上传用户:fstfhk1234
上传日期:2007-06-12
资源大小:67k
文件大小:31k
源码类别:

TAPI编程

开发平台:

Visual C++

  1. //
  2. // TAPIUTILS.CPP
  3. //
  4. // This module implements the CTapiConnection class.
  5. //
  6. #include "stdafx.h"
  7. #include <string.h>
  8. #include "TapiUtils.h"
  9. // All TAPI line functions return 0 for SUCCESS, so define it.
  10. #define SUCCESS 0
  11. // TAPI version that this sample is designed to use.
  12. #define SAMPLE_TAPI_VERSION 0x00010004
  13. // Early TAPI version
  14. #define EARLY_TAPI_VERSION 0x00010003
  15. // Possible return error for resynchronization functions.
  16. #define WAITERR_WAITABORTED  1
  17. // A pointer to my class because TAPI needs a callback
  18. CTapiConnection *MyThis;
  19. #if FALSE
  20. // Structures needed to handle special non-dialable characters.
  21. #define g_sizeofNonDialable (sizeof(g_sNonDialable)/sizeof(g_sNonDialable[0]))
  22. typedef struct {
  23.     LONG lError;
  24.     DWORD dwDevCapFlag;
  25.     LPSTR szToken;
  26.     LPSTR szMsg;
  27. } NONDIALTOKENS;
  28. NONDIALTOKENS g_sNonDialable[] = {
  29.     {LINEERR_DIALBILLING,  LINEDEVCAPFLAGS_DIALBILLING,  "$", 
  30.             "Wait for the credit card bong tone" },
  31.     {LINEERR_DIALDIALTONE, LINEDEVCAPFLAGS_DIALDIALTONE, "W", 
  32.             "Wait for the second dial tone" },
  33.     {LINEERR_DIALDIALTONE, LINEDEVCAPFLAGS_DIALDIALTONE, "w", 
  34.             "Wait for the second dial tone" },
  35.     {LINEERR_DIALQUIET,    LINEDEVCAPFLAGS_DIALQUIET,    "@", 
  36.             "Wait for the remote end to answer" },
  37.     {LINEERR_DIALPROMPT,   0,                            "?", 
  38.             "Press OK when you are ready to continue dialing"},
  39. };
  40. #endif
  41. // 
  42. // Constructor
  43. //
  44. CTapiConnection::CTapiConnection()
  45. {
  46.     m_bShuttingDown = FALSE;
  47.     m_bStoppingCall = FALSE;
  48.     m_bInitializing = FALSE;
  49.     m_bTapiInUse = FALSE;
  50.     m_dwNumDevs = 0;
  51.     m_hCall = NULL;
  52.     m_hLine = NULL;
  53.     m_dwDeviceID = 0;
  54.     m_hLineApp = NULL;
  55.     MyThis = this;
  56. };
  57. //
  58. // Destructor
  59. //
  60. CTapiConnection::~CTapiConnection()
  61. {
  62.     m_bInitialized = FALSE;
  63. };
  64. //
  65. //  FUNCTION: BOOL Create()
  66. //
  67. //  PURPOSE: Initializes TAPI
  68. //
  69. BOOL CTapiConnection::Create(char *szPhoneNumber)
  70. {
  71.     long lReturn;
  72.     // If we're already initialized, then initialization succeeds.
  73.     if (m_hLineApp)
  74.         return TRUE;
  75.     // If we're in the middle of initializing, then fail, we're not done.
  76.     if (m_bInitializing)
  77.         return FALSE;
  78.     m_bInitializing = TRUE;
  79.     // Initialize TAPI
  80.     do
  81.     {
  82.         lReturn = ::lineInitialize(&m_hLineApp, 
  83.             AfxGetInstanceHandle(), 
  84.             lineCallbackFunc, 
  85.             "DialIt", 
  86.             &m_dwNumDevs);
  87.         if (m_dwNumDevs == 0)
  88.         {
  89.             AfxMessageBox("There are no telephony devices installed.");
  90.             m_bInitializing = FALSE;
  91.             return FALSE;
  92.         }
  93.         if (HandleLineErr(lReturn))
  94.             continue;
  95.         else
  96.         {
  97.             OutputDebugString("lineInitialize unhandled errorn");
  98.             m_bInitializing = FALSE;
  99.             return FALSE;
  100.         }
  101.     }
  102.     while(lReturn != SUCCESS);
  103.     OutputDebugString("Tapi initialized.n");
  104.     // If the user furnished a phone number copy it over.
  105.     if (szPhoneNumber)
  106.         strcpy(m_szPhoneNumber, szPhoneNumber);
  107.     m_bInitializing = FALSE;
  108.     return TRUE;
  109. }
  110. //
  111. //  FUNCTION: DialCall()
  112. //
  113. //  PURPOSE: Get a number from the user and dial it.
  114. //
  115. BOOL CTapiConnection::DialCall(char *szPhoneNumber)
  116. {
  117.     long lReturn;
  118.     LPLINEDEVCAPS lpLineDevCaps = NULL;
  119.     if (m_bTapiInUse)
  120.     {
  121.         AfxMessageBox("A call is already being handled.");
  122.         return FALSE;
  123.     }
  124.     // Make sure TAPI is initialized properly
  125.     if (!m_hLineApp)
  126.     {
  127.         if (!Create(NULL))
  128.             return FALSE;
  129.     }
  130.     // If there are no line devices installed on the machine, quit.
  131.     if (m_dwNumDevs < 1)
  132.         return FALSE;
  133.     // We now have a call active.  Prevent future calls.
  134.     m_bTapiInUse = TRUE;
  135.     // Get a phone number from the user.
  136.     if (szPhoneNumber == (char *)NULL)
  137.     {
  138.         if (m_szPhoneNumber == (char *)NULL)
  139.         {
  140.             HangupCall();
  141.             goto DeleteBuffers;
  142.         }
  143.     }
  144.     else 
  145.         strcpy(m_szPhoneNumber, szPhoneNumber);
  146.     // Get the line to use
  147.     lpLineDevCaps = GetDeviceLine(&m_dwAPIVersion, lpLineDevCaps);
  148.     // Need to check the DevCaps to make sure this line is usable.
  149.     if (lpLineDevCaps == NULL)
  150.     {
  151.         OutputDebugString("Error on Requested linen");
  152.         goto DeleteBuffers;
  153.     }
  154.     if (!(lpLineDevCaps->dwBearerModes & LINEBEARERMODE_VOICE ))
  155.     {
  156.         AfxMessageBox("The selected line doesn't support VOICE capabilities");
  157.         goto DeleteBuffers;
  158.     }
  159.     // Does this line have the capability to make calls?
  160.     if (!(lpLineDevCaps->dwLineFeatures & LINEFEATURE_MAKECALL))
  161.     {
  162.         AfxMessageBox("The selected line doesn't support MAKECALL capabilities");
  163.         goto DeleteBuffers;
  164.     }
  165.     // Does this line have the capability for interactive voice?
  166.     if (!(lpLineDevCaps->dwMediaModes & LINEMEDIAMODE_INTERACTIVEVOICE))
  167.     {
  168.         AfxMessageBox("The selected line doesn't support INTERACTIVE VOICE capabilities");
  169.         goto DeleteBuffers;
  170.     }
  171.     // Open the Line for an outgoing call.
  172.     do
  173.     {
  174.         lReturn = ::lineOpen(m_hLineApp, m_dwDeviceID, &m_hLine,
  175.             m_dwAPIVersion, 0, 0,
  176.             LINECALLPRIVILEGE_NONE, 0, 0);
  177.         if((lReturn == LINEERR_ALLOCATED)||(lReturn == LINEERR_RESOURCEUNAVAIL))
  178.         {
  179.             HangupCall();
  180.             OutputDebugString("Line is already in use by a non-TAPI application "
  181.                 "or by another TAPI Service Provider.n");
  182.             goto DeleteBuffers;
  183.         }
  184.         if (HandleLineErr(lReturn))
  185.             continue;
  186.         else
  187.         {
  188.             OutputDebugString("Unable to Use Linen");
  189.             HangupCall();
  190.             goto DeleteBuffers;
  191.         }
  192.     }
  193.     while(lReturn != SUCCESS);
  194.     // Start dialing the number
  195.     if( MakeTheCall(lpLineDevCaps, m_szPhoneNumber))
  196.         OutputDebugString("lineMakeCall succeeded.n");
  197.     else
  198.     {
  199.         OutputDebugString("lineMakeCall failed.n");
  200.         HangupCall();
  201.     }
  202. DeleteBuffers:
  203.     if (lpLineDevCaps)
  204.         LocalFree(lpLineDevCaps);
  205.     return m_bTapiInUse;
  206. }
  207. //
  208. //  FUNCTION: void GetDeviceLine()
  209. //
  210. //  PURPOSE: Gets the first available line device.
  211. //
  212. //
  213. LPLINEDEVCAPS CTapiConnection::GetDeviceLine(DWORD *pdwAPIVersion, LPLINEDEVCAPS lpLineDevCaps)
  214. {
  215.     DWORD dwDeviceID;
  216.     char szLineUnavail[] = "Line Unavailable";
  217.     char szLineUnnamed[] = "Line Unnamed";
  218.     char szLineNameEmpty[] = "Line Name is Empty";
  219.     LPSTR lpszLineName;
  220.     long lReturn;
  221.     char buf[MAX_PATH];
  222.     LINEEXTENSIONID lineExtID;
  223.     BOOL bDone = FALSE;
  224.     for (dwDeviceID = 0; (dwDeviceID < m_dwNumDevs) && !bDone; dwDeviceID ++)
  225.     {
  226.         lReturn = ::lineNegotiateAPIVersion(m_hLineApp, dwDeviceID, 
  227.             EARLY_TAPI_VERSION, SAMPLE_TAPI_VERSION,
  228.             pdwAPIVersion, &lineExtID);
  229.         if ((HandleLineErr(lReturn))&&(*pdwAPIVersion))
  230.         {
  231.             lpLineDevCaps = MylineGetDevCaps(lpLineDevCaps, dwDeviceID, *pdwAPIVersion);
  232.             if ((lpLineDevCaps -> dwLineNameSize) &&
  233.                 (lpLineDevCaps -> dwLineNameOffset) &&
  234.                 (lpLineDevCaps -> dwStringFormat == STRINGFORMAT_ASCII))
  235.             {
  236.                 // This is the name of the device.
  237.                 lpszLineName = ((char *) lpLineDevCaps) + 
  238.                     lpLineDevCaps -> dwLineNameOffset;
  239.                 sprintf(buf, "Name of device is: %sn", lpszLineName);
  240.                 OutputDebugString(buf);
  241.             }
  242.             else  // DevCaps doesn't have a valid line name.  Unnamed.
  243.                 lpszLineName = szLineUnnamed;
  244.         }
  245.         else  // Couldn't NegotiateAPIVersion.  Line is unavail.
  246.             lpszLineName = szLineUnavail;
  247.         // If this line is usable and we don't have a default initial
  248.         // line yet, make this the initial line.
  249.         if ((lpszLineName != szLineUnavail) && 
  250.             (lReturn == SUCCESS )) 
  251.         {          
  252.             m_dwDeviceID = dwDeviceID;
  253.             bDone = TRUE;
  254.         }
  255.         else 
  256.             m_dwDeviceID = MAXDWORD;
  257.     }
  258.     return (lpLineDevCaps);
  259. }
  260. //
  261. //  FUNCTION: MylineGetDevCaps(LPLINEDEVCAPS, DWORD , DWORD)
  262. //
  263. //  PURPOSE: Gets a LINEDEVCAPS structure for the specified line.
  264. //
  265. //  COMMENTS:
  266. //
  267. //    This function is a wrapper around lineGetDevCaps to make it easy
  268. //    to handle the variable sized structure and any errors received.
  269. //    
  270. //    The returned structure has been allocated with LocalAlloc,
  271. //    so LocalFree has to be called on it when you're finished with it,
  272. //    or there will be a memory leak.
  273. //
  274. //    Similarly, if a lpLineDevCaps structure is passed in, it *must*
  275. //    have been allocated with LocalAlloc and it could potentially be 
  276. //    LocalFree()d.
  277. //
  278. //    If lpLineDevCaps == NULL, then a new structure is allocated.  It is
  279. //    normal to pass in NULL for this parameter unless you want to use a 
  280. //    lpLineDevCaps that has been returned by a previous I_lineGetDevCaps
  281. //    call.
  282. //
  283. //
  284. LPLINEDEVCAPS CTapiConnection::MylineGetDevCaps(
  285.     LPLINEDEVCAPS lpLineDevCaps,
  286.     DWORD dwDeviceID, DWORD dwAPIVersion)
  287. {
  288.     // Allocate enough space for the structure plus 1024.
  289.     size_t sizeofLineDevCaps = sizeof(LINEDEVCAPS) + 1024;
  290.     long lReturn;
  291.     
  292.     // Continue this loop until the structure is big enough.
  293.     while(TRUE)
  294.     {
  295.         // Make sure the buffer exists, is valid and big enough.
  296.         lpLineDevCaps = 
  297.             (LPLINEDEVCAPS) CheckAndReAllocBuffer(
  298.                 (LPVOID) lpLineDevCaps, // Pointer to existing buffer, if any
  299.                 sizeofLineDevCaps);      // Minimum size the buffer should be
  300.         if (lpLineDevCaps == NULL)
  301.             return NULL;
  302.         // Make the call to fill the structure.
  303.         do
  304.         {            
  305.             lReturn = 
  306.                 ::lineGetDevCaps(m_hLineApp, 
  307.                     dwDeviceID, dwAPIVersion, 0, lpLineDevCaps);
  308.             if (HandleLineErr(lReturn))
  309.                 continue;
  310.             else
  311.             {
  312.                 OutputDebugString("lineGetDevCaps unhandled error/n");
  313.                 LocalFree(lpLineDevCaps);
  314.                 return NULL;
  315.             }
  316.         }
  317.         while (lReturn != SUCCESS);
  318.         // If the buffer was big enough, then succeed.
  319.         if ((lpLineDevCaps -> dwNeededSize) <= (lpLineDevCaps -> dwTotalSize))
  320.             return lpLineDevCaps;
  321.         // Buffer wasn't big enough.  Make it bigger and try again.
  322.         sizeofLineDevCaps = lpLineDevCaps -> dwNeededSize;
  323.     }
  324. }
  325. //
  326. //  FUNCTION: LPVOID CheckAndReAllocBuffer(LPVOID, size_t, LPCSTR)
  327. //
  328. //  PURPOSE: Checks and ReAllocates a buffer if necessary.
  329. //
  330. LPVOID CTapiConnection::CheckAndReAllocBuffer(LPVOID lpBuffer, size_t sizeBufferMinimum)
  331. {
  332.     size_t sizeBuffer;
  333.     if (lpBuffer == NULL)  // Allocate the buffer if necessary. 
  334.     {
  335.         sizeBuffer = sizeBufferMinimum;
  336.         lpBuffer = (LPVOID) LocalAlloc (LPTR, sizeBuffer);
  337.             
  338.         if (lpBuffer == NULL)
  339.         {
  340.             OutputDebugString("LocalAlloc failed in CheckAndReAllocBuffer./n");
  341.             return NULL;
  342.         }
  343.     }
  344.     else // If the structure already exists, make sure its good.
  345.     {
  346.         sizeBuffer = LocalSize((HLOCAL) lpBuffer);
  347.         if (sizeBuffer == 0) // Bad pointer?
  348.         {
  349.             OutputDebugString("LocalSize returned 0 in CheckAndReAllocBuffer/n");
  350.             return NULL;
  351.         }
  352.         // Was the buffer big enough for the structure?
  353.         if (sizeBuffer < sizeBufferMinimum)
  354.         {
  355.             OutputDebugString("Reallocating structuren");
  356.             LocalFree(lpBuffer);
  357.             return CheckAndReAllocBuffer(NULL, sizeBufferMinimum);
  358.         }
  359.     }
  360.     memset(lpBuffer, 0, sizeBuffer);       
  361.     ((LPVARSTRING) lpBuffer ) -> dwTotalSize = (DWORD) sizeBuffer;
  362.     return lpBuffer;
  363. }
  364. //
  365. //  FUNCTION: MylineGetAddressCaps(LPLINEADDRESSCAPS, ..)
  366. //
  367. //  PURPOSE: Return a LINEADDRESSCAPS structure for the specified line.
  368. //
  369. LPLINEADDRESSCAPS CTapiConnection::MylineGetAddressCaps (
  370.     LPLINEADDRESSCAPS lpLineAddressCaps,
  371.     DWORD dwDeviceID, DWORD dwAddressID,
  372.     DWORD dwAPIVersion, DWORD dwExtVersion)
  373. {
  374.     size_t sizeofLineAddressCaps = sizeof(LINEADDRESSCAPS) + 1024;
  375.     long lReturn;
  376.     
  377.     // Continue this loop until the structure is big enough.
  378.     while(TRUE)
  379.     {
  380.         // Make sure the buffer exists, is valid and big enough.
  381.         lpLineAddressCaps = 
  382.             (LPLINEADDRESSCAPS) CheckAndReAllocBuffer(
  383.                 (LPVOID) lpLineAddressCaps,
  384.                 sizeofLineAddressCaps);
  385.         if (lpLineAddressCaps == NULL)
  386.             return NULL;
  387.             
  388.         // Make the call to fill the structure.
  389.         do
  390.         {
  391.             lReturn = 
  392.                 ::lineGetAddressCaps(m_hLineApp,
  393.                     dwDeviceID, dwAddressID, dwAPIVersion, dwExtVersion,
  394.                     lpLineAddressCaps);
  395.             if (HandleLineErr(lReturn))
  396.                 continue;
  397.             else
  398.             {
  399.                 OutputDebugString("lineGetAddressCaps unhandled errorn");
  400.                 LocalFree(lpLineAddressCaps);
  401.                 return NULL;
  402.             }
  403.         }
  404.         while (lReturn != SUCCESS);
  405.         // If the buffer was big enough, then succeed.
  406.         if ((lpLineAddressCaps -> dwNeededSize) <= 
  407.             (lpLineAddressCaps -> dwTotalSize))
  408.         {
  409.             return lpLineAddressCaps;
  410.         }
  411.         // Buffer wasn't big enough.  Make it bigger and try again.
  412.         sizeofLineAddressCaps = lpLineAddressCaps -> dwNeededSize;
  413.     }
  414. }
  415. //
  416. //  FUNCTION: MakeTheCall(LPLINEDEVCAPS, LPCSTR)
  417. //
  418. //  PURPOSE: Dials the call
  419. //
  420. BOOL CTapiConnection::MakeTheCall(LPLINEDEVCAPS lpLineDevCaps, LPCTSTR lpszAddress)
  421. {
  422.     LPLINECALLPARAMS  lpCallParams = NULL;
  423.     LPLINEADDRESSCAPS lpAddressCaps = NULL;
  424.     long lReturn;
  425.     BOOL bFirstDial = TRUE;
  426.                                
  427.     // Get the capabilities for the line device we're going to use.
  428.     lpAddressCaps = MylineGetAddressCaps(lpAddressCaps,
  429.         m_dwDeviceID, 0, m_dwAPIVersion, 0);
  430.     if (lpAddressCaps == NULL)
  431.         return FALSE;
  432.     // Setup our CallParams.
  433.     lpCallParams = CreateCallParams (lpCallParams, lpszAddress);
  434.     if (lpCallParams == NULL)
  435.         return FALSE;
  436.     do
  437.     {                   
  438.         if (bFirstDial)
  439.             //lReturn = ::lineMakeCall(m_hLine, &m_hCall, lpszAddress, 0, lpCallParams);
  440.             lReturn = WaitForReply( ::lineMakeCall(m_hLine, &m_hCall, lpszAddress, 
  441.                         0, lpCallParams) );
  442.         else
  443.             lReturn = WaitForReply(::lineDial(m_hCall, lpszAddress, 0));
  444.         if (lReturn == WAITERR_WAITABORTED)
  445.         {
  446.              OutputDebugString("While Dialing, WaitForReply aborted.n");
  447.              goto errExit;
  448.         }
  449.             
  450.         if (HandleLineErr(lReturn))
  451.             continue;
  452.         else
  453.         {
  454.             if (bFirstDial)
  455.                 OutputDebugString("lineMakeCall unhandled errorn");
  456.             else
  457.                 OutputDebugString("lineDial unhandled errorn");
  458.             goto errExit;
  459.         }
  460.     }
  461.     while (lReturn != SUCCESS);
  462.         
  463.     bFirstDial = FALSE;
  464.     if (lpCallParams)
  465.         LocalFree(lpCallParams);
  466.     if (lpAddressCaps)
  467.         LocalFree(lpAddressCaps);
  468.     
  469.     return TRUE;
  470.     
  471.   errExit:
  472.     if (lpCallParams)
  473.         LocalFree(lpCallParams);
  474.     if (lpAddressCaps)
  475.         LocalFree(lpAddressCaps);
  476.     AfxMessageBox("Dial failed.");
  477.     return FALSE;
  478. }   
  479. //
  480. //  FUNCTION: CreateCallParams(LPLINECALLPARAMS, LPCSTR)
  481. //
  482. //  PURPOSE: Allocates and fills a LINECALLPARAMS structure
  483. //
  484. //
  485. LPLINECALLPARAMS CTapiConnection::CreateCallParams (
  486.     LPLINECALLPARAMS lpCallParams, LPCSTR lpszDisplayableAddress)
  487. {
  488.     size_t sizeDisplayableAddress;
  489.     if (lpszDisplayableAddress == NULL)
  490.         lpszDisplayableAddress = "";
  491.         
  492.     sizeDisplayableAddress = strlen(lpszDisplayableAddress) + 1;
  493.                           
  494.     lpCallParams = (LPLINECALLPARAMS) CheckAndReAllocBuffer(
  495.         (LPVOID) lpCallParams, 
  496.         sizeof(LINECALLPARAMS) + sizeDisplayableAddress);
  497.     if (lpCallParams == NULL)
  498.         return NULL;
  499.     // This is where we configure the line.
  500.     lpCallParams -> dwBearerMode = LINEBEARERMODE_VOICE;
  501.     lpCallParams -> dwMediaMode  = LINEMEDIAMODE_INTERACTIVEVOICE;
  502.     // This specifies that we want to use only IDLE calls and
  503.     // don't want to cut into a call that might not be IDLE (ie, in use).
  504.     lpCallParams -> dwCallParamFlags = LINECALLPARAMFLAGS_IDLE;
  505.                                     
  506.     // if there are multiple addresses on line, use first anyway.
  507.     // It will take a more complex application than a simple tty app
  508.     // to use multiple addresses on a line anyway.
  509.     lpCallParams -> dwAddressMode = LINEADDRESSMODE_ADDRESSID;
  510.     // Address we are dialing.
  511.     lpCallParams -> dwDisplayableAddressOffset = sizeof(LINECALLPARAMS);
  512.     lpCallParams -> dwDisplayableAddressSize = sizeDisplayableAddress;
  513.     strcpy((LPSTR)lpCallParams + sizeof(LINECALLPARAMS),
  514.            lpszDisplayableAddress);
  515.     return lpCallParams;
  516. }
  517. //
  518. //  FUNCTION: long WaitForReply(long)
  519. //
  520. //  PURPOSE: Resynchronize by waiting for a LINE_REPLY 
  521. //
  522. //  PARAMETERS:
  523. //    lRequestID - The asynchronous request ID that we're
  524. //                 on a LINE_REPLY for.
  525. //
  526. //  RETURN VALUE:
  527. //    - 0 if LINE_REPLY responded with a success.
  528. //    - LINEERR constant if LINE_REPLY responded with a LINEERR
  529. //    - 1 if the line was shut down before LINE_REPLY is received.
  530. //
  531. //  COMMENTS:
  532. //
  533. //    This function allows us to resynchronize an asynchronous
  534. //    TAPI line call by waiting for the LINE_REPLY message.  It
  535. //    waits until a LINE_REPLY is received or the line is shut down.
  536. //
  537. //    Note that this could cause re-entrancy problems as
  538. //    well as mess with any message preprocessing that might
  539. //    occur on this thread (such as TranslateAccelerator).
  540. //
  541. //    This function should to be called from the thread that did
  542. //    lineInitialize, or the PeekMessage is on the wrong thread
  543. //    and the synchronization is not guaranteed to work.  Also note
  544. //    that if another PeekMessage loop is entered while waiting,
  545. //    this could also cause synchronization problems.
  546. //
  547. //    One more note.  This function can potentially be re-entered
  548. //    if the call is dropped for any reason while waiting.  If this
  549. //    happens, just drop out and assume the wait has been canceled.  
  550. //    This is signaled by setting bReentered to FALSE when the function 
  551. //    is entered and TRUE when it is left.  If bReentered is ever TRUE 
  552. //    during the function, then the function was re-entered.
  553. //
  554. //    This function times out and returns WAITERR_WAITTIMEDOUT
  555. //    after WAITTIMEOUT milliseconds have elapsed.
  556. //
  557. //
  558. long CTapiConnection::WaitForReply (long lRequestID)
  559. {
  560.     static BOOL bReentered;
  561.     bReentered = FALSE;
  562.     if (lRequestID > SUCCESS)
  563.     {
  564.         MSG msg; 
  565.         DWORD dwTimeStarted;
  566.         m_bReplyReceived = FALSE;
  567.         m_dwRequestedID = (DWORD) lRequestID;
  568.         // Initializing this just in case there is a bug
  569.         // that sets m_bReplyRecieved without setting the reply value.
  570.         m_lAsyncReply = LINEERR_OPERATIONFAILED;
  571.         dwTimeStarted = GetTickCount();
  572.         while(!m_bReplyReceived)
  573.         {
  574.             if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
  575.             {
  576.                 TranslateMessage(&msg);
  577.                 DispatchMessage(&msg);
  578.             }
  579.             // This should only occur if the line is shut down while waiting.
  580.             if ((m_hCall != NULL) &&(!m_bTapiInUse || bReentered))
  581.             {
  582.                 bReentered = TRUE;
  583.                 return WAITERR_WAITABORTED;
  584.             }
  585.             // Its a really bad idea to timeout a wait for a LINE_REPLY.
  586.             // If we are execting a LINE_REPLY, we should wait till we get
  587.             // it; it might take a long time to dial (for example).
  588.             // If 5 seconds go by without a reply, it might be a good idea
  589.             // to display a dialog box to tell the user that a
  590.             // wait is in progress and to give the user the capability to
  591.             // abort the wait.
  592.         }
  593.         bReentered = TRUE;
  594.         return m_lAsyncReply;
  595.     }
  596.     bReentered = TRUE;
  597.     return lRequestID;
  598. }
  599. //
  600. //  FUNCTION: lineCallbackFunc(..)
  601. //
  602. //  PURPOSE: Receive asynchronous TAPI events
  603. //
  604. void CALLBACK CTapiConnection::lineCallbackFunc(
  605.     DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance, 
  606.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  607. {
  608.     // Handle the line messages.
  609.     switch(dwMsg)
  610.     {
  611.         case LINE_CALLSTATE:
  612.             MyThis->HandleLineCallState(dwDevice, dwMsg, dwCallbackInstance, dwParam1, dwParam2,
  613.                 dwParam3);
  614.             break;
  615.         case LINE_CLOSE:
  616.             // Line has been shut down.  
  617.             ASSERT(MyThis);
  618.             MyThis->m_hLine = NULL;
  619.             MyThis->m_hCall = NULL;
  620.             MyThis->HangupCall(); // all handles invalidated by this time
  621.             break;
  622.         case LINE_REPLY:
  623.             if ((long) dwParam2 != SUCCESS)
  624.                 OutputDebugString("LINE_REPLY errorn");
  625.             else
  626.                 OutputDebugString("LINE_REPLY: successfully replied.n");
  627.             break;
  628.         case LINE_CREATE:
  629.             ASSERT(MyThis);
  630.             if (MyThis->m_dwNumDevs <= dwParam1)
  631.                 MyThis->m_dwNumDevs = dwParam1+1;
  632.             break;
  633.         default:
  634.             OutputDebugString("lineCallbackFunc message ignoredn");
  635.             break;
  636.     }
  637.     return;
  638. }
  639. //
  640. //  FUNCTION: HandleLineCallState(..)
  641. //
  642. //  PURPOSE: Handle LINE_CALLSTATE asynchronous messages.
  643. //
  644. void CTapiConnection::HandleLineCallState(
  645.     DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
  646.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  647. {
  648.     // Error if this CALLSTATE doesn't apply to our call in progress.
  649.     if ((HCALL) dwDevice != m_hCall)
  650.     {
  651.         OutputDebugString("LINE_CALLSTATE: Unknown device ID ");
  652.         return;
  653.     }
  654.     // dwParam1 is the specific CALLSTATE change that is occurring.
  655.     switch (dwParam1)
  656.     {
  657.         case LINECALLSTATE_DIALTONE:
  658.             OutputDebugString("Dial Tonen");
  659.             break;
  660.         case LINECALLSTATE_DIALING:
  661.             OutputDebugString("Dialingn");
  662.             break;
  663.         case LINECALLSTATE_PROCEEDING:
  664.             OutputDebugString("Proceedingn");
  665.             break;
  666.         case LINECALLSTATE_RINGBACK:
  667.             OutputDebugString("RingBackn");
  668.             break;
  669.         case LINECALLSTATE_BUSY:
  670.             OutputDebugString("Line busy, shutting downn");
  671.             HangupCall();
  672.             break;
  673.         case LINECALLSTATE_IDLE:
  674.             OutputDebugString("Line idlen");
  675.             HangupCall();
  676.             break;
  677.         case LINECALLSTATE_SPECIALINFO:
  678.             OutputDebugString("Special Info, probably couldn't dial numbern");
  679.             HangupCall();
  680.             break;
  681.         case LINECALLSTATE_DISCONNECTED:
  682.         {
  683.             LPSTR pszReasonDisconnected;
  684.             switch (dwParam2)
  685.             {
  686.                 case LINEDISCONNECTMODE_NORMAL:
  687.                     pszReasonDisconnected = "Remote Party Disconnected";
  688.                     break;
  689.                 case LINEDISCONNECTMODE_UNKNOWN:
  690.                     pszReasonDisconnected = "Disconnected: Unknown reason";
  691.                     break;
  692.                 case LINEDISCONNECTMODE_REJECT:
  693.                     pszReasonDisconnected = "Remote Party rejected call";
  694.                     break;
  695.                 case LINEDISCONNECTMODE_PICKUP:
  696.                     pszReasonDisconnected = 
  697.                         "Disconnected: Local phone picked up";
  698.                     break;
  699.                 case LINEDISCONNECTMODE_FORWARDED:
  700.                     pszReasonDisconnected = "Disconnected: Forwarded";
  701.                     break;
  702.                 case LINEDISCONNECTMODE_BUSY:
  703.                     pszReasonDisconnected = "Disconnected: Busy";
  704.                     break;
  705.                 case LINEDISCONNECTMODE_NOANSWER:
  706.                     pszReasonDisconnected = "Disconnected: No Answer";
  707.                     break;
  708.                 case LINEDISCONNECTMODE_BADADDRESS:
  709.                     pszReasonDisconnected = "Disconnected: Bad Address";
  710.                     break;
  711.                 case LINEDISCONNECTMODE_UNREACHABLE:
  712.                     pszReasonDisconnected = "Disconnected: Unreachable";
  713.                     break;
  714.                 case LINEDISCONNECTMODE_CONGESTION:
  715.                     pszReasonDisconnected = "Disconnected: Congestion";
  716.                     break;
  717.                 case LINEDISCONNECTMODE_INCOMPATIBLE:
  718.                     pszReasonDisconnected = "Disconnected: Incompatible";
  719.                     break;
  720.                 case LINEDISCONNECTMODE_UNAVAIL:
  721.                     pszReasonDisconnected = "Disconnected: Unavail";
  722.                     break;
  723.                 case LINEDISCONNECTMODE_NODIALTONE:
  724.                     pszReasonDisconnected = "Disconnected: No Dial Tone";
  725.                     break;
  726.                 default:
  727.                     pszReasonDisconnected = 
  728.                         "Disconnected: LINECALLSTATE; Bad Reason";
  729.                     break;
  730.             }
  731.             OutputDebugString(pszReasonDisconnected);
  732.             OutputDebugString("n");
  733.             HangupCall();
  734.             break;
  735.         }
  736.         case LINECALLSTATE_CONNECTED:  // CONNECTED!!!
  737.             OutputDebugString("Connected!n");
  738.             break;
  739.         default:
  740.             OutputDebugString("Unhandled LINECALLSTATE messagen");
  741.             break;
  742.     }
  743. }
  744. //
  745. //  FUNCTION: HandleLineErr(long)
  746. //
  747. //  PURPOSE: Handle several of the standard LINEERR errors
  748. //
  749. BOOL CTapiConnection::HandleLineErr(long lLineErr)
  750. {
  751.     BOOL bRet = FALSE;
  752.     // lLineErr is really an async request ID, not an error.
  753.     if (lLineErr > SUCCESS)
  754.         return bRet;
  755.     // All we do is dispatch the correct error handler.
  756.     switch(lLineErr)
  757.     {
  758.         case SUCCESS:
  759.             bRet = TRUE;
  760.             break;
  761.         case LINEERR_INVALCARD:
  762.         case LINEERR_INVALLOCATION:
  763.         case LINEERR_INIFILECORRUPT:
  764.             OutputDebugString("The values in the INI file are invalid.n");
  765.             break;
  766.         case LINEERR_NODRIVER:
  767.             OutputDebugString("There is a problem with your Telephony device driver.n");
  768.             break;
  769.         case LINEERR_REINIT:
  770.             ShutdownTAPI();
  771.             break;
  772.         case LINEERR_NOMULTIPLEINSTANCE:
  773.             OutputDebugString("Remove one of your copies of your Telephony driver.n");
  774.             break;
  775.         case LINEERR_NOMEM:
  776.             OutputDebugString("Out of memory. Cancelling action.n");
  777.             break;
  778.         case LINEERR_OPERATIONFAILED:
  779.             OutputDebugString("The TAPI operation failed.n");
  780.             break;
  781.         case LINEERR_RESOURCEUNAVAIL:
  782.             OutputDebugString("A TAPI resource is unavailable at this time.n");
  783.             break;
  784.         // Unhandled errors fail.
  785.         default:
  786.             break;
  787.     }
  788.     return bRet;
  789. }
  790. //
  791. //  FUNCTION: BOOL HangupCall()
  792. //
  793. //  PURPOSE: Hangup the call in progress if it exists.
  794. //
  795. BOOL CTapiConnection::HangupCall()
  796. {         
  797.     LPLINECALLSTATUS pLineCallStatus = NULL;
  798.     long lReturn;
  799.     // Prevent HangupCall re-entrancy problems.
  800.     if (m_bStoppingCall)
  801.         return TRUE;
  802.     // If Tapi is not being used right now, then the call is hung up.
  803.     if (!m_bTapiInUse)
  804.         return TRUE;
  805.     m_bStoppingCall = TRUE;
  806.     OutputDebugString("Stopping Call in progressn");
  807.     // If there is a call in progress, drop and deallocate it.
  808.     if (m_hCall)
  809.     {
  810.         pLineCallStatus = (LPLINECALLSTATUS)malloc(sizeof(LINECALLSTATUS));
  811.         if (!pLineCallStatus)
  812.         {
  813.             ShutdownTAPI();
  814.             m_bStoppingCall = FALSE;
  815.             return FALSE;
  816.         }
  817.         lReturn = ::lineGetCallStatus(m_hCall, pLineCallStatus);
  818.         // Only drop the call when the line is not IDLE.
  819.         if (!((pLineCallStatus -> dwCallState) & LINECALLSTATE_IDLE))
  820.         {
  821.             WaitForReply(lineDrop(m_hCall, NULL, 0));
  822.             OutputDebugString("Call Dropped.n");
  823.         }
  824.         // The call is now idle.  Deallocate it!
  825.         do
  826.         {
  827.             lReturn = ::lineDeallocateCall(m_hCall);
  828.             if (HandleLineErr(lReturn))
  829.                 continue;
  830.             else
  831.             {
  832.                 OutputDebugString("lineDeallocateCall unhandled errorn");
  833.                 break;
  834.             }
  835.         }
  836.         while(lReturn != SUCCESS);
  837.         OutputDebugString("Call Deallocated.n");
  838.     }
  839.     // if we have a line open, close it.
  840.     if (m_hLine)
  841.     {
  842.        lReturn = ::lineClose(m_hLine);
  843.        if (!HandleLineErr(lReturn))
  844.            OutputDebugString("lineClose unhandled errorn");
  845.         OutputDebugString("Line Closed.n");
  846.     }
  847.     // Clean up.
  848.     m_hCall = NULL;
  849.     m_hLine = NULL;
  850.     m_bTapiInUse = FALSE;
  851.     m_bStoppingCall = FALSE; // allow HangupCall to be called again.
  852.     // Need to free buffer returned from lineGetCallStatus
  853.     if (pLineCallStatus)
  854.         free(pLineCallStatus);  
  855.         
  856.     OutputDebugString("Call stoppedn");
  857.     return TRUE;
  858. }
  859. //
  860. //  FUNCTION: BOOL ShutdownTAPI()
  861. //
  862. //  PURPOSE: Shuts down all use of TAPI
  863. //
  864. BOOL CTapiConnection::ShutdownTAPI()
  865. {
  866.     long lReturn;
  867.     // If we aren't initialized, then Shutdown is unnecessary.
  868.     if (m_hLineApp == NULL)
  869.         return TRUE;
  870.     // Prevent ShutdownTAPI re-entrancy problems.
  871.     if (m_bShuttingDown)
  872.         return TRUE;
  873.     m_bShuttingDown = TRUE;
  874.     HangupCall();
  875.     
  876.     do
  877.     {
  878.         lReturn = ::lineShutdown(m_hLineApp);
  879.         if (HandleLineErr(lReturn))
  880.             continue;
  881.         else
  882.         {
  883.             OutputDebugString("lineShutdown unhandled errorn");
  884.             break;
  885.         }
  886.     }
  887.     while(lReturn != SUCCESS);
  888.     m_bTapiInUse = FALSE;
  889.     m_hLineApp = NULL;
  890.     m_hCall = NULL;
  891.     m_hLine = NULL;
  892.     m_bShuttingDown = FALSE;
  893.     OutputDebugString("TAPI uninitialized.n");
  894.     return TRUE;
  895. }