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

TAPI编程

开发平台:

Visual C++

  1.             if (HandleLineErr(lReturn))
  2.                 continue;
  3.             else
  4.             {
  5.                 OutputDebugLineError(lReturn, 
  6.                     "lineGetDevCaps unhandled error: ");
  7.                 LocalFree(lpLineDevCaps);
  8.                 return NULL;
  9.             }
  10.         }
  11.         while (lReturn != SUCCESS);
  12.         // If the buffer was big enough, then succeed.
  13.         if ((lpLineDevCaps -> dwNeededSize) <= (lpLineDevCaps -> dwTotalSize))
  14.             return lpLineDevCaps;
  15.         // Buffer wasn't big enough.  Make it bigger and try again.
  16.         sizeofLineDevCaps = lpLineDevCaps -> dwNeededSize;
  17.     }
  18. }
  19. //
  20. //  FUNCTION: I_lineGetAddressStatus(LPLINEADDRESSSTATUS, HLINE, DWORD)
  21. //
  22. //  PURPOSE: Retrieve a LINEADDRESSSTATUS structure for the specified line.
  23. //
  24. //  PARAMETERS:
  25. //    lpLineAddressStatus - Pointer to a LINEADDRESSSTATUS structure to use.
  26. //    hLine       - Handle of line to get the AddressStatus of.
  27. //    dwAddressID - Address ID on the hLine to be used.
  28. //
  29. //  RETURN VALUE:
  30. //    Returns a pointer to a LINEADDRESSSTATUS structure if successful.
  31. //    Returns NULL if unsuccessful.
  32. //
  33. //  COMMENTS:
  34. //
  35. //    This function is a wrapper around lineGetAddressStatus to make it easy
  36. //    to handle the variable sized structure and any errors received.
  37. //
  38. //    The returned structure has been allocated with LocalAlloc,
  39. //    so LocalFree has to be called on it when you're finished with it,
  40. //    or there will be a memory leak.
  41. //
  42. //    Similarly, if a lpLineAddressStatus structure is passed in, it *must*
  43. //    have been allocated with LocalAlloc and it could potentially be 
  44. //    LocalFree()d.
  45. //
  46. //    If lpLineAddressStatus == NULL, then a new structure is allocated.  It
  47. //    is normal to pass in NULL for this parameter unless you want to use a
  48. //    lpLineAddressStatus that has been returned by previous
  49. //    I_lineGetAddressStatus call.
  50. //
  51. //
  52. LPLINEADDRESSSTATUS I_lineGetAddressStatus(
  53.     LPLINEADDRESSSTATUS lpLineAddressStatus,
  54.     HLINE hLine, DWORD dwAddressID)
  55. {
  56.     size_t sizeofLineAddressStatus = sizeof(LINEADDRESSSTATUS) + 1024;
  57.     long lReturn;
  58.     
  59.     // Continue this loop until the structure is big enough.
  60.     while(TRUE)
  61.     {
  62.         // Make sure the buffer exists, is valid and big enough.
  63.         lpLineAddressStatus = 
  64.             (LPLINEADDRESSSTATUS) CheckAndReAllocBuffer(
  65.                 (LPVOID) lpLineAddressStatus,
  66.                 sizeofLineAddressStatus,     
  67.                 "lineGetAddressStatus");     
  68.         if (lpLineAddressStatus == NULL)
  69.             return NULL;
  70.         // Make the call to fill the structure.
  71.         do
  72.         {            
  73.             lReturn = 
  74.                 lineGetAddressStatus(hLine, dwAddressID, lpLineAddressStatus);
  75.             if (HandleLineErr(lReturn))
  76.                 continue;
  77.             else
  78.             {
  79.                 OutputDebugLineError(lReturn, 
  80.                     "lineGetAddressStatus unhandled error: ");
  81.                 LocalFree(lpLineAddressStatus);
  82.                 return NULL;
  83.             }
  84.         }
  85.         while (lReturn != SUCCESS);
  86.         // If the buffer was big enough, then succeed.
  87.         if ((lpLineAddressStatus -> dwNeededSize) <= 
  88.             (lpLineAddressStatus -> dwTotalSize))
  89.         {
  90.             return lpLineAddressStatus;
  91.         }
  92.         
  93.         // Buffer wasn't big enough.  Make it bigger and try again.
  94.         sizeofLineAddressStatus = lpLineAddressStatus -> dwNeededSize;
  95.     }
  96. }
  97. //
  98. //  FUNCTION: I_lineGetCallStatus(LPLINECALLSTATUS, HCALL)
  99. //
  100. //  PURPOSE: Retrieve a LINECALLSTATUS structure for the specified line.
  101. //
  102. //  PARAMETERS:
  103. //    lpLineCallStatus - Pointer to a LINECALLSTATUS structure to use.
  104. //    hCall - Handle of call to get the CallStatus of.
  105. //
  106. //  RETURN VALUE:
  107. //    Returns a pointer to a LINECALLSTATUS structure if successful.
  108. //    Returns NULL if unsuccessful.
  109. //
  110. //  COMMENTS:
  111. //
  112. //    This function is a wrapper around lineGetCallStatus to make it easy
  113. //    to handle the variable sized structure and any errors received.
  114. //
  115. //    The returned structure has been allocated with LocalAlloc,
  116. //    so LocalFree has to be called on it when you're finished with it,
  117. //    or there will be a memory leak.
  118. //
  119. //    Similarly, if a lpLineCallStatus structure is passed in, it *must*
  120. //    have been allocated with LocalAlloc and it could potentially be 
  121. //    LocalFree()d.
  122. //
  123. //    If lpLineCallStatus == NULL, then a new structure is allocated.  It
  124. //    is normal to pass in NULL for this parameter unless you want to use a
  125. //    lpLineCallStatus that has been returned by previous I_lineGetCallStatus
  126. //    call.
  127. //
  128. //
  129. LPLINECALLSTATUS I_lineGetCallStatus(
  130.     LPLINECALLSTATUS lpLineCallStatus,
  131.     HCALL hCall)
  132. {
  133.     size_t sizeofLineCallStatus = sizeof(LINECALLSTATUS) + 1024;
  134.     long lReturn;
  135.     
  136.     // Continue this loop until the structure is big enough.
  137.     while(TRUE)
  138.     {
  139.         // Make sure the buffer exists, is valid and big enough.
  140.         lpLineCallStatus = 
  141.             (LPLINECALLSTATUS) CheckAndReAllocBuffer(
  142.                 (LPVOID) lpLineCallStatus,
  143.                 sizeofLineCallStatus,     
  144.                 "lineGetCallStatus");     
  145.         if (lpLineCallStatus == NULL)
  146.             return NULL;
  147.             
  148.         // Make the call to fill the structure.
  149.         do
  150.         {
  151.             lReturn = 
  152.                 lineGetCallStatus(hCall, lpLineCallStatus);
  153.             if (HandleLineErr(lReturn))
  154.                 continue;
  155.             else
  156.             {
  157.                 OutputDebugLineError(lReturn, 
  158.                     "lineGetCallStatus unhandled error: ");
  159.                 LocalFree(lpLineCallStatus);
  160.                 return NULL;
  161.             }
  162.         }
  163.         while (lReturn != SUCCESS);
  164.         // If the buffer was big enough, then succeed.
  165.         if ((lpLineCallStatus -> dwNeededSize) <= 
  166.             (lpLineCallStatus -> dwTotalSize))
  167.         {
  168.             return lpLineCallStatus;
  169.         }
  170.         // Buffer wasn't big enough.  Make it bigger and try again.
  171.         sizeofLineCallStatus = lpLineCallStatus -> dwNeededSize;
  172.     }
  173. }
  174. //
  175. //  FUNCTION: I_lineTranslateAddress
  176. //              (LPLINETRANSLATEOUTPUT, DWORD, DWORD, LPCSTR)
  177. //
  178. //  PURPOSE: Retrieve a LINECALLSTATUS structure for the specified line.
  179. //
  180. //  PARAMETERS:
  181. //    lpLineTranslateOutput - Pointer to a LINETRANSLATEOUTPUT structure.
  182. //    dwDeviceID      - Device that we're translating for.
  183. //    dwAPIVersion    - API Version to use.
  184. //    lpszDialAddress - pointer to the DialAddress string to translate.
  185. //
  186. //  RETURN VALUE:
  187. //    Returns a pointer to a LINETRANSLATEOUTPUT structure if successful.
  188. //    Returns NULL if unsuccessful.
  189. //
  190. //  COMMENTS:
  191. //
  192. //    This function is a wrapper around lineGetTranslateOutput to make it
  193. //    easy to handle the variable sized structure and any errors received.
  194. //
  195. //    The returned structure has been allocated with LocalAlloc,
  196. //    so LocalFree has to be called on it when you're finished with it,
  197. //    or there will be a memory leak.
  198. //
  199. //    Similarly, if a lpLineTranslateOutput structure is passed in, it
  200. //    *must* have been allocated with LocalAlloc and it could potentially be 
  201. //    LocalFree()d.
  202. //
  203. //    If lpLineTranslateOutput == NULL, then a new structure is allocated.
  204. //    It is normal to pass in NULL for this parameter unless you want to use
  205. //    a lpLineTranslateOutput that has been returned by previous 
  206. //    I_lineTranslateOutput call.
  207. //
  208. //
  209. LPLINETRANSLATEOUTPUT I_lineTranslateAddress(
  210.     LPLINETRANSLATEOUTPUT lpLineTranslateOutput,
  211.     DWORD dwDeviceID, DWORD dwAPIVersion,
  212.     LPCSTR lpszDialAddress)
  213. {
  214.     size_t sizeofLineTranslateOutput = sizeof(LINETRANSLATEOUTPUT) + 1024;
  215.     long lReturn;
  216.     
  217.     // Continue this loop until the structure is big enough.
  218.     while(TRUE)
  219.     {
  220.         // Make sure the buffer exists, is valid and big enough.
  221.         lpLineTranslateOutput = 
  222.             (LPLINETRANSLATEOUTPUT) CheckAndReAllocBuffer(
  223.                 (LPVOID) lpLineTranslateOutput,
  224.                 sizeofLineTranslateOutput,
  225.                 "lineTranslateOutput");
  226.         if (lpLineTranslateOutput == NULL)
  227.             return NULL;
  228.         // Make the call to fill the structure.
  229.         do
  230.         {
  231.             // Note that CALLWAITING is disabled 
  232.             // (assuming the service provider can disable it)
  233.             lReturn = 
  234.                 lineTranslateAddress(g_hLineApp, dwDeviceID, dwAPIVersion,
  235.                     lpszDialAddress, 0, 
  236.                     LINETRANSLATEOPTION_CANCELCALLWAITING,
  237.                     lpLineTranslateOutput);
  238.             // If the address isn't translatable, notify the user.
  239.             if (lReturn == LINEERR_INVALADDRESS)
  240.                 MessageBox(g_hDlgParentWindow, 
  241.                     "Unable to translate phone number","Warning",MB_OK);
  242.             if (HandleLineErr(lReturn))
  243.                 continue;
  244.             else
  245.             {
  246.                 OutputDebugLineError(lReturn, 
  247.                     "lineTranslateOutput unhandled error: ");
  248.                 LocalFree(lpLineTranslateOutput);
  249.                 return NULL;
  250.             }
  251.         }
  252.         while (lReturn != SUCCESS);
  253.         // If the buffer was big enough, then succeed.
  254.         if ((lpLineTranslateOutput -> dwNeededSize) <= 
  255.             (lpLineTranslateOutput -> dwTotalSize))
  256.         {
  257.             return lpLineTranslateOutput;
  258.         }
  259.         // Buffer wasn't big enough.  Make it bigger and try again.
  260.         sizeofLineTranslateOutput = lpLineTranslateOutput -> dwNeededSize;
  261.     }
  262. }
  263. //
  264. //  FUNCTION: I_lineGetAddressCaps(LPLINEADDRESSCAPS, ..)
  265. //
  266. //  PURPOSE: Retrieve a LINEADDRESSCAPS structure for the specified line.
  267. //
  268. //  PARAMETERS:
  269. //    lpLineAddressCaps - Pointer to a LINEADDRESSCAPS, or NULL.
  270. //    dwDeviceID        - Device to get the address caps for.
  271. //    dwAddressID       - This sample always assumes the first address.
  272. //    dwAPIVersion      - API version negotiated for the device.
  273. //    dwExtVersion      - Always 0 for this sample.
  274. //
  275. //  RETURN VALUE:
  276. //    Returns a pointer to a LINEADDRESSCAPS structure if successful.
  277. //    Returns NULL if unsuccessful.
  278. //
  279. //  COMMENTS:
  280. //
  281. //    This function is a wrapper around lineGetAddressCaps to make it easy
  282. //    to handle the variable sized structure and any errors received.
  283. //
  284. //    The returned structure has been allocated with LocalAlloc,
  285. //    so LocalFree has to be called on it when you're finished with it,
  286. //    or there will be a memory leak.
  287. //
  288. //    Similarly, if a lpLineAddressCaps structure is passed in, it *must*
  289. //    have been allocated with LocalAlloc and it could potentially be 
  290. //    LocalFree()d.  It also *must* have the dwTotalSize field set.
  291. //
  292. //    If lpLineAddressCaps == NULL, then a new structure is allocated.  It
  293. //    is normal to pass in NULL for this parameter unless you want to use a
  294. //    lpLineCallStatus that has been returned by previous I_lineGetAddressCaps
  295. //    call.
  296. //
  297. //
  298. LPLINEADDRESSCAPS I_lineGetAddressCaps (
  299.     LPLINEADDRESSCAPS lpLineAddressCaps,
  300.     DWORD dwDeviceID, DWORD dwAddressID,
  301.     DWORD dwAPIVersion, DWORD dwExtVersion)
  302. {
  303.     size_t sizeofLineAddressCaps = sizeof(LINEADDRESSCAPS) + 1024;
  304.     long lReturn;
  305.     
  306.     // Continue this loop until the structure is big enough.
  307.     while(TRUE)
  308.     {
  309.         // Make sure the buffer exists, is valid and big enough.
  310.         lpLineAddressCaps = 
  311.             (LPLINEADDRESSCAPS) CheckAndReAllocBuffer(
  312.                 (LPVOID) lpLineAddressCaps,
  313.                 sizeofLineAddressCaps,
  314.                 "lineGetAddressCaps");
  315.         if (lpLineAddressCaps == NULL)
  316.             return NULL;
  317.             
  318.         // Make the call to fill the structure.
  319.         do
  320.         {
  321.             lReturn = 
  322.                 lineGetAddressCaps(g_hLineApp,
  323.                     dwDeviceID, dwAddressID, dwAPIVersion, dwExtVersion,
  324.                     lpLineAddressCaps);
  325.             if (HandleLineErr(lReturn))
  326.                 continue;
  327.             else
  328.             {
  329.                 OutputDebugLineError(lReturn, 
  330.                     "lineGetAddressCaps unhandled error: ");
  331.                 LocalFree(lpLineAddressCaps);
  332.                 return NULL;
  333.             }
  334.         }
  335.         while (lReturn != SUCCESS);
  336.         // If the buffer was big enough, then succeed.
  337.         if ((lpLineAddressCaps -> dwNeededSize) <= 
  338.             (lpLineAddressCaps -> dwTotalSize))
  339.         {
  340.             return lpLineAddressCaps;
  341.         }
  342.         // Buffer wasn't big enough.  Make it bigger and try again.
  343.         sizeofLineAddressCaps = lpLineAddressCaps -> dwNeededSize;
  344.     }
  345. }
  346. //**************************************************
  347. // LINEERR Error Handlers
  348. //**************************************************
  349. //
  350. //  FUNCTION: HandleLineErr(long)
  351. //
  352. //  PURPOSE: Handle several standard LINEERR errors
  353. //
  354. //  PARAMETERS:
  355. //    lLineErr - Error code to be handled.
  356. //
  357. //  RETURN VALUE:
  358. //    Return TRUE if lLineErr wasn't an error, or if the
  359. //      error was successfully handled and cleared up.
  360. //    Return FALSE if lLineErr was an unhandled error.
  361. //
  362. //  COMMENTS:
  363. //
  364. //    This is the main error handler for all TAPI line APIs.
  365. //    It handles (by correcting or just notifying the user)
  366. //    most of the errors that can occur while using TAPI line APIs.
  367. //
  368. //    Note that many errors still return FALSE (unhandled) even
  369. //    if a dialog is displayed.  Often, the dialog is just notifying
  370. //    the user why the action was canceled.
  371. //    
  372. //
  373. //
  374. BOOL HandleLineErr(long lLineErr)
  375. {
  376.     // lLineErr is really an async request ID, not an error.
  377.     if (lLineErr > SUCCESS)
  378.         return FALSE;
  379.     // All we do is dispatch the correct error handler.
  380.     switch(lLineErr)
  381.     {
  382.         case SUCCESS:
  383.             return TRUE;
  384.         case LINEERR_INVALCARD:
  385.         case LINEERR_INVALLOCATION:
  386.         case LINEERR_INIFILECORRUPT:
  387.             return HandleIniFileCorrupt();
  388.         case LINEERR_NODRIVER:
  389.             return HandleNoDriver();
  390.         case LINEERR_REINIT:
  391.             return HandleReInit();
  392.         case LINEERR_NOMULTIPLEINSTANCE:
  393.             return HandleNoMultipleInstance();
  394.         case LINEERR_NOMEM:
  395.             return HandleNoMem();
  396.         case LINEERR_OPERATIONFAILED:
  397.             return HandleOperationFailed();
  398.         case LINEERR_RESOURCEUNAVAIL:
  399.             return HandleResourceUnavail();
  400.         // Unhandled errors fail.
  401.         default:
  402.             return FALSE;
  403.     }
  404. }
  405. //
  406. //  FUNCTION: HandleIniFileCorrupt
  407. //
  408. //  PURPOSE: Handle INIFILECORRUPT error.
  409. //
  410. //  PARAMETERS:
  411. //    none
  412. //
  413. //  RETURN VALUE:
  414. //    TRUE  - error was corrected.
  415. //    FALSE - error was not corrected.
  416. //
  417. //  COMMENTS:
  418. //
  419. //    This error shouldn't happen under Windows 95 anymore.  The TAPI.DLL
  420. //    takes care of correcting this problem.  If it does happen, just
  421. //    notify the user.
  422. //
  423. BOOL HandleIniFileCorrupt()
  424. {
  425.     if (IDCANCEL == MessageBox(g_hDlgParentWindow, 
  426.         "Configuration information relating to Telephony "
  427.         "services appears to be corrupt.n"
  428.         "This could be the first time you have used the Telephony services.n"
  429.         "Would you like to configure the Telephony services?",
  430.         "Warning",MB_OKCANCEL))
  431.     {
  432.         return FALSE;
  433.     }
  434.     lineTranslateDialog(g_hLineApp, 0, SAMPLE_TAPI_VERSION, 
  435.         g_hDlgParentWindow, NULL);
  436.     return TRUE;
  437. }
  438. //
  439. //  FUNCTION: HandleNoDriver
  440. //
  441. //  PURPOSE: Handle NODRIVER error.
  442. //
  443. //  PARAMETERS:
  444. //    none
  445. //
  446. //  RETURN VALUE:
  447. //    TRUE  - error was corrected.
  448. //    FALSE - error was not corrected.
  449. //
  450. //  COMMENTS:
  451. //
  452. //
  453. BOOL HandleNoDriver()
  454. {
  455.     int mbRet;
  456.     mbRet = MessageBox(g_hDlgParentWindow, 
  457.         "One of the components of the Telephony device driver is missing.n"
  458.             "Use the Control Panel to set up the driver properly.",
  459.         "Warning",MB_OK);
  460.     return FALSE;
  461. }
  462. //
  463. //  FUNCTION: HandleNoMultipleInstance
  464. //
  465. //  PURPOSE: Handle NOMULTIPLEINSTANCE error.
  466. //
  467. //  PARAMETERS:
  468. //    none
  469. //
  470. //  RETURN VALUE:
  471. //    TRUE  - error was corrected.
  472. //    FALSE - error was not corrected.
  473. //
  474. //  COMMENTS:
  475. //
  476. //
  477. BOOL HandleNoMultipleInstance()
  478. {
  479.     MessageBox(g_hDlgParentWindow, 
  480.         "You have two copies of the same Telephony driver installed.n"
  481.             "Use the Control Panel to remove one of the copies.",
  482.         "Warning",MB_OK);
  483.         
  484.     return FALSE;
  485. }
  486. //
  487. //  FUNCTION: HandleReInit
  488. //
  489. //  PURPOSE: Handle REINIT error.
  490. //
  491. //  PARAMETERS:
  492. //    none
  493. //
  494. //  RETURN VALUE:
  495. //    TRUE  - error was corrected.
  496. //    FALSE - error was not corrected.
  497. //
  498. //  COMMENTS:
  499. //
  500. //
  501. BOOL HandleReInit()
  502. {
  503.     ShutdownTAPI();
  504.     return FALSE;
  505. }
  506. //
  507. //  FUNCTION: HandleNoMem
  508. //
  509. //  PURPOSE: Handle NOMEM error.
  510. //
  511. //  PARAMETERS:
  512. //    none
  513. //
  514. //  RETURN VALUE:
  515. //    TRUE  - error was corrected.
  516. //    FALSE - error was not corrected.
  517. //
  518. //  COMMENTS:
  519. //    This is also called if I run out of memory for LocalAlloc()s
  520. //
  521. //
  522. BOOL HandleNoMem()
  523. {
  524.     MessageBox(g_hDlgParentWindow,
  525.         "Out of Memory error, canceling action.","Error", MB_OK);
  526.     return FALSE;
  527. }
  528. //
  529. //  FUNCTION: HandleOperationFailed
  530. //
  531. //  PURPOSE: Handle OPERATIONFAILED error.
  532. //
  533. //  PARAMETERS:
  534. //    none
  535. //
  536. //  RETURN VALUE:
  537. //    TRUE  - error was corrected.
  538. //    FALSE - error was not corrected.
  539. //
  540. //  COMMENTS:
  541. //
  542. //
  543. BOOL HandleOperationFailed()
  544. {
  545.     MessageBox(g_hDlgParentWindow,
  546.         "TAPI Operation Failed for unknown reasons.",
  547.         "Error", MB_OK);
  548.     return FALSE;
  549. }
  550. //
  551. //  FUNCTION: HandleResourceUnavail
  552. //
  553. //  PURPOSE: Handle RESOURCEUNAVAIL error.
  554. //
  555. //  PARAMETERS:
  556. //    none
  557. //
  558. //  RETURN VALUE:
  559. //    TRUE  - error was corrected.
  560. //    FALSE - error was not corrected.
  561. //
  562. //  COMMENTS:
  563. //
  564. //
  565. BOOL HandleResourceUnavail()
  566. {
  567.     int mbRet;
  568.     mbRet = MessageBox(g_hDlgParentWindow,
  569.         "A Telephony resource is temporarily unavaiable.  "
  570.         "This could mean a short wait is necessary or "
  571.         "that a non-TAPI application is using the line.",
  572.         "Warning",MB_RETRYCANCEL);
  573.     if (mbRet == IDRETRY)
  574.         return TRUE;
  575.     return FALSE;
  576. }
  577. //
  578. //  FUNCTION: HandleNoDevicesInstalled
  579. //
  580. //  PURPOSE: Handle cases when we know NODEVICE error
  581. //    is returned because there are no devices installed.
  582. //
  583. //  PARAMETERS:
  584. //    none
  585. //
  586. //  RETURN VALUE:
  587. //    TRUE  - error was corrected.
  588. //    FALSE - error was not corrected.
  589. //
  590. //  COMMENTS:
  591. //
  592. //    This function is not part of standard error handling
  593. //    but is only used when we know that the NODEVICE error
  594. //    means that no devices are installed.
  595. //
  596. //
  597. BOOL HandleNoDevicesInstalled()
  598. {
  599.     int mbRet;
  600.     mbRet = MessageBox(g_hDlgParentWindow, 
  601.        "There are no devices configured for Telephony use.n"
  602.        "Would you like to run the Modem Control Panel to add a modem driver?",
  603.        "Warning",MB_YESNO);
  604.     if (mbRet == IDYES)
  605.     {
  606.         if (LaunchModemControlPanelAdd())
  607.             return TRUE;
  608.     }
  609.         
  610.     return FALSE;
  611. }
  612. //
  613. //  FUNCTION: LaunchModemControlPanelAdd
  614. //
  615. //  PURPOSE: Launch Add Modem Control Panel applet.
  616. //
  617. //  PARAMETERS:
  618. //    none
  619. //
  620. //  RETURN VALUE:
  621. //    TRUE  - Control Panel launched successfully.
  622. //    FALSE - It didn't.
  623. //
  624. //  COMMENTS:
  625. //
  626. //
  627. BOOL LaunchModemControlPanelAdd()
  628. {
  629.     PROCESS_INFORMATION piProcInfo;
  630.     STARTUPINFO siStartupInfo;
  631.     siStartupInfo.cb = sizeof(STARTUPINFO);
  632.     siStartupInfo.lpReserved = NULL;
  633.     siStartupInfo.lpDesktop = NULL;
  634.     siStartupInfo.lpTitle = NULL;
  635.     siStartupInfo.dwFlags = STARTF_USESHOWWINDOW;
  636.     siStartupInfo.wShowWindow = SW_SHOWNORMAL;
  637.     siStartupInfo.cbReserved2 = 0;
  638.     siStartupInfo.lpReserved2 = NULL;
  639.     
  640.     // The string to launch the modem control panel is *VERY* likely
  641.     // to change on NT.  If nothing else, this is 'contrl32' on NT
  642.     // instead of 'control'.
  643.     if (CreateProcess(
  644.             NULL,
  645.             "CONTROL.EXE MODEM.CPL,,ADD",
  646.             NULL, NULL, FALSE, 
  647.             NORMAL_PRIORITY_CLASS,
  648.             NULL, NULL, 
  649.             &siStartupInfo, 
  650.             &piProcInfo))
  651.     {
  652.         CloseHandle(piProcInfo.hThread);
  653.         // Control panel 'Add New Modem' has been launched.  Now we should
  654.         // wait for it to go away before continueing.
  655.         // If we WaitForSingleObject for the control panel to exit, then we
  656.         // get into a deadlock situation if we need to respond to any messages
  657.         // from the control panel.
  658.         
  659.         // If we use a PeekMessage loop to wait, we run into
  660.         // message re-entrancy problems.  (The user can get back to our UI
  661.         // and click 'dial' again).
  662.         // Instead, we take the easy way out and return FALSE to abort
  663.         // the current operation.
  664.     
  665.         CloseHandle(piProcInfo.hProcess);
  666.     }
  667.     else
  668.     {
  669.         OutputDebugLastError(GetLastError(), 
  670.             "Unable to LaunchModemControlPanelAdd: ");
  671.         MessageBox(g_hDlgParentWindow, 
  672.             "Unable to launch the Modem Control Panel",
  673.             "Warning", MB_OK);
  674.     }
  675.     
  676.     return FALSE;
  677. }
  678. //
  679. //  FUNCTION: WarningBox(LPCSTR)
  680. //
  681. //  PURPOSE: Prints a warning box when conditions remove a line in use.
  682. //
  683. //  PARAMETERS:
  684. //    lpszMessage - String that specifies why the line was removed form use.
  685. //    
  686. //  RETURN VALUE:
  687. //    none
  688. //
  689. //  COMMENTS:
  690. //
  691. //    If there is a call in progress on the line removed, then display a message
  692. //    specifying why the line was removed and that the call is being canceled.
  693. //
  694. //
  695. void WarningBox(LPCSTR lpszMessage)
  696. {
  697.     char szBuff[1024];
  698.     strcpy(szBuff, lpszMessage);
  699.     // If there is a call open, tell user we're going to close it.
  700.     if (g_hCall)
  701.         strcat(szBuff, "nClosing existing call.");
  702.     MessageBox(g_hDlgParentWindow, szBuff, "Warning", MB_OK);
  703.     strcat(szBuff, "rn");
  704.     OutputDebugString(szBuff);
  705. }
  706. //**************************************************
  707. //
  708. // All the functions from this point on are used solely by the "Dial" dialog.
  709. // This dialog is used to get both the 'phone number' address, 
  710. // the line device to be used as well as allow the user to configure
  711. // dialing properties and the line device.
  712. //
  713. //**************************************************
  714. //
  715. //  FUNCTION: DWORD I_lineNegotiateLegacyAPIVersion(DWORD)
  716. //
  717. //  PURPOSE: Negotiate an API Version to use for a specific device.
  718. //
  719. //  PARAMETERS:
  720. //    dwDeviceID - device to negotiate an API Version for.
  721. //
  722. //  RETURN VALUE:
  723. //    Returns the API Version to use for this line if successful.
  724. //    Returns 0 if negotiations fall through.
  725. //
  726. //  COMMENTS:
  727. //
  728. //    This wrapper is slightly different from the I_lineNegotiateAPIVersion.
  729. //    This wrapper allows TapiComm to negotiate an API version between
  730. //    1.3 and SAMPLE_TAPI_VERSION.  Normally, this sample is specific to 
  731. //    API Version SAMPLE_TAPI_VERSION.  However, there are a few times when
  732. //    TapiComm needs to get information from a service provider, but also knows
  733. //    that a lower API Version would be ok.  This allows TapiComm to recognize
  734. //    legacy service providers even though it can't use them.  1.3 is the
  735. //    lowest API Version a legacy service provider should support.
  736. //
  737. //
  738. DWORD I_lineNegotiateLegacyAPIVersion(DWORD dwDeviceID)
  739. {
  740.     LINEEXTENSIONID LineExtensionID;
  741.     long lReturn;
  742.     DWORD dwLocalAPIVersion;
  743.     
  744.     do
  745.     {
  746.         lReturn = lineNegotiateAPIVersion(g_hLineApp, dwDeviceID, 
  747.             0x00010003, SAMPLE_TAPI_VERSION,
  748.             &dwLocalAPIVersion, &LineExtensionID);
  749.         if (lReturn == LINEERR_INCOMPATIBLEAPIVERSION)
  750.         {
  751.             OutputDebugString("INCOMPATIBLEAPIVERSION in Dial Dialog.n");
  752.             return 0;
  753.         }
  754.         if (HandleLineErr(lReturn))
  755.             continue;
  756.         else
  757.         {
  758.             OutputDebugLineError(lReturn, 
  759.                 "lineNegotiateAPIVersion in Dial Dialog unhandled error: ");
  760.             return 0;
  761.         }
  762.     }
  763.     while(lReturn != SUCCESS);
  764.         
  765.     return dwLocalAPIVersion;
  766. }
  767. //
  768. //  FUNCTION: long VerifyUsableLine(DWORD)
  769. //
  770. //  PURPOSE: Verifies that a specific line device is useable by TapiComm.
  771. //
  772. //  PARAMETERS:
  773. //    dwDeviceID - The ID of the line device to be verified
  774. //
  775. //  RETURN VALUE:
  776. //    Returns SUCCESS if dwDeviceID is a usable line device.
  777. //    Returns a LINENOTUSEABLE_ constant otherwise.
  778. //
  779. //  COMMENTS:
  780. //
  781. //    VerifyUsableLine takes the give device ID and verifies step by step
  782. //    that the device supports all the features that TapiComm requires.
  783. //
  784. //
  785. long VerifyUsableLine(DWORD dwDeviceID)
  786. {
  787.     LPLINEDEVCAPS lpLineDevCaps = NULL;
  788.     LPLINEADDRESSSTATUS lpLineAddressStatus = NULL;
  789.     LPVARSTRING lpVarString = NULL;
  790.     DWORD dwAPIVersion;
  791.     long lReturn;
  792.     long lUsableLine = SUCCESS;
  793.     HLINE hLine = 0;
  794.     OutputDebugPrintf("Testing Line ID '0x%lx'",dwDeviceID);
  795.     // The line device must support an API Version that TapiComm does.
  796.     dwAPIVersion = I_lineNegotiateAPIVersion(dwDeviceID);
  797.     if (dwAPIVersion == 0)
  798.         return LINENOTUSEABLE_ERROR;
  799.     lpLineDevCaps = I_lineGetDevCaps(lpLineDevCaps,
  800.         dwDeviceID, dwAPIVersion);
  801.     if (lpLineDevCaps == NULL)
  802.         return LINENOTUSEABLE_ERROR;
  803.     // Must support LINEBEARERMODE_VOICE
  804.     if (!(lpLineDevCaps->dwBearerModes & LINEBEARERMODE_VOICE ))
  805.     {
  806.         lUsableLine = LINENOTUSEABLE_NOVOICE;
  807.         OutputDebugString("LINEBEARERMODE_VOICE not supportedn");
  808.         goto DeleteBuffers;
  809.     }
  810.     // Must support LINEMEDIAMODE_DATAMODEM
  811.     if (!(lpLineDevCaps->dwMediaModes & LINEMEDIAMODE_DATAMODEM))
  812.     {
  813.         lUsableLine = LINENOTUSEABLE_NODATAMODEM;
  814.         OutputDebugString("LINEMEDIAMODE_DATAMODEM not supportedn");
  815.         goto DeleteBuffers;
  816.     }
  817.     // Must be able to make calls
  818.     if (!(lpLineDevCaps->dwLineFeatures & LINEFEATURE_MAKECALL))
  819.     {
  820.         lUsableLine = LINENOTUSEABLE_NOMAKECALL;
  821.         OutputDebugString("LINEFEATURE_MAKECALL not supportedn");
  822.         goto DeleteBuffers;
  823.     }
  824.     // It is necessary to open the line so we can check if 
  825.     // there are any call appearances available.  Other TAPI
  826.     // applications could be using all call appearances.
  827.     // Opening the line also checks for other possible problems.
  828.     do
  829.     {
  830.         lReturn = lineOpen(g_hLineApp, dwDeviceID, &hLine,
  831.             dwAPIVersion, 0, 0,
  832.             LINECALLPRIVILEGE_NONE, LINEMEDIAMODE_DATAMODEM,
  833.             0);
  834.         if(lReturn == LINEERR_ALLOCATED)
  835.         {
  836.             OutputDebugString(
  837.                 "Line is already in use by a non-TAPI app or"
  838.                 " another Service Provider.n");
  839.             lUsableLine = LINENOTUSEABLE_ALLOCATED;
  840.             goto DeleteBuffers;
  841.         }
  842.         if (HandleLineErr(lReturn))
  843.             continue;
  844.         else
  845.         {
  846.             OutputDebugLineError(lReturn, "lineOpen unhandled error: ");
  847.             lUsableLine = LINENOTUSEABLE_ERROR;
  848.             goto DeleteBuffers;
  849.         }
  850.     }
  851.     while(lReturn != SUCCESS);
  852.     // Get LineAddressStatus to make sure the line isn't already in use.
  853.     lpLineAddressStatus = 
  854.         I_lineGetAddressStatus(lpLineAddressStatus, hLine, 0);
  855.         
  856.     if (lpLineAddressStatus == NULL)
  857.     {
  858.         lUsableLine = LINENOTUSEABLE_ERROR;
  859.         goto DeleteBuffers;
  860.     }
  861.     // Are there any available call appearances (ie: is it in use)?
  862.     if ( !((lpLineAddressStatus -> dwAddressFeatures) &
  863.            LINEADDRFEATURE_MAKECALL) )
  864.     {
  865.         OutputDebugString("LINEADDRFEATURE_MAKECALL not availablen");
  866.         lUsableLine = LINENOTUSEABLE_INUSE;
  867.         goto DeleteBuffers;
  868.     }
  869.     // Make sure the "comm/datamodem" device class is supported
  870.     // Note that we don't want any of the 'extra' information
  871.     // normally returned in the VARSTRING structure.  All we care
  872.     // about is if lineGetID succeeds.
  873.     do
  874.     {
  875.         lpVarString = CheckAndReAllocBuffer((LPVOID) lpVarString,
  876.             sizeof(VARSTRING),"VerifyUsableLine:lineGetID: ");
  877.         if (lpVarString == NULL)
  878.         {
  879.             lUsableLine = LINENOTUSEABLE_ERROR;
  880.             goto DeleteBuffers;
  881.         }
  882.         lReturn = lineGetID(hLine, 0, 0, LINECALLSELECT_LINE,
  883.             lpVarString, "comm/datamodem");
  884.         if (HandleLineErr(lReturn))
  885.             continue;
  886.         else
  887.         {
  888.             OutputDebugLineError(lReturn, 
  889.                 "lineGetID unhandled error: ");
  890.             lUsableLine = LINENOTUSEABLE_NOCOMMDATAMODEM;
  891.             goto DeleteBuffers;
  892.         }
  893.     }
  894.     while(lReturn != SUCCESS);
  895.     OutputDebugString("Line is suitable and available for use.n");
  896.   DeleteBuffers:
  897.     if (hLine)
  898.         lineClose(hLine);
  899.     if (lpLineAddressStatus)
  900.         LocalFree(lpLineAddressStatus);
  901.     if (lpLineDevCaps)
  902.         LocalFree(lpLineDevCaps);
  903.     if (lpVarString)
  904.         LocalFree(lpVarString);
  905.     return lUsableLine;
  906. }
  907. //
  908. //  FUNCTION: void FillTAPILine(HWND)
  909. //
  910. //  PURPOSE: Fills the 'TAPI Line' control with the available line devices.
  911. //
  912. //  PARAMETERS: 
  913. //    hwndDlg - handle to the current "Dial" dialog 
  914. //
  915. //  RETURN VALUE:
  916. //    none
  917. //
  918. //  COMMENTS:
  919. //
  920. //    This function enumerates through all the TAPI line devices and
  921. //    queries each for the device name.  The device name is then put into
  922. //    the 'TAPI Line' control.  These device names are kept in order rather
  923. //    than sorted.  This allows "Dial" to know which device ID the user 
  924. //    selected just by the knowing the index of the selected string.
  925. //
  926. //    There are default values if there isn't a device name, if there is
  927. //    an error on the device, or if the device name is an empty string.
  928. //    The device name is also checked to make sure it is null terminated.
  929. //
  930. //    Note that a Legacy API Version is negotiated.  Since the fields in
  931. //    the LINEDEVCAPS structure that we are interested in haven't moved, we
  932. //    can negotiate a lower API Version than this sample is designed for
  933. //    and still be able to access the necessary structure members.
  934. //
  935. //    The first line that is usable by TapiComm is selected as the 'default'
  936. //    line.  Also note that if there was a previously selected line, this
  937. //    remains the default line.  This would likely only occur if this
  938. //    function is called after the dialog has initialized once; for example,
  939. //    if a new line is added.
  940. //
  941. //
  942. void FillTAPILine(HWND hwndDlg)
  943. {
  944.     DWORD dwDeviceID;
  945.     DWORD dwAPIVersion;
  946.     LPLINEDEVCAPS lpLineDevCaps = NULL;
  947.     char szLineUnavail[] = "Line Unavailable";
  948.     char szLineUnnamed[] = "Line Unnamed";
  949.     char szLineNameEmpty[] = "Line Name is Empty";
  950.     LPSTR lpszLineName;
  951.     long lReturn;
  952.     DWORD dwDefaultDevice = MAXDWORD;
  953.     // Make sure the control is empty.  If it isn't,
  954.     // hold onto the currently selected ID and then reset it.
  955.     if (SendDlgItemMessage(hwndDlg, IDC_TAPILINE, CB_GETCOUNT, 0, 0))
  956.     {
  957.         dwDefaultDevice = SendDlgItemMessage(hwndDlg, IDC_TAPILINE, 
  958.             CB_GETCURSEL, 0, 0);
  959.         SendDlgItemMessage(hwndDlg, IDC_TAPILINE, CB_RESETCONTENT, 0, 0);
  960.     }
  961.     for (dwDeviceID = 0; dwDeviceID < g_dwNumDevs; dwDeviceID ++)
  962.     {
  963.         dwAPIVersion = I_lineNegotiateLegacyAPIVersion(dwDeviceID);
  964.         if (dwAPIVersion)
  965.         {
  966.             lpLineDevCaps = I_lineGetDevCaps(lpLineDevCaps,
  967.                 dwDeviceID, dwAPIVersion);
  968.             if (lpLineDevCaps)
  969.             {
  970.                 if ((lpLineDevCaps -> dwLineNameSize) &&
  971.                     (lpLineDevCaps -> dwLineNameOffset) &&
  972.                     (lpLineDevCaps -> dwStringFormat == STRINGFORMAT_ASCII))
  973.                 {
  974.                     // This is the name of the device.
  975.                     lpszLineName = ((char *) lpLineDevCaps) + 
  976.                         lpLineDevCaps -> dwLineNameOffset;
  977.                     if (lpszLineName[0] != '')
  978.                     {
  979.         // Reverse indented to make this fit
  980.         // Make sure the device name is null terminated.
  981.         if (lpszLineName[lpLineDevCaps->dwLineNameSize -1] != '')
  982.         {
  983.             // If the device name is not null terminated, null
  984.             // terminate it.  Yes, this looses the end character.
  985.             // Its a bug in the service provider.
  986.             lpszLineName[lpLineDevCaps->dwLineNameSize-1] = '';
  987.             OutputDebugPrintf(
  988.                 "Device name for device 0x%lx is not null terminated.n",
  989.                 dwDeviceID);
  990.         }
  991.                     }
  992.                     else // Line name started with a NULL.
  993.                         lpszLineName = szLineNameEmpty;
  994.                 }
  995.                 else  // DevCaps doesn't have a valid line name.  Unnamed.
  996.                     lpszLineName = szLineUnnamed;
  997.             }
  998.             else  // Couldn't GetDevCaps.  Line is unavail.
  999.                 lpszLineName = szLineUnavail;
  1000.         }
  1001.         else  // Couldn't NegotiateAPIVersion.  Line is unavail.
  1002.             lpszLineName = szLineUnavail;
  1003.         // Put the device name into the control
  1004.         lReturn = SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
  1005.             CB_ADDSTRING, 0, (LPARAM) (LPCTSTR) lpszLineName);
  1006.         // If this line is usable and we don't have a default initial
  1007.         // line yet, make this the initial line.
  1008.         if ((lpszLineName != szLineUnavail) && 
  1009.             (dwDefaultDevice == MAXDWORD) &&
  1010.             (VerifyUsableLine(dwDeviceID) == SUCCESS))
  1011.         {
  1012.             dwDefaultDevice = dwDeviceID;
  1013.         }
  1014.     }
  1015.     if (lpLineDevCaps)
  1016.         LocalFree(lpLineDevCaps);
  1017.     if (dwDefaultDevice == MAXDWORD)
  1018.         dwDefaultDevice = 0;
  1019.     // Set the initial default line
  1020.     SendDlgItemMessage(hwndDlg, IDC_TAPILINE, 
  1021.         CB_SETCURSEL, dwDefaultDevice, 0);
  1022. }
  1023. //
  1024. //  FUNCTION: BOOL VerifyAndWarnUsableLine(HWND)
  1025. //
  1026. //  PURPOSE: Verifies the line device selected by the user.
  1027. //
  1028. //  PARAMETERS:
  1029. //    hwndDlg - The handle to the current "Dial" dialog.
  1030. //
  1031. //  RETURN VALUE:
  1032. //    Returns TRUE if the currently selected line device is useable
  1033. //      by TapiComm.  Returns FALSE if it isn't.
  1034. //
  1035. //  COMMENTS:
  1036. //
  1037. //    This function is very specific to the "Dial" dialog.  It gets
  1038. //    the device selected by the user from the 'TAPI Line' control and
  1039. //    VerifyUsableLine to make sure this line device is usable.  If the
  1040. //    line isn't useable, it notifies the user and disables the 'Dial'
  1041. //    button so that the user can't initiate a call with this line.
  1042. //
  1043. //    This function is also responsible for filling in the line specific
  1044. //    icon found on the "Dial" dialog.
  1045. //
  1046. //
  1047. BOOL VerifyAndWarnUsableLine(HWND hwndDlg)
  1048. {
  1049.     DWORD dwDeviceID;
  1050.     long lReturn;
  1051.     HICON hIcon = 0;
  1052.     HWND hControlWnd;
  1053.     // Get the selected line device.
  1054.     dwDeviceID = SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
  1055.                         CB_GETCURSEL, 0, 0);
  1056.     // Get the "comm" device icon associated with this line device.
  1057.     lReturn = lineGetIcon(dwDeviceID, "comm", &hIcon);
  1058.     if (lReturn == SUCCESS)
  1059.         SendDlgItemMessage(hwndDlg, IDC_LINEICON, STM_SETICON,
  1060.             (WPARAM) hIcon, 0);
  1061.     else
  1062.         // Any failure to get an icon makes us use the default icon.
  1063.         SendDlgItemMessage(hwndDlg, IDC_LINEICON, WM_SETTEXT,
  1064.             0, (LPARAM) (LPCTSTR) "TapiComm");
  1065. /*  // It turns out that TAPI will always return an icon, even if
  1066.     // the device class isn't supported by the TSP or even if the TSP
  1067.     // doesn't return any icons at all.  This code is unnecessary.
  1068.     // The only reason lineGetIcon would fail is due to resource problems.
  1069.     else
  1070.     {
  1071.         // If the line doesn't have a "comm" device icon, use its default one.
  1072.         lReturn = lineGetIcon(dwDeviceID, NULL, &hIcon);
  1073.         if (lReturn == SUCCESS)
  1074.         {
  1075.             OutputDebugString("Line doesn't support a "comm" icon.n");
  1076.             SendDlgItemMessage(hwndDlg, IDC_LINEICON, STM_SETICON,
  1077.                 (WPARAM) hIcon, 0);
  1078.         }
  1079.         else
  1080.         {
  1081.             // If lineGetIcon fails, just use TapiComms icon.
  1082.             OutputDebugLineError(lReturn, "lineGetIcon: ");
  1083.             SendDlgItemMessage(hwndDlg, IDC_LINEICON, WM_SETTEXT,
  1084.                 0, (LPARAM) (LPCTSTR) "TapiComm");
  1085.         }
  1086.     }
  1087. */
  1088.     // Verify if the device is usable by TapiComm.
  1089.     lReturn = VerifyUsableLine(dwDeviceID);
  1090.     // Enable or disable the 'Dial' button, depending on if the line is ok.
  1091.     // Make sure there is a number to dial before enabling the button.
  1092.     hControlWnd = GetDlgItem(hwndDlg, IDC_DIAL);
  1093.     if (SendDlgItemMessage(hwndDlg, IDC_CANONICALNUMBER,
  1094.         WM_GETTEXTLENGTH, 0, 0) == 0)
  1095.     {
  1096.         EnableWindow(hControlWnd, FALSE);
  1097.     }
  1098.     else
  1099.         EnableWindow(hControlWnd, (lReturn == SUCCESS));
  1100.     // Any errors on this line prevent us from configuring it
  1101.     // or using dialing properties.
  1102.     if (lReturn == LINENOTUSEABLE_ERROR)
  1103.     {
  1104.         EnableWindow(GetDlgItem(hwndDlg, IDC_CONFIGURELINE), FALSE);
  1105.         EnableWindow(GetDlgItem(hwndDlg, IDC_DIALINGPROPERTIES), FALSE);
  1106.     }
  1107.     else
  1108.     {
  1109.         EnableWindow(GetDlgItem(hwndDlg, IDC_CONFIGURELINE), TRUE);
  1110.         if (SendDlgItemMessage(hwndDlg, IDC_USEDIALINGRULES, BM_GETCHECK, 0, 0))
  1111.             EnableWindow(GetDlgItem(hwndDlg, IDC_DIALINGPROPERTIES), TRUE);
  1112.     }
  1113.     switch(lReturn)
  1114.     {
  1115.         case SUCCESS:
  1116.             g_dwDeviceID = dwDeviceID;
  1117.             return TRUE;
  1118.         case LINENOTUSEABLE_ERROR:
  1119.             MessageBox(hwndDlg,
  1120.                 "The selected line is incompatible with the TapiComm sample",
  1121.                 "Warning",MB_OK);
  1122.             break;
  1123.         case LINENOTUSEABLE_NOVOICE:
  1124.             MessageBox(hwndDlg,
  1125.                 "The selected line doesn't support VOICE capabilities",
  1126.                 "Warning",MB_OK);
  1127.             break;
  1128.         case LINENOTUSEABLE_NODATAMODEM:
  1129.             MessageBox(hwndDlg,
  1130.                 "The selected line doesn't support DATAMODEM capabilities",
  1131.                 "Warning",MB_OK);
  1132.             break;
  1133.         case LINENOTUSEABLE_NOMAKECALL:
  1134.             MessageBox(hwndDlg,
  1135.                 "The selected line doesn't support MAKECALL capabilities",
  1136.                 "Warning",MB_OK);
  1137.             break;
  1138.         case LINENOTUSEABLE_ALLOCATED:
  1139.             MessageBox(hwndDlg,
  1140.                 "The selected line is already in use by a non-TAPI application",
  1141.                 "Warning",MB_OK);
  1142.             break;
  1143.         case LINENOTUSEABLE_INUSE:
  1144.             MessageBox(hwndDlg,
  1145.                 "The selected line is already in use by a TAPI application",
  1146.                 "Warning",MB_OK);
  1147.             break;
  1148.         
  1149.         case LINENOTUSEABLE_NOCOMMDATAMODEM:
  1150.             MessageBox(hwndDlg,
  1151.                 "The selected line doesn't support the COMM/DATAMODEM device class",
  1152.                 "Warning",MB_OK);
  1153.             break;
  1154.     }
  1155.     // g_dwDeviceID == MAXDWORD mean the selected device isn't usable.
  1156.     g_dwDeviceID = MAXDWORD;
  1157.     return FALSE;
  1158. }
  1159. //
  1160. //  FUNCTION: void FillCountryCodeList(HWND, DWORD)
  1161. //
  1162. //  PURPOSE: Fill the 'Country Code' control
  1163. //
  1164. //  PARAMETERS:
  1165. //    hwndDlg - handle to the current "Dial" dialog 
  1166. //    dwDefaultCountryID - ID of the 'default' country to be selected
  1167. //
  1168. //  RETURN VALUE:
  1169. //    none
  1170. //
  1171. //  COMMENTS:
  1172. //
  1173. //    This function fills the 'Country Code' control with country names.
  1174. //    The country code is appended to the end of the name and the names
  1175. //    are added to the control sorted.  Because the country code is
  1176. //    embedded in the string along with the country name, there is no need
  1177. //    for any of the country information structures to be kept around.  The 
  1178. //    country code can be extracted from the selected string at any time.
  1179. //
  1180. //
  1181. void FillCountryCodeList(HWND hwndDlg, DWORD dwDefaultCountryID)
  1182. {
  1183.     LPLINECOUNTRYLIST lpLineCountryList = NULL;
  1184.     DWORD dwSizeofCountryList = sizeof(LINECOUNTRYLIST);
  1185.     long lReturn;
  1186.     DWORD dwCountry;
  1187.     LPLINECOUNTRYENTRY lpLineCountryEntries;
  1188.     char szRenamedCountry[256];
  1189.     // Get the country information stored in TAPI
  1190.     do
  1191.     {
  1192.         lpLineCountryList = (LPLINECOUNTRYLIST) CheckAndReAllocBuffer(
  1193.             (LPVOID) lpLineCountryList, dwSizeofCountryList,
  1194.             "FillCountryCodeList");
  1195.         if (lpLineCountryList == NULL)
  1196.             return;
  1197.         lReturn = lineGetCountry (0, SAMPLE_TAPI_VERSION, lpLineCountryList);
  1198.         if (HandleLineErr(lReturn))
  1199.             ;
  1200.         else
  1201.         {
  1202.             OutputDebugLineError(lReturn, "lineGetCountry unhandled error: ");
  1203.             LocalFree(lpLineCountryList);
  1204.             return;
  1205.         }
  1206.         if ((lpLineCountryList -> dwNeededSize) >
  1207.             (lpLineCountryList -> dwTotalSize))
  1208.         {
  1209.             dwSizeofCountryList = lpLineCountryList ->dwNeededSize;
  1210.             lReturn = -1; // Lets loop again.
  1211.         }
  1212.     }
  1213.     while (lReturn != SUCCESS);
  1214.     // Find the first country entry
  1215.     lpLineCountryEntries = (LPLINECOUNTRYENTRY) 
  1216.         (((LPBYTE) lpLineCountryList) 
  1217.          + lpLineCountryList -> dwCountryListOffset);
  1218.     // Now enumerate through all the countries
  1219.     for (dwCountry = 0; 
  1220.          dwCountry < lpLineCountryList -> dwNumCountries; 
  1221.          dwCountry++)
  1222.     {
  1223.         // append the country code to the country name
  1224.         wsprintf(szRenamedCountry,"%s (%lu)",
  1225.             (((LPSTR) lpLineCountryList) +
  1226.                 lpLineCountryEntries[dwCountry].dwCountryNameOffset),
  1227.             lpLineCountryEntries[dwCountry].dwCountryCode);
  1228.         // Now put this country name / code string into the combobox
  1229.         lReturn = SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE, CB_ADDSTRING,
  1230.                     0, (LPARAM) (LPCTSTR) szRenamedCountry);
  1231.         // If this country is the default country, select it.
  1232.         if (lpLineCountryEntries[dwCountry].dwCountryID
  1233.             == dwDefaultCountryID)
  1234.         {
  1235.             SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE, CB_SETCURSEL, lReturn, 0);
  1236.         }
  1237.     }
  1238.     
  1239.     LocalFree(lpLineCountryList);
  1240.     return;
  1241. }
  1242. //
  1243. //  FUNCTION: void FillLocationInfo(HWND, LPSTR, LPDWORD, LPSTR)
  1244. //
  1245. //  PURPOSE: Fill (or refill) the 'Your Location' control
  1246. //
  1247. //  PARAMETERS:
  1248. //    hwndDlg - handle to the current "Dial" dialog 
  1249. //    lpszCurrentLocation - Name of current location, or NULL
  1250. //    lpdwCountryID - location to store the current country ID or NULL
  1251. //    lpszAreaCode - location to store the current area code or NULL
  1252. //
  1253. //  RETURN VALUE:
  1254. //    none
  1255. //
  1256. //  COMMENTS:
  1257. //
  1258. //    This function is moderately multipurpose.
  1259. //
  1260. //    If lpszCurrentLocation is NULL, then the 'Your Location' control
  1261. //    is filled with all the locations stored in TAPI and the TAPI 'default' 
  1262. //    location is selected.  This is done during initialization and
  1263. //    also after the 'Dialing Properties' dialog has been displayed.
  1264. //    This last is done because the user can change the current location
  1265. //    or add and delete locations while in the 'Dialing Properties' dialog.
  1266. //    
  1267. //    If lpszCurrentLocation is a valid string pointer, then it is assumed
  1268. //    that the 'Your Location' control is already filled and that the user
  1269. //    is selecting a specific location.  In this case, all of the existing
  1270. //    TAPI locations are enumerated until the specified location is found.
  1271. //    At this point, the specified location is set to the current location.
  1272. //    
  1273. //    In either case, if lpdwCountryID is not NULL, it is filled with the
  1274. //    country ID for the current location.  If lpszAreaCode is not NULL, it
  1275. //    is filled with the area code defined for the current location.  These
  1276. //    values can be used later to initialize other "Dial" controls.
  1277. //
  1278. //    This function also fills the 'Calling Card' control based on
  1279. //    the information stored in the current location.
  1280. //
  1281. //
  1282. void FillLocationInfo(HWND hwndDlg, LPSTR lpszCurrentLocation, 
  1283.     LPDWORD lpdwCountryID, LPSTR lpszAreaCode)
  1284. {
  1285.     LPLINETRANSLATECAPS lpTranslateCaps = NULL;
  1286.     DWORD dwSizeofTranslateCaps = sizeof(LINETRANSLATECAPS);
  1287.     long lReturn;
  1288.     DWORD dwCounter;
  1289.     LPLINELOCATIONENTRY lpLocationEntry;
  1290.     LPLINECARDENTRY lpLineCardEntry = NULL;
  1291.     DWORD dwPreferredCardID = MAXDWORD;
  1292.     // First, get the TRANSLATECAPS
  1293.     do
  1294.     {
  1295.         lpTranslateCaps = (LPLINETRANSLATECAPS) CheckAndReAllocBuffer(
  1296.             (LPVOID) lpTranslateCaps, dwSizeofTranslateCaps,
  1297.             "FillLocationInfo");
  1298.         if (lpTranslateCaps == NULL)
  1299.             return;
  1300.         lReturn = lineGetTranslateCaps(g_hLineApp, SAMPLE_TAPI_VERSION, 
  1301.                     lpTranslateCaps);
  1302.         if (HandleLineErr(lReturn))
  1303.             ;
  1304.         else
  1305.         {
  1306.             OutputDebugLineError(lReturn, 
  1307.                 "lineGetTranslateCaps unhandled error: ");
  1308.             LocalFree(lpTranslateCaps);
  1309.             return;
  1310.         }
  1311.         if ((lpTranslateCaps -> dwNeededSize) >
  1312.             (lpTranslateCaps -> dwTotalSize))
  1313.         {
  1314.             dwSizeofTranslateCaps = lpTranslateCaps ->dwNeededSize;
  1315.             lReturn = -1; // Lets loop again.
  1316.         }
  1317.     }
  1318.     while(lReturn != SUCCESS);
  1319.     // Find the location information in the TRANSLATECAPS
  1320.     lpLocationEntry = (LPLINELOCATIONENTRY)
  1321.         (((LPBYTE) lpTranslateCaps) + lpTranslateCaps->dwLocationListOffset);
  1322.     // If lpszCurrentLocation, then make that location 'current'
  1323.     if (lpszCurrentLocation)
  1324.     {
  1325.         // loop through all locations, looking for a location match
  1326.         for(dwCounter = 0; 
  1327.             dwCounter < lpTranslateCaps -> dwNumLocations;
  1328.             dwCounter++)
  1329.         {
  1330.             if (strcmp((((LPSTR) lpTranslateCaps) + 
  1331.                             lpLocationEntry[dwCounter].dwLocationNameOffset),
  1332.                         lpszCurrentLocation)
  1333.                 == 0)
  1334.             {
  1335.                 // Found it!  Set the current location.
  1336.                 lineSetCurrentLocation(g_hLineApp, 
  1337.                     lpLocationEntry[dwCounter].dwPermanentLocationID);
  1338.                 // Set the return values.
  1339.                 if (lpdwCountryID)
  1340.                     *lpdwCountryID = lpLocationEntry[dwCounter].dwCountryID;
  1341.                 if (lpszAreaCode)
  1342.                     strcpy(lpszAreaCode, (((LPSTR) lpTranslateCaps) + 
  1343.                             lpLocationEntry[dwCounter].dwCityCodeOffset));
  1344.                 // Store the preferred card ID for later use.
  1345.                 dwPreferredCardID = lpLocationEntry[dwCounter].dwPreferredCardID;
  1346.                 break;
  1347.             }
  1348.         }
  1349.         // Was a match for lpszCurrentLocation found?
  1350.         if (dwPreferredCardID == MAXDWORD)
  1351.         {
  1352.             OutputDebugString("lpszCurrentLocation not foundn");
  1353.             SendDlgItemMessage(hwndDlg, IDC_CALLINGCARD, WM_SETTEXT, 0, 
  1354.                 (LPARAM) (LPCSTR) "Invalid Location Selected");
  1355.             LocalFree(lpTranslateCaps);
  1356.             return;
  1357.         }
  1358.     }
  1359.     else // fill the combobox and use the TAPI 'current' location.
  1360.     {
  1361.         // First empty the combobox
  1362.         SendDlgItemMessage(hwndDlg, IDC_LOCATION, CB_RESETCONTENT, 0, 0);
  1363.     
  1364.         // enumerate all the locations
  1365.         for(dwCounter = 0; 
  1366.             dwCounter < lpTranslateCaps -> dwNumLocations;
  1367.             dwCounter++)
  1368.         {
  1369.             // Put each one into the combobox
  1370.             lReturn = SendDlgItemMessage(hwndDlg, IDC_LOCATION, CB_ADDSTRING,
  1371.                 0, (LPARAM) (((LPBYTE) lpTranslateCaps) + 
  1372.                     lpLocationEntry[dwCounter].dwLocationNameOffset));
  1373.         
  1374.             // Is this location the 'current' location?
  1375.             if (lpLocationEntry[dwCounter].dwPermanentLocationID ==
  1376.                 lpTranslateCaps->dwCurrentLocationID)
  1377.             {
  1378.                 // Return the requested information
  1379.                 if (lpdwCountryID)
  1380.                     *lpdwCountryID = lpLocationEntry[dwCounter].dwCountryID;
  1381.                 if (lpszAreaCode)
  1382.                     strcpy(lpszAreaCode, (((LPSTR) lpTranslateCaps) + 
  1383.                             lpLocationEntry[dwCounter].dwCityCodeOffset));
  1384.                 // Set this to be the active location.
  1385.                 SendDlgItemMessage(hwndDlg, IDC_LOCATION, CB_SETCURSEL, lReturn, 0);
  1386.                 dwPreferredCardID = lpLocationEntry[dwCounter].dwPreferredCardID;
  1387.             }
  1388.         }
  1389.     }
  1390.     // Now locate the prefered card and display it.
  1391.     lpLineCardEntry = (LPLINECARDENTRY)
  1392.         (((LPBYTE) lpTranslateCaps) + lpTranslateCaps->dwCardListOffset);
  1393.     for(dwCounter = 0; 
  1394.         dwCounter < lpTranslateCaps -> dwNumCards;
  1395.         dwCounter++)
  1396.     {
  1397.         if (lpLineCardEntry[dwCounter].dwPermanentCardID == dwPreferredCardID)
  1398.         {
  1399.             SendDlgItemMessage(hwndDlg, IDC_CALLINGCARD, WM_SETTEXT, 0, 
  1400.                 (LPARAM) (((LPBYTE) lpTranslateCaps) + 
  1401.                     lpLineCardEntry[dwCounter].dwCardNameOffset));
  1402.             break;
  1403.         }
  1404.     }
  1405.     LocalFree(lpTranslateCaps);
  1406. }
  1407. //
  1408. //  FUNCTION: void UseDialingRules(HWND)
  1409. //
  1410. //  PURPOSE: Enable/disable Dialing Rule controls
  1411. //
  1412. //  PARAMETERS:
  1413. //    hwndDlg - handle to the current "Dial" dialog 
  1414. //
  1415. //  RETURN VALUE:
  1416. //    none
  1417. //
  1418. //  COMMENTS:
  1419. //
  1420. //    The sole purpose of this function is to enable or disable
  1421. //    the controls that apply to dialing rules if the 
  1422. //    "Use Country Code and Area Code" checkbox is checked or unchecked,
  1423. //    as appropriate.
  1424. //
  1425. //
  1426. void UseDialingRules(HWND hwndDlg)
  1427. {
  1428.     HWND hControl;
  1429.     BOOL bEnableWindow;
  1430.     bEnableWindow = SendDlgItemMessage(hwndDlg,
  1431.         IDC_USEDIALINGRULES, BM_GETCHECK, 0, 0);
  1432.     hControl = GetDlgItem(hwndDlg, IDC_STATICCOUNTRYCODE);
  1433.     EnableWindow(hControl, bEnableWindow);
  1434.     hControl = GetDlgItem(hwndDlg, IDC_COUNTRYCODE);
  1435.     EnableWindow(hControl, bEnableWindow);
  1436.     hControl = GetDlgItem(hwndDlg, IDC_STATICAREACODE);
  1437.     EnableWindow(hControl, bEnableWindow);
  1438.     hControl = GetDlgItem(hwndDlg, IDC_AREACODE);
  1439.     EnableWindow(hControl, bEnableWindow);
  1440.     hControl = GetDlgItem(hwndDlg, IDC_STATICLOCATION);
  1441.     EnableWindow(hControl, bEnableWindow);
  1442.     hControl = GetDlgItem(hwndDlg, IDC_LOCATION);
  1443.     EnableWindow(hControl, bEnableWindow);
  1444.     hControl = GetDlgItem(hwndDlg, IDC_STATICCALLINGCARD);
  1445.     EnableWindow(hControl, bEnableWindow);
  1446.     hControl = GetDlgItem(hwndDlg, IDC_CALLINGCARD);
  1447.     EnableWindow(hControl, bEnableWindow);
  1448.     if (IsWindowEnabled(GetDlgItem(hwndDlg, IDC_CONFIGURELINE)))
  1449.     {
  1450.         hControl = GetDlgItem(hwndDlg, IDC_DIALINGPROPERTIES);
  1451.         EnableWindow(hControl, bEnableWindow);
  1452.     }
  1453. }
  1454. //
  1455. //  FUNCTION: void DisplayPhoneNumber(HWND)
  1456. //
  1457. //  PURPOSE: Create, Translate and Display the Phone Number
  1458. //
  1459. //  PARAMETERS:
  1460. //    hwndDlg - handle to the current "Dial" dialog 
  1461. //
  1462. //  RETURN VALUE:
  1463. //    none
  1464. //
  1465. //  COMMENTS:
  1466. //
  1467. //    This function uses the information stored in many other controls
  1468. //    to build the phone number, translate it, and display it.  Also
  1469. //    makes sure the Dial button is enabled or disabled, based on if the
  1470. //    number can be dialed or not.
  1471. //
  1472. //    There are actually three phone numbers generated during this
  1473. //    process:  canonical, dialable and displayable.  Normally, only the
  1474. //    displayable number is shown to the user; the other two numbers are
  1475. //    to be used by the program internally.  However, for demonstration
  1476. //    purposes (and because it is cool for developers to see these numbers),
  1477. //    all three numbers are displayed.
  1478. //
  1479. void DisplayPhoneNumber(HWND hwndDlg)
  1480. {
  1481.     char szPreTranslatedNumber[1024] = "";
  1482.     int  nPreTranslatedSize = 0;
  1483.     char szTempBuffer[512];
  1484.     int  i;
  1485.     DWORD dwDeviceID;
  1486.     LPLINETRANSLATEOUTPUT lpLineTranslateOutput = NULL;
  1487.     // Disable the 'dial' button if there isn't a number to dial
  1488.     if (0 == SendDlgItemMessage(hwndDlg, IDC_PHONENUMBER, 
  1489.             WM_GETTEXTLENGTH, 0, 0))
  1490.     {
  1491.         SendDlgItemMessage(hwndDlg, IDC_CANONICALNUMBER, WM_SETTEXT, 0,
  1492.             (LPARAM) NULL);
  1493.         SendDlgItemMessage(hwndDlg, IDC_DIALABLENUMBER, WM_SETTEXT, 0,
  1494.             (LPARAM) NULL);
  1495.         SendDlgItemMessage(hwndDlg, IDC_DISPLAYABLENUMBER, WM_SETTEXT, 0,
  1496.             (LPARAM) (LPCTSTR) "No Phone Number");
  1497.         EnableWindow(GetDlgItem(hwndDlg, IDC_DIAL), FALSE);
  1498.         return;
  1499.     }
  1500.     // If we use the dialing rules, lets make canonical format.
  1501.     // Canonical format is explained in the TAPI documentation and the
  1502.     // string format needs to be followed very strictly.
  1503.     if (SendDlgItemMessage(hwndDlg, IDC_USEDIALINGRULES,
  1504.         BM_GETCHECK, 0, 0))
  1505.     {
  1506.         // First character *has* to be the plus sign.
  1507.         szPreTranslatedNumber[0] = '+';
  1508.         nPreTranslatedSize = 1;
  1509.         // The country code *has* to be next.
  1510.         // Country code was stored in the string with the country
  1511.         // name and needs to be extracted at this point.
  1512.         i = SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE,
  1513.                 CB_GETCURSEL, 0, 0);
  1514.         SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE,
  1515.             CB_GETLBTEXT, (WPARAM) i, (LPARAM) (LPCTSTR) szTempBuffer);
  1516.         // Country code is at the end of the string, surounded by parens.
  1517.         // This makes it easy to identify the country code.
  1518.         i = strlen(szTempBuffer);
  1519.         while(szTempBuffer[--i] != '(');
  1520.         while(szTempBuffer[++i] != ')') 
  1521.             szPreTranslatedNumber[nPreTranslatedSize++] = szTempBuffer[i];
  1522.         // Next is the area code.
  1523.         i = SendDlgItemMessage(hwndDlg, IDC_AREACODE, WM_GETTEXT,
  1524.                 510, (LPARAM) (LPCTSTR) szTempBuffer);
  1525.         // Note that the area code is optional.  If it is included,
  1526.         // then it has to be preceeded by *exactly* one space and it
  1527.         // *has* to be surrounded by parens.
  1528.         if (i)
  1529.             nPreTranslatedSize +=
  1530.                 wsprintf(&szPreTranslatedNumber[nPreTranslatedSize],
  1531.                     " (%s)", szTempBuffer);
  1532.         // There has to be *exactly* one space before the rest of the number.
  1533.         szPreTranslatedNumber[nPreTranslatedSize++] = ' ';
  1534.         // At this point, the phone number is appended to the
  1535.         // canonical number.  The next step is the same whether canonical
  1536.         // format is used or not; just the prepended area code and 
  1537.         // country code are different.
  1538.     }
  1539.     SendDlgItemMessage(hwndDlg, IDC_PHONENUMBER, WM_GETTEXT,
  1540.         510, (LPARAM) (LPCTSTR) szTempBuffer);
  1541.     strcat(&szPreTranslatedNumber[nPreTranslatedSize], szTempBuffer);
  1542.     dwDeviceID = SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
  1543.                         CB_GETCURSEL, 0, 0);
  1544.     // Translate the address!
  1545.     lpLineTranslateOutput = I_lineTranslateAddress(
  1546.         lpLineTranslateOutput, dwDeviceID, SAMPLE_TAPI_VERSION,
  1547.         szPreTranslatedNumber);
  1548.     // Unable to translate it?
  1549.     if (lpLineTranslateOutput == NULL)
  1550.     {
  1551.         SendDlgItemMessage(hwndDlg, IDC_CANONICALNUMBER, WM_SETTEXT, 0,
  1552.             (LPARAM) NULL);
  1553.         SendDlgItemMessage(hwndDlg, IDC_DIALABLENUMBER, WM_SETTEXT, 0,
  1554.             (LPARAM) NULL);
  1555.         SendDlgItemMessage(hwndDlg, IDC_DISPLAYABLENUMBER, WM_SETTEXT, 0,
  1556.             (LPARAM) (LPCTSTR) "Invalid Phone Number or Area Code");
  1557.         EnableWindow(GetDlgItem(hwndDlg, IDC_DIAL), FALSE);
  1558.         return;
  1559.     }
  1560.     // Is the selected device useable with TapiComm?
  1561.     if (g_dwDeviceID != MAXDWORD)
  1562.         EnableWindow(GetDlgItem(hwndDlg, IDC_DIAL), TRUE);
  1563.     // Fill the appropriate phone number controls.
  1564.     SendDlgItemMessage(hwndDlg, IDC_CANONICALNUMBER, WM_SETTEXT, 0,
  1565.         (LPARAM) (LPCTSTR) szPreTranslatedNumber);
  1566.     SendDlgItemMessage(hwndDlg, IDC_DIALABLENUMBER, WM_SETTEXT, 0,
  1567.         (LPARAM) ((LPSTR) lpLineTranslateOutput +
  1568.             lpLineTranslateOutput -> dwDialableStringOffset));
  1569.     SendDlgItemMessage(hwndDlg, IDC_DISPLAYABLENUMBER, WM_SETTEXT, 0,
  1570.         (LPARAM) ((LPSTR) lpLineTranslateOutput +
  1571.             lpLineTranslateOutput -> dwDisplayableStringOffset));
  1572.     LocalFree(lpLineTranslateOutput);
  1573. }
  1574. //
  1575. //  FUNCTION: void PreConfigureDevice(HWND, DWORD)
  1576. //
  1577. //  PURPOSE: 
  1578. //
  1579. //  PARAMETERS:
  1580. //    hwndDlg - handle to the current "Dial" dialog 
  1581. //    dwDeviceID - line device to be configured
  1582. //
  1583. //  RETURN VALUE:
  1584. //    none
  1585. //
  1586. //  COMMENTS:
  1587. //
  1588. //    At one point, PreConfigureDevice used lineConfigDialog to
  1589. //    configure the device.  This has the unfortunate effect of configuring
  1590. //    the device immediately, even if it is in use by another TAPI app.
  1591. //    This can be really bad if data communications are already in
  1592. //    progress (like with RAS).
  1593. //
  1594. //    Now, PreConfigureDevice uses lineConfigDialogEdit to give the
  1595. //    user the configuration UI, but it doesn't actually do anything to
  1596. //    the line device.  TapiComm stores the configuration information so
  1597. //    that it can be set later, just before making the call.
  1598. //
  1599. //
  1600. void PreConfigureDevice(HWND hwndDlg, DWORD dwDeviceID)
  1601. {
  1602.     long lReturn;
  1603.     LPVARSTRING lpVarString = NULL;
  1604.     DWORD dwSizeofVarString = sizeof(VARSTRING);
  1605.     // If there isn't already any device configuration information,
  1606.     // then we need to get some.
  1607.     if (g_lpDeviceConfig == NULL)
  1608.     {
  1609.         do
  1610.         {
  1611.             lpVarString = (LPVARSTRING) CheckAndReAllocBuffer(
  1612.                 (LPVOID) lpVarString, dwSizeofVarString,
  1613.                 "PreConfigureDevice - lineGetDevConfig: ");
  1614.             if (lpVarString == NULL)
  1615.                 return;
  1616.             lReturn = lineGetDevConfig(dwDeviceID, lpVarString, 
  1617.                 "comm/datamodem");
  1618.             if (HandleLineErr(lReturn))
  1619.                 ;
  1620.             else
  1621.             {
  1622.                 OutputDebugLineError(lReturn, 
  1623.                     "lineGetDevCaps unhandled error: ");
  1624.                 LocalFree(lpVarString);
  1625.                 return;
  1626.             }
  1627.             if ((lpVarString -> dwNeededSize) > (lpVarString -> dwTotalSize))
  1628.             {
  1629.                 dwSizeofVarString = lpVarString -> dwNeededSize;
  1630.                 lReturn = -1; // Lets loop again.
  1631.             }
  1632.         }
  1633.         while (lReturn != SUCCESS);
  1634.         g_dwSizeDeviceConfig = lpVarString -> dwStringSize;
  1635.         // The extra byte allocated is in case dwStringSize is 0.
  1636.         g_lpDeviceConfig = CheckAndReAllocBuffer(
  1637.                 g_lpDeviceConfig, g_dwSizeDeviceConfig+1,
  1638.                 "PreConfigureDevice - Allocate device config: ");
  1639.         if (!g_lpDeviceConfig)
  1640.         {
  1641.             LocalFree(lpVarString);
  1642.             return;
  1643.         }
  1644.         memcpy(g_lpDeviceConfig, 
  1645.             ((LPBYTE) lpVarString + lpVarString -> dwStringOffset),
  1646.             g_dwSizeDeviceConfig);
  1647.     }
  1648.     // Next make the lineConfigDialogEdit call.
  1649.     // Note that we determine the initial size of the VARSTRING
  1650.     // structure based on the known size of the existing configuration
  1651.     // information.  I make the assumption that this configuration
  1652.     // information is very unlikely to grow by more than 5K or by
  1653.     // more than 5 times.  This is a *very* conservative number.
  1654.     // We do *not* want lineConfigDialogEdit to fail just because there 
  1655.     // wasn't enough room to stored the data.  This would require the user
  1656.     // to go through configuration again and that would be annoying.
  1657.     dwSizeofVarString = 5 * g_dwSizeDeviceConfig + 5000;
  1658.     do
  1659.     {
  1660.         lpVarString = (LPVARSTRING) CheckAndReAllocBuffer(
  1661.             (LPVOID) lpVarString, dwSizeofVarString,
  1662.             "PreConfigureDevice - lineConfigDialogEdit: ");
  1663.         if (lpVarString == NULL)
  1664.             return;
  1665.         lReturn = lineConfigDialogEdit(dwDeviceID, hwndDlg, "comm/datamodem",
  1666.             g_lpDeviceConfig, g_dwSizeDeviceConfig, lpVarString);
  1667.         if (HandleLineErr(lReturn))
  1668.             ;
  1669.         else
  1670.         {
  1671.             OutputDebugLineError(lReturn, 
  1672.                 "lineConfigDialogEdit unhandled error: ");
  1673.             LocalFree(lpVarString);
  1674.             return;
  1675.         }
  1676.         if ((lpVarString -> dwNeededSize) > (lpVarString -> dwTotalSize))
  1677.         {
  1678.             // We had been conservative about making sure the structure was 
  1679.             // big enough.  Unfortunately, not conservative enough.  Hopefully, 
  1680.             // this will not happen a second time because we are *DOUBLING* 
  1681.             // the NeededSize.
  1682.             MessageBox(hwndDlg, 
  1683.                 "Internal Error: Unable to set line configuration.n"
  1684.                 "Please try again.",
  1685.                 "Oops", MB_OK);
  1686.             dwSizeofVarString = (lpVarString -> dwNeededSize) * 2;
  1687.             lReturn = -1; // Lets loop again.
  1688.         }
  1689.     }
  1690.     while (lReturn != SUCCESS);
  1691.     // Store the configuration information into a global structure
  1692.     // so it can be set at a later time.
  1693.     g_dwSizeDeviceConfig = lpVarString -> dwStringSize;
  1694.     g_lpDeviceConfig = CheckAndReAllocBuffer(
  1695.             g_lpDeviceConfig, g_dwSizeDeviceConfig+1,
  1696.             "PreConfigureDevice - Reallocate device config: ");
  1697.     if (!g_lpDeviceConfig)
  1698.     {
  1699.         LocalFree(lpVarString);
  1700.         return;
  1701.     }
  1702.     memcpy(g_lpDeviceConfig, 
  1703.         ((LPBYTE) lpVarString + lpVarString -> dwStringOffset),
  1704.         g_dwSizeDeviceConfig);
  1705.     LocalFree(lpVarString);
  1706. }
  1707. //
  1708. //  FUNCTION: BOOL GetAddressToDial
  1709. //
  1710. //  PURPOSE: Get an address to dial from the user.
  1711. //
  1712. //  PARAMETERS:
  1713. //    none
  1714. //
  1715. //  RETURN VALUE:
  1716. //    TRUE if a valid device and phone number have been entered by
  1717. //    the user.  FALSE if the user canceled the dialing process.
  1718. //
  1719. //  COMMENTS:
  1720. //
  1721. //    All this function does is launch the "Dial" dialog.
  1722. //
  1723. //
  1724. BOOL GetAddressToDial()
  1725. {
  1726.     BOOL bRet;
  1727.     UpdateStatusBar("Getting Number to Dial",1,0);
  1728.     bRet = DialogBoxParam(hInst, "DialDialog", g_hDlgParentWindow, 
  1729.         DialDialogProc, 0);
  1730.     g_hDialog = NULL;
  1731.     g_hDlgParentWindow = g_hWndMainWindow;
  1732.     if (bRet == FALSE)
  1733.         UpdateStatusBar("Dial aborted",1,0);
  1734.     return bRet;
  1735. }
  1736. //
  1737. //  FUNCTION: DialDialogProc(HWND, UINT, WPARAM, LPARAM)
  1738. //
  1739. //  PURPOSE: Dialog callback procedure for the dialing dialog
  1740. //
  1741. //  PARAMETERS:
  1742. //    hwndDlg - Dialog calling the callback.
  1743. //    uMsg    - Dialog message.
  1744. //    wParam  - uMsg specific.
  1745. //    lParam  - uMsg specific.
  1746. //
  1747. //  RETURN VALUE:
  1748. //    returns 0 - command handled.
  1749. //    returns non-0 - command unhandled
  1750. //
  1751. //  COMMENTS:
  1752. //
  1753. //    This is the dialog to get the phone number and line device 
  1754. //    from the user.  All the relavent information is stored in global
  1755. //    variables to be used later if the dialog returns successfully.
  1756. //
  1757. //
  1758. BOOL CALLBACK DialDialogProc(
  1759.     HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1760. {
  1761.     // Static variables to store the information from last time the
  1762.     // "Dial" dialog was displayed.  That way the phone number can be
  1763.     // typed once but used several times.
  1764.     static char szCountryName[512] = "";
  1765.     static char szAreaCode[256] = "Not Valid";
  1766.     static char szPhoneNumber[512] = "Not Valid";
  1767.     static DWORD dwUsedDeviceID = MAXDWORD;
  1768.     static BOOL bUsedCountryAndArea = FALSE;
  1769.     static BOOL bHistoryValid = FALSE;
  1770.     switch(uMsg)
  1771.     {
  1772.         case WM_INITDIALOG:
  1773.         {
  1774.             DWORD dwCountryID = 0;
  1775.             // Store the Dialog Window so it can be dismissed if necessary
  1776.             g_hDialog = hwndDlg;
  1777.             // This dialog should be parent to all dialogs.
  1778.             g_hDlgParentWindow = hwndDlg;
  1779.             // Initialize the Dialog Box. Lots to do here.
  1780.             FillTAPILine(hwndDlg);
  1781.             if (g_lpDeviceConfig)
  1782.             {
  1783.                 LocalFree(g_lpDeviceConfig);
  1784.                 g_lpDeviceConfig = NULL;
  1785.             }
  1786.             // If there is a valid history, use it to initialize the controls.
  1787.             if (bHistoryValid)
  1788.             {
  1789.                 FillLocationInfo(hwndDlg, NULL, NULL, NULL);
  1790.                 FillCountryCodeList(hwndDlg, 0);
  1791.                 SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE, CB_SELECTSTRING,
  1792.                     (WPARAM) -1, (LPARAM) (LPCTSTR) szCountryName);
  1793.                 SendDlgItemMessage(hwndDlg, IDC_PHONENUMBER, WM_SETTEXT, 0,
  1794.                     (LPARAM) (LPCTSTR) szPhoneNumber);
  1795.                     
  1796.                 SendDlgItemMessage(hwndDlg, IDC_USEDIALINGRULES, 
  1797.                     BM_SETCHECK, (WPARAM) bUsedCountryAndArea, 0);
  1798.                 SendDlgItemMessage(hwndDlg, IDC_TAPILINE, CB_SETCURSEL,
  1799.                     g_dwDeviceID, 0);
  1800.             }
  1801.             else
  1802.             {
  1803.                 FillLocationInfo(hwndDlg, NULL, &dwCountryID, szAreaCode);
  1804.                 FillCountryCodeList(hwndDlg, dwCountryID);
  1805.                 SendDlgItemMessage(hwndDlg, IDC_USEDIALINGRULES,
  1806.                     BM_SETCHECK, 1, 0);
  1807.             }
  1808.             SendDlgItemMessage(hwndDlg, IDC_AREACODE, WM_SETTEXT, 
  1809.                 0, (LPARAM) (LPCTSTR) szAreaCode);
  1810.             UseDialingRules(hwndDlg);
  1811.             DisplayPhoneNumber(hwndDlg);
  1812.             VerifyAndWarnUsableLine(hwndDlg);
  1813.             return TRUE;
  1814.         }
  1815.         case WM_COMMAND:
  1816.         {
  1817.             switch(LOWORD(wParam))
  1818.             {
  1819.                 case IDC_TAPILINE:
  1820.                     if (HIWORD(wParam) == CBN_SELENDOK)
  1821.                     {
  1822.                         if (g_lpDeviceConfig)
  1823.                         {
  1824.                             LocalFree(g_lpDeviceConfig);
  1825.                             g_lpDeviceConfig = NULL;
  1826.                         }
  1827.                         DisplayPhoneNumber(hwndDlg);
  1828.                         VerifyAndWarnUsableLine(hwndDlg);
  1829.                     }
  1830.                     return TRUE;
  1831.                 case IDC_CONFIGURELINE:
  1832.                 {
  1833.                     DWORD dwDeviceID;
  1834.                     dwDeviceID = SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
  1835.                         CB_GETCURSEL, 0, 0);
  1836.                     PreConfigureDevice(hwndDlg, dwDeviceID);
  1837.                     DisplayPhoneNumber(hwndDlg);
  1838.                     return TRUE;
  1839.                 }
  1840.                 case IDC_COUNTRYCODE:
  1841.                     if (HIWORD(wParam) == CBN_SELENDOK)
  1842.                         DisplayPhoneNumber(hwndDlg);
  1843.                     return TRUE;
  1844.                 case IDC_AREACODE:
  1845.                 case IDC_PHONENUMBER:
  1846.                     if (HIWORD(wParam) == EN_CHANGE)
  1847.                         DisplayPhoneNumber(hwndDlg);
  1848.                     return TRUE;
  1849.                 case IDC_USEDIALINGRULES:
  1850.                     if (HIWORD(wParam) == BN_CLICKED)
  1851.                     {
  1852.                         UseDialingRules(hwndDlg);
  1853.                         DisplayPhoneNumber(hwndDlg);
  1854.                     }
  1855.                     return TRUE;
  1856.                 case IDC_LOCATION:
  1857.                     if (HIWORD(wParam) == CBN_CLOSEUP)
  1858.                     {
  1859.                         char szCurrentLocation[1024];
  1860.                         int nCurrentSelection;
  1861.                         nCurrentSelection = SendDlgItemMessage(hwndDlg, 
  1862.                             IDC_LOCATION, CB_GETCURSEL, 0, 0);
  1863.                         SendDlgItemMessage(hwndDlg, IDC_LOCATION,
  1864.                             CB_GETLBTEXT, nCurrentSelection, 
  1865.                             (LPARAM) (LPCTSTR) szCurrentLocation);
  1866.                         // If the user selected a 'location', make it current.
  1867.                         FillLocationInfo(hwndDlg, szCurrentLocation, NULL, NULL);
  1868.                         DisplayPhoneNumber(hwndDlg);
  1869.                     }
  1870.                     return TRUE;
  1871.                 case IDC_DIALINGPROPERTIES:
  1872.                 {
  1873.                     char szAddress[1024];
  1874.                     DWORD dwDeviceID;
  1875.                     long lReturn;
  1876.                     dwDeviceID = SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
  1877.                             CB_GETCURSEL, 0, 0);
  1878.                     SendDlgItemMessage(hwndDlg, IDC_CANONICALNUMBER,
  1879.                         WM_GETTEXT, 1023, (LPARAM) (LPCTSTR) szAddress);
  1880.                       
  1881.                     lReturn = lineTranslateDialog(g_hLineApp, dwDeviceID,
  1882.                         SAMPLE_TAPI_VERSION, hwndDlg, szAddress);
  1883.                     if (lReturn != SUCCESS)
  1884.                         OutputDebugLineError(lReturn,"lineTranslateDialog: ");
  1885.                     // The user could have changed the default location, or
  1886.                     // added or removed a location while in the 'Dialing
  1887.                     // Properties' dialog.  Refill the Location Info.
  1888.                     FillLocationInfo(hwndDlg, NULL, NULL, NULL);
  1889.                     DisplayPhoneNumber(hwndDlg);
  1890.                     return TRUE;
  1891.                 }
  1892.                 case IDCANCEL:
  1893.                     EndDialog(hwndDlg, FALSE);
  1894.                     return TRUE;
  1895.                 case IDC_DIAL:
  1896.                 {
  1897.                     // The Dial button has to be enabled and the line has
  1898.                     // to be currently usable to continue.
  1899.                     if (!(IsWindowEnabled((HWND)lParam) &&
  1900.                           VerifyAndWarnUsableLine(hwndDlg)))
  1901.                         return TRUE;
  1902.                     DisplayPhoneNumber(hwndDlg);
  1903.                     // Get the displayable and dialable numbers and store
  1904.                     // them in global variables to be used while dialing.
  1905.                     SendDlgItemMessage(hwndDlg, IDC_DISPLAYABLENUMBER,
  1906.                         WM_GETTEXT, 1023, (LPARAM) (LPCTSTR) g_szDisplayableAddress);
  1907.                     SendDlgItemMessage(hwndDlg, IDC_DIALABLENUMBER,
  1908.                         WM_GETTEXT, 1023, (LPARAM) (LPCTSTR) g_szDialableAddress);
  1909.                     // Store all the relavent information in static
  1910.                     // variables so they will be available the next time a
  1911.                     // number is dialed.
  1912.                     SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE,
  1913.                         WM_GETTEXT, 511, (LPARAM) (LPCTSTR) szCountryName);
  1914.                     SendDlgItemMessage(hwndDlg, IDC_AREACODE,
  1915.                         WM_GETTEXT, 255, (LPARAM) (LPCTSTR) szAreaCode);
  1916.                     SendDlgItemMessage(hwndDlg, IDC_PHONENUMBER,
  1917.                         WM_GETTEXT, 511, (LPARAM) (LPCTSTR) szPhoneNumber);
  1918.                     bUsedCountryAndArea = (BOOL) SendDlgItemMessage(hwndDlg,
  1919.                         IDC_USEDIALINGRULES, BM_GETCHECK, 0, 0);
  1920.                     bHistoryValid = TRUE;
  1921.                     EndDialog(hwndDlg, TRUE);
  1922.                     return TRUE;
  1923.                 }
  1924.                 // This message is actually posted to the dialog from the
  1925.                 // lineCallbackFunc when it receives a 
  1926.                 // LINEDEVSTATE_TRANSLATECHANGE message.  Notify the user and
  1927.                 // retranslate the number.  Also refill the Location Info
  1928.                 // since this could have been generated by a location change.
  1929.                 case IDC_CONFIGURATIONCHANGED:
  1930.                 {
  1931.                     FillLocationInfo(hwndDlg, NULL, NULL, NULL);
  1932.                     DisplayPhoneNumber(hwndDlg);
  1933.                     MessageBox(hwndDlg, 
  1934.                         "Location Configuration has been changed.",
  1935.                         "Warning",MB_OK);
  1936.                     return TRUE;
  1937.                 }
  1938.                 // If we get a LINE_CREATE message, all that needs to be done
  1939.                 // is to reset this controls contents.  The selected line
  1940.                 // won't change and no lines will be removed.
  1941.                 case IDC_LINECREATE:
  1942.                 {
  1943.                     FillTAPILine(hwndDlg);
  1944.                     return TRUE;
  1945.                 }
  1946.                 default:
  1947.                     break;
  1948.             }
  1949.             break;
  1950.         }
  1951.         // This dialog has the DS_CONTEXTMENU flag so that the right mouse 
  1952.         // button will send this message.  Bring up the appropriate help page.
  1953.         case WM_CONTEXTMENU:
  1954.         {
  1955.             if (hwndDlg != (HWND) wParam)
  1956.                 WinHelp ((HWND)wParam,
  1957.                     "TAPICOMM.HLP",
  1958.                      HELP_CONTEXTMENU,
  1959.                     (DWORD)(LPVOID) g_adwSampleMenuHelpIDs);
  1960.             break;
  1961.         }
  1962.         // Bring up the appropriate help page.
  1963.         case WM_HELP:
  1964.         {
  1965.             LPHELPINFO lphi;
  1966.             lphi = (LPHELPINFO)lParam;
  1967.             if ((lphi->iContextType == HELPINFO_WINDOW) &&
  1968.                 (hwndDlg != lphi->hItemHandle) &&
  1969.                 (lphi->iCtrlId < IDC_NOHELPCONTROLS))
  1970.             {
  1971.                 WinHelp (lphi->hItemHandle,
  1972.                     "TAPICOMM.HLP",
  1973.                     HELP_WM_HELP,
  1974.                     (DWORD)(LPVOID) g_adwSampleMenuHelpIDs);
  1975.             }
  1976.          return TRUE; 
  1977.         }
  1978.         default:
  1979.             break;
  1980.     }
  1981.     return FALSE;
  1982. }