Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4896 lines
145 KiB

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