mirror of https://github.com/tongzx/nt5src
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
144 KiB
4896 lines
144 KiB
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (C) 1995 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
// MODULE: TapiCode.c
|
|
//
|
|
// PURPOSE: Handles all the TAPI routines for TapiComm.
|
|
//
|
|
//
|
|
// EXPORTED FUNCTIONS: These functions are for use by other modules.
|
|
//
|
|
// InitializeTAPI - Initialize this app with TAPI.
|
|
// ShutdownTAPI - Shutdown this app from TAPI.
|
|
// DialCall - Dial a Call.
|
|
// HangupCall - Hangup an existing Call.
|
|
// PostHangupCall - Posts a HangupCall message to the main window.
|
|
//
|
|
// INTERNAL FUNCTIONS: These functions are for this module only.
|
|
//
|
|
// DialCallInParts - Actually Dial the call.
|
|
//
|
|
// lineCallbackFunc - TAPI callback for async messages.
|
|
//
|
|
// CheckAndReAllocBuffer - Helper function for I_ wrappers functions.
|
|
//
|
|
// I_lineNegotiateAPIVersion - Wrapper for lineNegotiateAPIVersion.
|
|
// I_lineGetDevCaps - Wrapper for lineGetDevCaps.
|
|
// I_lineGetAddressStatus - Wrapper for lineGetAddressStatus.
|
|
// I_lineTranslateAddress - Wrapper for lineTranslateAddress.
|
|
// I_lineGetCallStatus - Wrapper for lineGetCallStatus.
|
|
// I_lineGetAddressCaps - Wrapper for lineGetAddressCaps.
|
|
//
|
|
// WaitForCallState - Resynchronize by Waiting for a CallState.
|
|
// WaitForReply - Resynchronize by Waiting for a LINE_REPLY.
|
|
//
|
|
// DoLineReply - Handle asynchronous LINE_REPLY.
|
|
// DoLineClose - Handle asynchronous LINE_CLOSE.
|
|
// DoLineDevState - Handle asynchronous LINE_LINEDEVSTATE.
|
|
// DoLineCallState - Handle asynchronous LINE_CALLSTATE.
|
|
// DoLineCreate - Handle asynchronous LINE_CREATE.
|
|
//
|
|
// HandleLineErr - Handler for most LINEERR errors.
|
|
//
|
|
// HandleIniFileCorrupt - LINEERR handler for INIFILECORRUPT.
|
|
// HandleNoDriver - LINEERR handler for NODRIVER.
|
|
// HandleNoDevicesInstalled - LINEERR handler for NODEVICE.
|
|
// HandleReInit - LINEERR handler for REINIT.
|
|
// HandleNoMultipleInstance - LINEERR handler for NOMULTIPLEINSTANCE.
|
|
// HandleNoMem - LINEERR handler for NOMEM.
|
|
// HandleOperationFailed - LINEERR handler for OPERATIONFAILED.
|
|
// HandleResourceUnavail - LINEERR handler for RESOURCEUNAVAIL.
|
|
//
|
|
// LaunchModemControlPanelAdd - Launches the Modem Control Panel.
|
|
//
|
|
// GetAddressToDial - Launches a GetAddressToDial dialog.
|
|
// DialDialogProc - Dialog Proc for the GetAddressToDial API.
|
|
//
|
|
// I_lineNegotiateLegacyAPIVersion - Wrapper to negoitiate with legacy TSPs
|
|
// VerifyUsableLine - Verify that a line device is usable
|
|
// FillTAPILine - Fill a combobox with TAPI Device names
|
|
// VerifyAndWarnUsableLine - Verify and warn if a line device is usable
|
|
// FillCountryCodeList - Fill a combobox with country codes
|
|
// FillLocationInfo - Fill a combobox with current TAPI locations
|
|
// UseDialingRules - Enable/Disable dialing rules controls
|
|
// DisplayPhoneNumber - Create and display a valid phone number
|
|
// PreConfigureDevice - Preconfigure a device line
|
|
|
|
|
|
#include <tapi.h>
|
|
#include <windows.h>
|
|
#include <string.h>
|
|
#include "globals.h"
|
|
#include "TapiInfo.h"
|
|
#include "TapiCode.h"
|
|
#include "CommCode.h"
|
|
#include "resource.h"
|
|
// #include "statbar.h"
|
|
// #include "toolbar.h"
|
|
#include <logit.h>
|
|
|
|
HANDLE g_hConnectionEvent = NULL;
|
|
|
|
extern "C" HINSTANCE hInst;
|
|
|
|
// All TAPI line functions return 0 for SUCCESS, so define it.
|
|
#define SUCCESS 0
|
|
|
|
// Possible return error for resynchronization functions.
|
|
#define WAITERR_WAITABORTED 1
|
|
#define WAITERR_WAITTIMEDOUT 2
|
|
|
|
// Reasons why a line device might not be usable by TapiComm.
|
|
#define LINENOTUSEABLE_ERROR 1
|
|
#define LINENOTUSEABLE_NOVOICE 2
|
|
#define LINENOTUSEABLE_NODATAMODEM 3
|
|
#define LINENOTUSEABLE_NOMAKECALL 4
|
|
#define LINENOTUSEABLE_ALLOCATED 5
|
|
#define LINENOTUSEABLE_INUSE 6
|
|
#define LINENOTUSEABLE_NOCOMMDATAMODEM 7
|
|
|
|
// Constant used in WaitForCallState when any new
|
|
// callstate message is acceptable.
|
|
#define I_LINECALLSTATE_ANY 0
|
|
|
|
// Wait up to 30 seconds for an async completion.
|
|
#define WAITTIMEOUT 30000
|
|
|
|
// TAPI version that this sample is designed to use.
|
|
#define SAMPLE_TAPI_VERSION 0x00010004
|
|
|
|
|
|
// Global TAPI variables.
|
|
HWND g_hWndMainWindow = NULL; // Apps main window.
|
|
HWND g_hDlgParentWindow = NULL; // This will be the parent of all dialogs.
|
|
HLINEAPP g_hLineApp = NULL;
|
|
DWORD g_dwNumDevs = 0;
|
|
|
|
// Global variable that holds the handle to a TAPI dialog
|
|
// that needs to be dismissed if line conditions change.
|
|
HWND g_hDialog = NULL;
|
|
|
|
// Global flags to prevent re-entrancy problems.
|
|
BOOL g_bShuttingDown = FALSE;
|
|
BOOL g_bStoppingCall = FALSE;
|
|
BOOL g_bInitializing = FALSE;
|
|
|
|
|
|
// This sample only supports one call in progress at a time.
|
|
BOOL g_bTapiInUse = FALSE;
|
|
|
|
|
|
// Data needed per call. This sample only supports one call.
|
|
HCALL g_hCall = NULL;
|
|
HLINE g_hLine = NULL;
|
|
DWORD g_dwDeviceID = 0;
|
|
DWORD g_dwAPIVersion = 0;
|
|
DWORD g_dwCallState = 0;
|
|
char g_szTranslatedNumber[128] = "";
|
|
char g_szDisplayableAddress[128] = "";
|
|
char g_szDialableAddress[128] = "";
|
|
BOOL g_bConnected = FALSE;
|
|
LPVOID g_lpDeviceConfig = NULL;
|
|
DWORD g_dwSizeDeviceConfig;
|
|
|
|
// Global variables to allow us to do various waits.
|
|
BOOL g_bReplyRecieved;
|
|
DWORD g_dwRequestedID;
|
|
long g_lAsyncReply;
|
|
BOOL g_bCallStateReceived;
|
|
|
|
// Structures needed to handle special non-dialable characters.
|
|
#define g_sizeofNonDialable (sizeof(g_sNonDialable)/sizeof(g_sNonDialable[0]))
|
|
|
|
typedef struct {
|
|
LONG lError;
|
|
DWORD dwDevCapFlag;
|
|
LPSTR szToken;
|
|
LPSTR szMsg;
|
|
} NONDIALTOKENS;
|
|
|
|
NONDIALTOKENS g_sNonDialable[] = {
|
|
{LINEERR_DIALBILLING, LINEDEVCAPFLAGS_DIALBILLING, "$",
|
|
"Wait for the credit card bong tone" },
|
|
{LINEERR_DIALDIALTONE, LINEDEVCAPFLAGS_DIALDIALTONE, "W",
|
|
"Wait for the second dial tone" },
|
|
{LINEERR_DIALDIALTONE, LINEDEVCAPFLAGS_DIALDIALTONE, "w",
|
|
"Wait for the second dial tone" },
|
|
{LINEERR_DIALQUIET, LINEDEVCAPFLAGS_DIALQUIET, "@",
|
|
"Wait for the remote end to answer" },
|
|
{LINEERR_DIALPROMPT, 0, "?",
|
|
"Press OK when you are ready to continue dialing"},
|
|
};
|
|
|
|
// "Dial" dialog controls and their associated help page IDs
|
|
DWORD g_adwSampleMenuHelpIDs[] =
|
|
{
|
|
IDC_COUNTRYCODE , IDC_COUNTRYCODE,
|
|
IDC_STATICCOUNTRYCODE , IDC_COUNTRYCODE,
|
|
IDC_AREACODE , IDC_AREACODE,
|
|
IDC_STATICAREACODE , IDC_AREACODE,
|
|
IDC_PHONENUMBER , IDC_PHONENUMBER,
|
|
IDC_STATICPHONENUMBER , IDC_PHONENUMBER,
|
|
IDC_USEDIALINGRULES , IDC_USEDIALINGRULES,
|
|
IDC_LOCATION , IDC_LOCATION,
|
|
IDC_STATICLOCATION , IDC_LOCATION,
|
|
IDC_CALLINGCARD , IDC_CALLINGCARD,
|
|
IDC_STATICCALLINGCARD , IDC_CALLINGCARD,
|
|
IDC_DIALINGPROPERTIES , IDC_DIALINGPROPERTIES,
|
|
IDC_TAPILINE , IDC_TAPILINE,
|
|
IDC_STATICTAPILINE , IDC_TAPILINE,
|
|
IDC_CONFIGURELINE , IDC_CONFIGURELINE,
|
|
IDC_DIAL , IDC_DIAL,
|
|
IDC_LINEICON , IDC_LINEICON,
|
|
//IDC_STATICWHERETODIAL , IDC_STATICWHERETODIAL,
|
|
//IDC_STATICHOWTODIAL , IDC_STATICHOWTODIAL,
|
|
//IDC_STATICCONNECTUSING , IDC_STATICCONNECTUSING,
|
|
//IDC_STATICPHONENUMBER , IDC_PHONENUMBER,
|
|
0,0
|
|
};
|
|
|
|
//**************************************************
|
|
// Prototypes for functions used only in this module.
|
|
//**************************************************
|
|
|
|
BOOL DialCallInParts (
|
|
LPLINEDEVCAPS lpLineDevCaps,
|
|
LPCSTR lpszAddress,
|
|
LPCSTR lpszDisplayableAddress);
|
|
|
|
LPLINECALLPARAMS CreateCallParams (
|
|
LPLINECALLPARAMS lpCallParams,
|
|
LPCSTR lpszDisplayableAddress);
|
|
|
|
DWORD I_lineNegotiateAPIVersion (
|
|
DWORD dwDeviceID);
|
|
|
|
LPLINECALLINFO I_lineGetCallInfo(LPLINECALLINFO lpLineCallInfo);
|
|
|
|
volatile DWORD g_dwRate = 0;
|
|
BOOL g_bCallCancel = FALSE;
|
|
|
|
LPVOID CheckAndReAllocBuffer(
|
|
LPVOID lpBuffer, size_t sizeBufferMinimum,
|
|
LPTCH szApiPhrase);
|
|
|
|
LPLINEDEVCAPS I_lineGetDevCaps (
|
|
LPLINEDEVCAPS lpLineDevCaps,
|
|
DWORD dwDeviceID,
|
|
DWORD dwAPIVersion);
|
|
|
|
LPLINEADDRESSSTATUS I_lineGetAddressStatus (
|
|
LPLINEADDRESSSTATUS lpLineAddressStatus,
|
|
HLINE hLine,
|
|
DWORD dwAddressID);
|
|
|
|
LPLINETRANSLATEOUTPUT I_lineTranslateAddress (
|
|
LPLINETRANSLATEOUTPUT lpLineTranslateOutput,
|
|
DWORD dwDeviceID,
|
|
DWORD dwAPIVersion,
|
|
LPCSTR lpszDialAddress);
|
|
|
|
LPLINECALLSTATUS I_lineGetCallStatus (
|
|
LPLINECALLSTATUS lpLineCallStatus,
|
|
HCALL hCall);
|
|
|
|
LPLINEADDRESSCAPS I_lineGetAddressCaps (
|
|
LPLINEADDRESSCAPS lpLineAddressCaps,
|
|
DWORD dwDeviceID, DWORD dwAddressID,
|
|
DWORD dwAPIVersion, DWORD dwExtVersion);
|
|
|
|
long WaitForCallState (DWORD dwNewCallState);
|
|
|
|
long WaitForReply (long lRequestID);
|
|
|
|
void CALLBACK lineCallbackFunc(
|
|
DWORD hDevice, DWORD dwMsg, DWORD_PTR dwCallbackInstance,
|
|
DWORD_PTR dwParam1, DWORD_PTR dwParam2, DWORD_PTR dwParam3);
|
|
|
|
void DoLineReply(
|
|
DWORD dwDevice, DWORD dwMsg, DWORD_PTR dwCallbackInstance,
|
|
DWORD_PTR dwParam1, DWORD_PTR dwParam2, DWORD_PTR dwParam3);
|
|
void DoLineClose(
|
|
DWORD dwDevice, DWORD dwMsg, DWORD_PTR dwCallbackInstance,
|
|
DWORD_PTR dwParam1, DWORD_PTR dwParam2, DWORD_PTR dwParam3);
|
|
void DoLineDevState(
|
|
DWORD dwDevice, DWORD dwsg, DWORD_PTR dwCallbackInstance,
|
|
DWORD_PTR dwParam1, DWORD_PTR dwParam2, DWORD_PTR dwParam3);
|
|
void DoLineCallState(
|
|
DWORD dwDevice, DWORD dwMsg, DWORD_PTR dwCallbackInstance,
|
|
DWORD_PTR dwParam1, DWORD_PTR dwParam2, DWORD_PTR dwParam3);
|
|
void DoLineCreate(
|
|
DWORD dwDevice, DWORD dwMessage, DWORD_PTR dwCallbackInstance,
|
|
DWORD_PTR dwParam1, DWORD_PTR dwParam2, DWORD_PTR dwParam3);
|
|
|
|
BOOL HandleLineErr(long lLineErr);
|
|
|
|
BOOL HandleIniFileCorrupt();
|
|
BOOL HandleNoDriver();
|
|
BOOL HandleNoDevicesInstalled();
|
|
BOOL HandleReInit();
|
|
BOOL HandleNoMultipleInstance();
|
|
BOOL HandleNoMem();
|
|
BOOL HandleOperationFailed();
|
|
BOOL HandleResourceUnavail();
|
|
|
|
BOOL LaunchModemControlPanelAdd();
|
|
|
|
INT_PTR CALLBACK DialDialogProc(
|
|
HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
|
|
BOOL GetAddressToDial();
|
|
|
|
DWORD I_lineNegotiateLegacyAPIVersion(DWORD dwDeviceID);
|
|
long VerifyUsableLine(DWORD dwDeviceID);
|
|
void FillTAPILine(HWND hwndDlg);
|
|
BOOL VerifyAndWarnUsableLine(HWND hwndDlg);
|
|
void FillCountryCodeList(HWND hwndDlg, DWORD dwDefaultCountryID);
|
|
void FillLocationInfo(HWND hwndDlg, LPSTR lpszCurrentLocation,
|
|
LPDWORD lpdwCountryID, LPSTR lpszAreaCode);
|
|
void UseDialingRules(HWND hwndDlg);
|
|
void DisplayPhoneNumber(HWND hwndDlg);
|
|
void PreConfigureDevice(HWND hwndDlg, DWORD dwDeviceID);
|
|
|
|
|
|
//**************************************************
|
|
// Entry points from the UI
|
|
//**************************************************
|
|
|
|
|
|
//
|
|
// FUNCTION: BOOL InitializeTAPI(HWND)
|
|
//
|
|
// PURPOSE: Initializes TAPI
|
|
//
|
|
// PARAMETERS:
|
|
// hWndParent - Window to use as parent of any dialogs.
|
|
//
|
|
// RETURN VALUE:
|
|
// Always returns 0 - command handled.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This is the API that initializes the app with TAPI.
|
|
// If NULL is passed for the hWndParent, then its assumed
|
|
// that re-initialization has occurred and the previous hWnd
|
|
// is used.
|
|
//
|
|
//
|
|
|
|
BOOL InitializeTAPI(HWND hWndParent)
|
|
{
|
|
long lReturn;
|
|
BOOL bTryReInit = TRUE;
|
|
|
|
// If we're already initialized, then initialization succeeds.
|
|
if (g_hLineApp)
|
|
return TRUE;
|
|
|
|
// If we're in the middle of initializing, then fail, we're not done.
|
|
if (g_bInitializing)
|
|
return FALSE;
|
|
|
|
g_bInitializing = TRUE;
|
|
|
|
// Initialize TAPI
|
|
do
|
|
{
|
|
lReturn = lineInitialize(&g_hLineApp, hInst,
|
|
lineCallbackFunc, "DPlayComm", &g_dwNumDevs);
|
|
|
|
// If we get this error, its because some other app has yet
|
|
// to respond to the REINIT message. Wait 5 seconds and try
|
|
// again. If it still doesn't respond, tell the user.
|
|
if (lReturn == LINEERR_REINIT)
|
|
{
|
|
if (bTryReInit)
|
|
{
|
|
MSG msg;
|
|
DWORD dwTimeStarted;
|
|
|
|
dwTimeStarted = GetTickCount();
|
|
|
|
while(GetTickCount() - dwTimeStarted < 5000)
|
|
{
|
|
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
|
|
bTryReInit = FALSE;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
g_bInitializing = FALSE;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (lReturn == LINEERR_NODEVICE)
|
|
{
|
|
if (HandleNoDevicesInstalled())
|
|
continue;
|
|
else
|
|
{
|
|
|
|
TSHELL_INFO(TEXT("No devices installed."));
|
|
|
|
g_bInitializing = FALSE;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (HandleLineErr(lReturn))
|
|
continue;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineInitialize unhandled error: %x"), lReturn));
|
|
g_bInitializing = FALSE;
|
|
return FALSE;
|
|
}
|
|
}
|
|
while(lReturn != SUCCESS);
|
|
|
|
g_hDlgParentWindow = g_hWndMainWindow = NULL;
|
|
|
|
g_hCall = NULL;
|
|
g_hLine = NULL;
|
|
|
|
|
|
TSHELL_INFO(TEXT("Tapi initialized."));
|
|
|
|
g_bInitializing = FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: BOOL ShutdownTAPI()
|
|
//
|
|
// PURPOSE: Shuts down all use of TAPI
|
|
//
|
|
// PARAMETERS:
|
|
// None
|
|
//
|
|
// RETURN VALUE:
|
|
// True if TAPI successfully shut down.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// If ShutdownTAPI fails, then its likely either a problem
|
|
// with the service provider (and might require a system
|
|
// reboot to correct) or the application ran out of memory.
|
|
//
|
|
//
|
|
|
|
BOOL ShutdownTAPI()
|
|
{
|
|
long lReturn;
|
|
|
|
// If we aren't initialized, then Shutdown is unnecessary.
|
|
if (g_hLineApp == NULL)
|
|
return TRUE;
|
|
|
|
// Prevent ShutdownTAPI re-entrancy problems.
|
|
if (g_bShuttingDown)
|
|
return TRUE;
|
|
|
|
g_bShuttingDown = TRUE;
|
|
|
|
HangupCall(__LINE__);
|
|
|
|
do
|
|
{
|
|
lReturn = lineShutdown(g_hLineApp);
|
|
if (HandleLineErr(lReturn))
|
|
continue;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineShutdown unhandled error: %x"), lReturn));
|
|
break;
|
|
}
|
|
}
|
|
while(lReturn != SUCCESS);
|
|
|
|
g_bTapiInUse = FALSE;
|
|
g_bConnected = FALSE;
|
|
g_hLineApp = NULL;
|
|
g_hCall = NULL;
|
|
g_hLine = NULL;
|
|
g_bShuttingDown = FALSE;
|
|
|
|
TSHELL_INFO(TEXT("TAPI uninitialized."));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// FUNCTION: BOOL HangupCall()
|
|
//
|
|
// PURPOSE: Hangup the call in progress if it exists.
|
|
//
|
|
// PARAMETERS:
|
|
// none
|
|
//
|
|
// RETURN VALUE:
|
|
// TRUE if call hung up successfully.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// If HangupCall fails, then its likely either a problem
|
|
// with the service provider (and might require a system
|
|
// reboot to correct) or the application ran out of memory.
|
|
//
|
|
//
|
|
|
|
extern BOOL g_bPostHangup;
|
|
BOOL HangupCall(DWORD dwCallLine)
|
|
{
|
|
|
|
DBG_INFO((DBGARG, TEXT("HangupCall was called from %d"), dwCallLine));
|
|
|
|
if (g_hConnectionEvent)
|
|
SetEvent(g_hConnectionEvent);
|
|
|
|
if (g_bPostHangup)
|
|
return(TRUE);
|
|
else
|
|
return(HangupCallI());
|
|
}
|
|
BOOL HangupCallI()
|
|
{
|
|
LPLINECALLSTATUS pLineCallStatus = NULL;
|
|
long lReturn;
|
|
|
|
|
|
// Prevent HangupCall re-entrancy problems.
|
|
if (g_bStoppingCall)
|
|
return TRUE;
|
|
|
|
// if the 'Call' dialog is up, dismiss it.
|
|
if (g_hDialog)
|
|
PostMessage(g_hDialog, WM_COMMAND, IDCANCEL, 0);
|
|
|
|
// If Tapi is not being used right now, then the call is hung up.
|
|
if (!g_bTapiInUse)
|
|
return TRUE;
|
|
|
|
g_bStoppingCall = TRUE;
|
|
|
|
TSHELL_INFO(TEXT("Stopping Call in progress"));
|
|
|
|
// Stop any data communications on the comm port.
|
|
StopComm(g_hConnectionEvent);
|
|
|
|
// If there is a call in progress, drop and deallocate it.
|
|
if (g_hCall)
|
|
{
|
|
TSHELL_INFO(TEXT("Calling lineGetCallStatus"));
|
|
|
|
// I_lineGetCallStatus returns a LocalAlloc()d buffer
|
|
pLineCallStatus = I_lineGetCallStatus(pLineCallStatus, g_hCall);
|
|
if (pLineCallStatus == NULL)
|
|
{
|
|
ShutdownTAPI();
|
|
g_bStoppingCall = FALSE;
|
|
return FALSE;
|
|
}
|
|
|
|
// Only drop the call when the line is not IDLE.
|
|
if (!((pLineCallStatus -> dwCallState) & LINECALLSTATE_IDLE))
|
|
{
|
|
|
|
TSHELL_INFO(TEXT("Line isn't idle, call lineDrop"));
|
|
|
|
do
|
|
{
|
|
lReturn = WaitForReply(lineDrop(g_hCall, NULL, 0));
|
|
|
|
if (lReturn == WAITERR_WAITTIMEDOUT)
|
|
{
|
|
|
|
TSHELL_INFO(TEXT("Call timed out in WaitForReply."));
|
|
|
|
break;
|
|
}
|
|
|
|
if (lReturn == WAITERR_WAITABORTED)
|
|
{
|
|
|
|
TSHELL_INFO(TEXT("lineDrop: WAITERR_WAITABORTED."));
|
|
|
|
break;
|
|
}
|
|
|
|
// Was the call already in IDLE?
|
|
if (lReturn == LINEERR_INVALCALLSTATE)
|
|
break;
|
|
|
|
if (HandleLineErr(lReturn))
|
|
continue;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineDrop unhandled error: %x"), lReturn));
|
|
break;
|
|
}
|
|
}
|
|
while(lReturn != SUCCESS);
|
|
|
|
// Wait for the dropped call to go IDLE before continuing.
|
|
lReturn = WaitForCallState(LINECALLSTATE_IDLE);
|
|
|
|
#ifdef DEBUG
|
|
if (lReturn == WAITERR_WAITTIMEDOUT)
|
|
TSHELL_INFO(TEXT("Call timed out waiting for IDLE state."));
|
|
|
|
if (lReturn == WAITERR_WAITABORTED)
|
|
TSHELL_INFO(TEXT("WAITERR_WAITABORTED while waiting for IDLE state."));
|
|
#endif
|
|
|
|
TSHELL_INFO(TEXT("Call Dropped."));
|
|
}
|
|
|
|
// The call is now idle. Deallocate it!
|
|
do
|
|
{
|
|
lReturn = lineDeallocateCall(g_hCall);
|
|
if (HandleLineErr(lReturn))
|
|
continue;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineDeallocateCall unhandled error: %x"), lReturn));
|
|
break;
|
|
}
|
|
}
|
|
while(lReturn != SUCCESS);
|
|
|
|
|
|
TSHELL_INFO(TEXT("Call Deallocated."));
|
|
|
|
}
|
|
else
|
|
{
|
|
TSHELL_INFO(TEXT("g_hCall is NULL."));
|
|
}
|
|
|
|
|
|
|
|
|
|
// if we have a line open, close it.
|
|
if (g_hLine)
|
|
{
|
|
do
|
|
{
|
|
lReturn = lineClose(g_hLine);
|
|
if (HandleLineErr(lReturn))
|
|
continue;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineClose unhandled error: %x"), lReturn));
|
|
break;
|
|
}
|
|
}
|
|
while(lReturn != SUCCESS);
|
|
|
|
|
|
TSHELL_INFO(TEXT("Line Closed."));
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
TSHELL_INFO(TEXT("g_hLine is NULL."));
|
|
}
|
|
|
|
|
|
// Call and Line are taken care of. Finish cleaning up.
|
|
|
|
// If there is device configuration information, free the memory.
|
|
if (g_lpDeviceConfig)
|
|
LocalFree(g_lpDeviceConfig);
|
|
g_lpDeviceConfig = NULL;
|
|
|
|
g_hCall = NULL;
|
|
g_hLine = NULL;
|
|
g_bConnected = FALSE;
|
|
|
|
g_bTapiInUse = FALSE;
|
|
|
|
g_bStoppingCall = FALSE; // allow HangupCall to be called again.
|
|
|
|
|
|
TSHELL_INFO(TEXT("Call stopped"));
|
|
|
|
|
|
// Need to free LocalAlloc()d buffer returned from I_lineGetCallStatus
|
|
if (pLineCallStatus)
|
|
LocalFree(pLineCallStatus);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// FUNCTION: LONG GetDefaultLine()
|
|
//
|
|
// PURPOSE: Get Default line device.
|
|
//
|
|
LONG GetDefaultLine()
|
|
{
|
|
DWORD dwDeviceID;
|
|
DWORD dwAPIVersion;
|
|
LPLINEDEVCAPS lpLineDevCaps = NULL;
|
|
DWORD dwDefaultDevice = MAXDWORD;
|
|
|
|
|
|
TSHELL_INFO(TEXT("GetDefaultLine"));
|
|
|
|
|
|
for (dwDeviceID = 0; dwDeviceID < g_dwNumDevs
|
|
&& dwDefaultDevice == MAXDWORD; dwDeviceID ++)
|
|
{
|
|
dwAPIVersion = I_lineNegotiateLegacyAPIVersion(dwDeviceID);
|
|
|
|
if (dwAPIVersion)
|
|
{
|
|
lpLineDevCaps = I_lineGetDevCaps(lpLineDevCaps,
|
|
dwDeviceID, dwAPIVersion);
|
|
if (lpLineDevCaps)
|
|
{
|
|
if ( (lpLineDevCaps->dwMediaModes & LINEMEDIAMODE_DATAMODEM)
|
|
&& VerifyUsableLine(dwDeviceID) == SUCCESS)
|
|
{
|
|
dwDefaultDevice = dwDeviceID;
|
|
}
|
|
else; // Line isn't valid, unuseable.
|
|
}
|
|
else; // Couldn't GetDevCaps. Line is unavail.
|
|
}
|
|
else; // Couldn't NegotiateAPIVersion. Line is unavail.
|
|
}
|
|
|
|
if (lpLineDevCaps)
|
|
LocalFree(lpLineDevCaps);
|
|
|
|
if (dwDefaultDevice == MAXDWORD)
|
|
return(-1);
|
|
else
|
|
return((LONG) dwDefaultDevice);
|
|
}
|
|
|
|
//
|
|
// FUNCTION: ReceiveCall()
|
|
//
|
|
// PURPOSE: Wait for someone to call us.
|
|
//
|
|
// PARAMETERS:
|
|
// none
|
|
//
|
|
// RETURN VALUE:
|
|
// TRUE if able to find a line.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This function makes several assumptions:
|
|
//
|
|
|
|
BOOL ReceiveCall()
|
|
{
|
|
long lReturn;
|
|
LPLINEADDRESSSTATUS lpLineAddressStatus = NULL;
|
|
LPLINEDEVCAPS lpLineDevCaps = NULL;
|
|
|
|
|
|
|
|
TSHELL_INFO(TEXT("Receive Call"));
|
|
|
|
|
|
|
|
if (g_bTapiInUse)
|
|
{
|
|
|
|
TSHELL_INFO(TEXT("A call is already being handled"));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// If TAPI isn't initialized, its either because we couldn't initialize
|
|
// at startup (and this might have been corrected by now), or because
|
|
// a REINIT event was received. In either case, try to init now.
|
|
|
|
if (!g_hLineApp)
|
|
{
|
|
if (!InitializeTAPI(NULL))
|
|
return FALSE;
|
|
}
|
|
|
|
// If there are no line devices installed on the machine, lets give
|
|
// the user the opportunity to install one.
|
|
if (g_dwNumDevs < 1)
|
|
{
|
|
if (!HandleNoDevicesInstalled())
|
|
return FALSE;
|
|
}
|
|
|
|
// We now have a call active. Prevent future calls.
|
|
g_bTapiInUse = TRUE;
|
|
|
|
if ((lReturn = GetDefaultLine()) < 0)
|
|
return(FALSE);
|
|
|
|
g_dwDeviceID = (DWORD) lReturn;
|
|
|
|
// Negotiate the API version to use for this device.
|
|
g_dwAPIVersion = I_lineNegotiateAPIVersion(g_dwDeviceID);
|
|
if (g_dwAPIVersion == 0)
|
|
{
|
|
TSHELL_INFO(TEXT("Line Version problem."));
|
|
HangupCall(__LINE__);
|
|
goto DeleteBuffers;
|
|
}
|
|
|
|
// Open the Line for an incomming DATAMODEM call.
|
|
do
|
|
{
|
|
lReturn = lineOpen(g_hLineApp, g_dwDeviceID, &g_hLine,
|
|
g_dwAPIVersion, 0, 0,
|
|
LINECALLPRIVILEGE_OWNER, LINEMEDIAMODE_DATAMODEM,
|
|
0);
|
|
|
|
if(lReturn == LINEERR_ALLOCATED)
|
|
{
|
|
TSHELL_INFO(TEXT("Fatal Error"));
|
|
HangupCall(__LINE__);
|
|
goto DeleteBuffers;
|
|
}
|
|
|
|
if (HandleLineErr(lReturn))
|
|
continue;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineOpen unhandled error: %x"), lReturn));
|
|
|
|
HangupCall(__LINE__);
|
|
goto DeleteBuffers;
|
|
}
|
|
}
|
|
while(lReturn != SUCCESS);
|
|
|
|
// Tell the service provider that we want all notifications that
|
|
// have anything to do with this line.
|
|
do
|
|
{
|
|
// Set the messages we are interested in.
|
|
|
|
// Note that while most applications aren't really interested
|
|
// in dealing with all of the possible messages, its interesting
|
|
// to see which come through the callback for testing purposes.
|
|
|
|
lReturn = lineSetStatusMessages(g_hLine,
|
|
LINEDEVSTATE_OTHER |
|
|
LINEDEVSTATE_RINGING | // Important state!
|
|
LINEDEVSTATE_CONNECTED | // Important state!
|
|
LINEDEVSTATE_DISCONNECTED | // Important state!
|
|
LINEDEVSTATE_MSGWAITON |
|
|
LINEDEVSTATE_MSGWAITOFF |
|
|
LINEDEVSTATE_INSERVICE |
|
|
LINEDEVSTATE_OUTOFSERVICE | // Important state!
|
|
LINEDEVSTATE_MAINTENANCE | // Important state!
|
|
LINEDEVSTATE_OPEN |
|
|
LINEDEVSTATE_CLOSE |
|
|
LINEDEVSTATE_NUMCALLS |
|
|
LINEDEVSTATE_NUMCOMPLETIONS |
|
|
LINEDEVSTATE_TERMINALS |
|
|
LINEDEVSTATE_ROAMMODE |
|
|
LINEDEVSTATE_BATTERY |
|
|
LINEDEVSTATE_SIGNAL |
|
|
LINEDEVSTATE_DEVSPECIFIC |
|
|
LINEDEVSTATE_REINIT | // Not allowed to disable this.
|
|
LINEDEVSTATE_LOCK |
|
|
LINEDEVSTATE_CAPSCHANGE |
|
|
LINEDEVSTATE_CONFIGCHANGE |
|
|
LINEDEVSTATE_COMPLCANCEL ,
|
|
|
|
LINEADDRESSSTATE_OTHER |
|
|
LINEADDRESSSTATE_DEVSPECIFIC|
|
|
LINEADDRESSSTATE_INUSEZERO |
|
|
LINEADDRESSSTATE_INUSEONE |
|
|
LINEADDRESSSTATE_INUSEMANY |
|
|
LINEADDRESSSTATE_NUMCALLS |
|
|
LINEADDRESSSTATE_FORWARD |
|
|
LINEADDRESSSTATE_TERMINALS |
|
|
LINEADDRESSSTATE_CAPSCHANGE);
|
|
|
|
|
|
if (HandleLineErr(lReturn))
|
|
continue;
|
|
else
|
|
{
|
|
// If we do get an unhandled problem, we don't care.
|
|
// We just won't get notifications.
|
|
DBG_INFO((DBGARG, TEXT("lineSetStatusMessages unhandled error: %x"), lReturn));
|
|
break;
|
|
}
|
|
}
|
|
while(lReturn != SUCCESS);
|
|
|
|
|
|
return(TRUE);
|
|
|
|
|
|
DeleteBuffers:
|
|
|
|
if (lpLineAddressStatus)
|
|
LocalFree(lpLineAddressStatus);
|
|
if (lpLineDevCaps)
|
|
LocalFree(lpLineDevCaps);
|
|
|
|
return g_bTapiInUse;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// FUNCTION: DialCall(LPSTR lpDisplay, LPSTR lpDialable, DWORD dwDeviceID, HANDLE hEvent)
|
|
//
|
|
// PURPOSE: Get a number from the user and dial it.
|
|
//
|
|
// PARAMETERS:
|
|
// none
|
|
//
|
|
// RETURN VALUE:
|
|
// TRUE if able to get a number, find a line, and dial successfully.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This function makes several assumptions:
|
|
// - The number dialed will always explicitly come from the user.
|
|
// - There will only be one outgoing address per line.
|
|
//
|
|
|
|
BOOL DialCall(LPSTR lpDisplay, LPSTR lpDialable, LPDWORD pdwDeviceID, HANDLE hEvent)
|
|
{
|
|
long lReturn;
|
|
LPLINEADDRESSSTATUS lpLineAddressStatus = NULL;
|
|
LPLINEDEVCAPS lpLineDevCaps = NULL;
|
|
|
|
g_bCallCancel = FALSE;
|
|
|
|
if (g_bTapiInUse)
|
|
{
|
|
|
|
TSHELL_INFO(TEXT("A call is already being handled"));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
g_hConnectionEvent = hEvent;
|
|
// If TAPI isn't initialized, its either because we couldn't initialize
|
|
// at startup (and this might have been corrected by now), or because
|
|
// a REINIT event was received. In either case, try to init now.
|
|
|
|
if (!g_hLineApp)
|
|
{
|
|
if (!InitializeTAPI(NULL))
|
|
return FALSE;
|
|
}
|
|
|
|
// If there are no line devices installed on the machine, lets give
|
|
// the user the opportunity to install one.
|
|
if (g_dwNumDevs < 1)
|
|
{
|
|
if (!HandleNoDevicesInstalled())
|
|
return FALSE;
|
|
}
|
|
|
|
// We now have a call active. Prevent future calls.
|
|
g_bTapiInUse = TRUE;
|
|
|
|
//
|
|
// See if we can find the users Window.
|
|
//
|
|
{
|
|
HWND hWndTop;
|
|
DWORD dwWindowProcId;
|
|
DWORD dwMyProcId;
|
|
|
|
|
|
|
|
hWndTop = GetActiveWindow();
|
|
dwMyProcId = GetCurrentProcessId();
|
|
GetWindowThreadProcessId( hWndTop, &dwWindowProcId);
|
|
|
|
DBG_INFO((DBGARG, TEXT("My process is %8x and the active window proc is %8x"),
|
|
dwMyProcId, dwWindowProcId));
|
|
|
|
if (dwMyProcId == dwWindowProcId)
|
|
g_hDlgParentWindow = hWndTop;
|
|
else
|
|
{
|
|
hWndTop = GetTopWindow(NULL);
|
|
|
|
GetWindowThreadProcessId( hWndTop, &dwWindowProcId);
|
|
|
|
if (dwMyProcId == dwWindowProcId)
|
|
g_hDlgParentWindow = hWndTop;
|
|
|
|
DBG_INFO((DBGARG, TEXT("My process is %8x and the top window proc is %8x"),
|
|
dwMyProcId, dwWindowProcId));
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (lpDialable[0])
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("Dialing with old data (%s)\r\n"), lpDialable));
|
|
|
|
//
|
|
// We were supplied with remembered data. Use that.
|
|
//
|
|
lstrcpy( g_szDialableAddress, lpDialable);
|
|
if ((lReturn = GetDefaultLine()) < 0)
|
|
return(FALSE);
|
|
|
|
g_dwDeviceID = (DWORD) lReturn;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
TSHELL_INFO( TEXT("Get number from user"));
|
|
|
|
// Get a phone number from the user.
|
|
// Phone number will be placed in global variables if successful
|
|
if (!GetAddressToDial())
|
|
{
|
|
g_bCallCancel = TRUE;
|
|
HangupCall(__LINE__);
|
|
TSHELL_INFO(TEXT("User didn't cooperate, bailing out."));
|
|
goto DeleteBuffers;
|
|
}
|
|
lstrcpy( lpDisplay , g_szDisplayableAddress);
|
|
lstrcpy( lpDialable, g_szDialableAddress );
|
|
*pdwDeviceID = g_dwDeviceID;
|
|
|
|
}
|
|
|
|
// Negotiate the API version to use for this device.
|
|
g_dwAPIVersion = I_lineNegotiateAPIVersion(g_dwDeviceID);
|
|
|
|
if (g_dwAPIVersion == 0)
|
|
{
|
|
HangupCall(__LINE__);
|
|
TSHELL_INFO(TEXT("Line Version problem."));
|
|
goto DeleteBuffers;
|
|
}
|
|
|
|
// Need to check the DevCaps to make sure this line is usable.
|
|
// The 'Dial' dialog checks also, but better safe than sorry.
|
|
lpLineDevCaps = I_lineGetDevCaps(lpLineDevCaps,
|
|
g_dwDeviceID, g_dwAPIVersion);
|
|
if (lpLineDevCaps == NULL)
|
|
{
|
|
HangupCall(__LINE__);
|
|
TSHELL_INFO(TEXT("No useable line."));
|
|
goto DeleteBuffers;
|
|
}
|
|
|
|
if (!(lpLineDevCaps->dwBearerModes & LINEBEARERMODE_VOICE ))
|
|
{
|
|
HangupCall(__LINE__);
|
|
goto DeleteBuffers;
|
|
}
|
|
|
|
if (!(lpLineDevCaps->dwMediaModes & LINEMEDIAMODE_DATAMODEM))
|
|
{
|
|
HangupCall(__LINE__);
|
|
TSHELL_INFO(TEXT("No Datamodem capacity."));
|
|
goto DeleteBuffers;
|
|
}
|
|
|
|
// Does this line have the capability to make calls?
|
|
// It is possible that some lines can't make outbound calls.
|
|
if (!(lpLineDevCaps->dwLineFeatures & LINEFEATURE_MAKECALL))
|
|
{
|
|
HangupCall(__LINE__);
|
|
TSHELL_INFO(TEXT("Can't make calls on the device."));
|
|
goto DeleteBuffers;
|
|
}
|
|
|
|
// Open the Line for an outgoing DATAMODEM call.
|
|
do
|
|
{
|
|
TSHELL_INFO(TEXT("Opening line for Datamodem service."));
|
|
|
|
lReturn = lineOpen(g_hLineApp, g_dwDeviceID, &g_hLine,
|
|
g_dwAPIVersion, 0, 0,
|
|
LINECALLPRIVILEGE_NONE, LINEMEDIAMODE_DATAMODEM,
|
|
0);
|
|
|
|
if(lReturn == LINEERR_ALLOCATED)
|
|
{
|
|
HangupCall(__LINE__);
|
|
TSHELL_INFO(TEXT("Fatal Error"));
|
|
goto DeleteBuffers;
|
|
}
|
|
|
|
if (HandleLineErr(lReturn))
|
|
continue;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineOpen unhandled error: %x"), lReturn));
|
|
|
|
HangupCall(__LINE__);
|
|
goto DeleteBuffers;
|
|
}
|
|
}
|
|
while(lReturn != SUCCESS);
|
|
|
|
TSHELL_INFO(TEXT("Line is OPEN."));
|
|
|
|
|
|
// Tell the service provider that we want all notifications that
|
|
// have anything to do with this line.
|
|
do
|
|
{
|
|
// Set the messages we are interested in.
|
|
|
|
// Note that while most applications aren't really interested
|
|
// in dealing with all of the possible messages, its interesting
|
|
// to see which come through the callback for testing purposes.
|
|
|
|
lReturn = lineSetStatusMessages(g_hLine,
|
|
LINEDEVSTATE_OTHER |
|
|
LINEDEVSTATE_RINGING |
|
|
LINEDEVSTATE_CONNECTED | // Important state!
|
|
LINEDEVSTATE_DISCONNECTED | // Important state!
|
|
LINEDEVSTATE_MSGWAITON |
|
|
LINEDEVSTATE_MSGWAITOFF |
|
|
LINEDEVSTATE_INSERVICE |
|
|
LINEDEVSTATE_OUTOFSERVICE | // Important state!
|
|
LINEDEVSTATE_MAINTENANCE | // Important state!
|
|
LINEDEVSTATE_OPEN |
|
|
LINEDEVSTATE_CLOSE |
|
|
LINEDEVSTATE_NUMCALLS |
|
|
LINEDEVSTATE_NUMCOMPLETIONS |
|
|
LINEDEVSTATE_TERMINALS |
|
|
LINEDEVSTATE_ROAMMODE |
|
|
LINEDEVSTATE_BATTERY |
|
|
LINEDEVSTATE_SIGNAL |
|
|
LINEDEVSTATE_DEVSPECIFIC |
|
|
LINEDEVSTATE_REINIT | // Not allowed to disable this.
|
|
LINEDEVSTATE_LOCK |
|
|
LINEDEVSTATE_CAPSCHANGE |
|
|
LINEDEVSTATE_CONFIGCHANGE |
|
|
LINEDEVSTATE_COMPLCANCEL ,
|
|
|
|
LINEADDRESSSTATE_OTHER |
|
|
LINEADDRESSSTATE_DEVSPECIFIC|
|
|
LINEADDRESSSTATE_INUSEZERO |
|
|
LINEADDRESSSTATE_INUSEONE |
|
|
LINEADDRESSSTATE_INUSEMANY |
|
|
LINEADDRESSSTATE_NUMCALLS |
|
|
LINEADDRESSSTATE_FORWARD |
|
|
LINEADDRESSSTATE_TERMINALS |
|
|
LINEADDRESSSTATE_CAPSCHANGE);
|
|
|
|
|
|
if (HandleLineErr(lReturn))
|
|
continue;
|
|
else
|
|
{
|
|
// If we do get an unhandled problem, we don't care.
|
|
// We just won't get notifications.
|
|
|
|
DBG_INFO((DBGARG, TEXT("lineSetStatusMessages unhandled error: %x"), lReturn));
|
|
break;
|
|
}
|
|
}
|
|
while(lReturn != SUCCESS);
|
|
|
|
// Get LineAddressStatus so we can make sure the line
|
|
// isn't already in use by a TAPI application.
|
|
lpLineAddressStatus =
|
|
I_lineGetAddressStatus(lpLineAddressStatus, g_hLine, 0);
|
|
|
|
if (lpLineAddressStatus == NULL)
|
|
{
|
|
TSHELL_INFO(TEXT("Fatal Error"));
|
|
HangupCall(__LINE__);
|
|
goto DeleteBuffers;
|
|
}
|
|
|
|
// MAKECALL will be set if there are any available call appearances
|
|
if ( ! ((lpLineAddressStatus -> dwAddressFeatures) &
|
|
LINEADDRFEATURE_MAKECALL) )
|
|
{
|
|
|
|
TSHELL_INFO(TEXT("This line is not available to place a call."));
|
|
|
|
HangupCall(__LINE__);
|
|
goto DeleteBuffers;
|
|
}
|
|
|
|
// If the line was configured in the 'Dial' dialog, then
|
|
// we need to actually complete the configuration.
|
|
if (g_lpDeviceConfig)
|
|
lineSetDevConfig(g_dwDeviceID, g_lpDeviceConfig,
|
|
g_dwSizeDeviceConfig, "comm/datamodem");
|
|
|
|
// Start dialing the number
|
|
if (DialCallInParts(lpLineDevCaps, g_szDialableAddress,
|
|
g_szDisplayableAddress))
|
|
{
|
|
TSHELL_INFO(TEXT("DialCallInParts succeeded."));
|
|
}
|
|
else
|
|
{
|
|
|
|
TSHELL_INFO(TEXT("DialCallInParts failed."));
|
|
|
|
HangupCall(__LINE__);
|
|
goto DeleteBuffers;
|
|
}
|
|
|
|
|
|
DeleteBuffers:
|
|
|
|
if (lpLineAddressStatus)
|
|
LocalFree(lpLineAddressStatus);
|
|
if (lpLineDevCaps)
|
|
LocalFree(lpLineDevCaps);
|
|
|
|
return g_bTapiInUse;
|
|
}
|
|
|
|
|
|
//**************************************************
|
|
// These APIs are specific to this module
|
|
//**************************************************
|
|
|
|
|
|
|
|
//
|
|
// FUNCTION: DialCallInParts(LPLINEDEVCAPS, LPCSTR, LPCSTR)
|
|
//
|
|
// PURPOSE: Dials the call, handling special characters.
|
|
//
|
|
// PARAMETERS:
|
|
// lpLineDevCaps - LINEDEVCAPS for the line to be used.
|
|
// lpszAddress - Address to Dial.
|
|
// lpszDisplayableAddress - Displayable Address.
|
|
//
|
|
// RETURN VALUE:
|
|
// Returns TRUE if we successfully Dial.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This function dials the Address and handles any
|
|
// special characters in the address that the service provider
|
|
// can't handle. It requires input from the user to handle
|
|
// these characters; this can cause problems for fully automated
|
|
// dialing.
|
|
//
|
|
// Note that we can return TRUE, even if we don't reach a
|
|
// CONNECTED state. DIalCallInParts returns as soon as the
|
|
// Address is fully dialed or when an error occurs.
|
|
//
|
|
//
|
|
|
|
#ifdef WINNT
|
|
#define Xstrcspn strcspn
|
|
#else
|
|
//
|
|
// Source for strcspn here because it isn't in C10 std lib.
|
|
//
|
|
static size_t __cdecl Xstrcspn (
|
|
const char * string,
|
|
const char * control
|
|
)
|
|
{
|
|
const unsigned char *str = (const unsigned char *) string;
|
|
const unsigned char *ctrl = (const unsigned char *) control;
|
|
|
|
unsigned char map[32];
|
|
int count;
|
|
|
|
/* Clear out bit map */
|
|
for (count=0; count<32; count++)
|
|
map[count] = 0;
|
|
|
|
/* Set bits in control map */
|
|
while (*ctrl)
|
|
{
|
|
map[*ctrl >> 3] |= (1 << (*ctrl & 7));
|
|
ctrl++;
|
|
}
|
|
|
|
|
|
/* 1st char in control map stops search */
|
|
count=0;
|
|
map[0] |= 1; /* null chars not considered */
|
|
while (!(map[*str >> 3] & (1 << (*str & 7))))
|
|
{
|
|
count++;
|
|
str++;
|
|
}
|
|
return(count);
|
|
}
|
|
#endif
|
|
|
|
|
|
BOOL DialCallInParts(LPLINEDEVCAPS lpLineDevCaps,
|
|
LPCSTR lpszAddress, LPCSTR lpszDisplayableAddress)
|
|
{
|
|
LPLINECALLPARAMS lpCallParams = NULL;
|
|
LPLINEADDRESSCAPS lpAddressCaps = NULL;
|
|
LPLINECALLSTATUS lpLineCallStatus = NULL;
|
|
|
|
long lReturn;
|
|
int i;
|
|
DWORD dwDevCapFlags;
|
|
char szFilter[1+sizeof(g_sNonDialable)] = "";
|
|
BOOL bFirstDial = TRUE;
|
|
|
|
// Variables to handle Dialable Substring dialing.
|
|
LPSTR lpDS; // This is just so we can free lpszDialableSubstring later.
|
|
LPSTR lpszDialableSubstring;
|
|
int nAddressLength = 0;
|
|
int nCurrentAddress = 0;
|
|
char chUnhandledCharacter;
|
|
|
|
// Get the capabilities for the line device we're going to use.
|
|
lpAddressCaps = I_lineGetAddressCaps(lpAddressCaps,
|
|
g_dwDeviceID, 0, g_dwAPIVersion, 0);
|
|
|
|
if (lpAddressCaps == NULL)
|
|
return FALSE;
|
|
|
|
// Setup our CallParams for DATAMODEM settings.
|
|
lpCallParams = CreateCallParams (lpCallParams, lpszDisplayableAddress);
|
|
if (lpCallParams == NULL)
|
|
return FALSE;
|
|
|
|
// Determine which special characters the service provider
|
|
// does *not* handle so we can handle them manually.
|
|
// Keep list of unhandled characters in szFilter.
|
|
|
|
dwDevCapFlags = lpLineDevCaps -> dwDevCapFlags; // SP handled characters.
|
|
for (i = 0; i < g_sizeofNonDialable ; i++)
|
|
{
|
|
if ((dwDevCapFlags & g_sNonDialable[i].dwDevCapFlag) == 0)
|
|
{
|
|
strcat(szFilter, g_sNonDialable[i].szToken);
|
|
}
|
|
}
|
|
|
|
// szFilter now contains the set of tokens which delimit dialable substrings
|
|
|
|
// Setup the strings for substring dialing.
|
|
|
|
nAddressLength = strlen(lpszAddress);
|
|
lpDS = lpszDialableSubstring = (LPSTR) LocalAlloc(LPTR, nAddressLength + 1);
|
|
if (lpszDialableSubstring == NULL)
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("LocalAlloc failed: %x"), GetLastError()));
|
|
|
|
HandleNoMem();
|
|
goto errExit;
|
|
}
|
|
|
|
// Lets start dialing substrings!
|
|
while (nCurrentAddress < nAddressLength)
|
|
{
|
|
retryAfterError:
|
|
|
|
// Find the next undialable character
|
|
i = Xstrcspn(&lpszAddress[nCurrentAddress], szFilter);
|
|
|
|
// Was there one before the end of the Address string?
|
|
if (i + nCurrentAddress < nAddressLength)
|
|
{
|
|
// Make sure this device can handle partial dial.
|
|
if (! (lpAddressCaps -> dwAddrCapFlags &
|
|
LINEADDRCAPFLAGS_PARTIALDIAL))
|
|
{
|
|
goto errExit;
|
|
}
|
|
// Remember what the unhandled character is so we can handle it.
|
|
chUnhandledCharacter = lpszAddress[nCurrentAddress+i];
|
|
|
|
// Copy the dialable string to the Substring.
|
|
memcpy(lpszDialableSubstring, &lpszAddress[nCurrentAddress], i);
|
|
|
|
// Terminate the substring with a ';' to signify the partial dial.
|
|
lpszDialableSubstring[i] = ';';
|
|
lpszDialableSubstring[i+1] = '\0';
|
|
|
|
// Increment the address for next iteration.
|
|
nCurrentAddress += i + 1;
|
|
}
|
|
else // No more partial dials. Dial the rest of the Address.
|
|
{
|
|
lpszDialableSubstring = (LPSTR) &lpszAddress[nCurrentAddress];
|
|
chUnhandledCharacter = 0;
|
|
nCurrentAddress = nAddressLength;
|
|
}
|
|
|
|
do
|
|
{
|
|
if (bFirstDial)
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineMakeCall %8s %8x"), lpszDialableSubstring, lpCallParams));
|
|
|
|
lReturn = WaitForReply(
|
|
lineMakeCall(g_hLine, &g_hCall, lpszDialableSubstring,
|
|
0, lpCallParams) );
|
|
|
|
}
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineDial %8x %8s"), g_hCall, lpszDialableSubstring));
|
|
|
|
lReturn = WaitForReply(
|
|
lineDial(g_hCall, lpszDialableSubstring, 0) );
|
|
}
|
|
|
|
DBG_INFO((DBGARG, TEXT("LineDial return %8x"), lReturn));
|
|
|
|
switch(lReturn)
|
|
{
|
|
// We should not have received these errors because of the
|
|
// prefiltering strategy, but there may be some ill-behaved
|
|
// service providers which do not correctly set their
|
|
// devcapflags. Add the character corresponding to the error
|
|
// to the filter set and retry dialing.
|
|
//
|
|
case LINEERR_DIALBILLING:
|
|
case LINEERR_DIALDIALTONE:
|
|
case LINEERR_DIALQUIET:
|
|
case LINEERR_DIALPROMPT:
|
|
{
|
|
|
|
TSHELL_INFO(TEXT("Service Provider incorrectly sets dwDevCapFlags"));
|
|
|
|
|
|
for (i = 0; i < g_sizeofNonDialable; i++)
|
|
if (lReturn == g_sNonDialable[i].lError)
|
|
{
|
|
strcat(szFilter, g_sNonDialable[i].szToken);
|
|
}
|
|
|
|
goto retryAfterError;
|
|
}
|
|
|
|
case WAITERR_WAITABORTED:
|
|
|
|
TSHELL_INFO(TEXT("While Dialing, WaitForReply aborted."));
|
|
|
|
goto errExit;
|
|
|
|
}
|
|
|
|
if (HandleLineErr(lReturn))
|
|
continue;
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
if (bFirstDial)
|
|
DBG_INFO((DBGARG, TEXT("lineMakeCall unhandled error: %x"), lReturn));
|
|
else
|
|
DBG_INFO((DBGARG, TEXT("lineDial unhandled error: %x"), lReturn));
|
|
#endif
|
|
TSHELL_INFO(TEXT("Error Exit!"));
|
|
|
|
goto errExit;
|
|
}
|
|
|
|
}
|
|
while (lReturn != SUCCESS);
|
|
|
|
bFirstDial = FALSE;
|
|
|
|
// The dial was successful; now handle characters the service
|
|
// provider didn't (if any).
|
|
if (chUnhandledCharacter)
|
|
{
|
|
LPSTR lpMsg = "";
|
|
|
|
// First, wait until we know we can continue dialing. While the
|
|
// last string is still pending to be dialed, we can't dial another.
|
|
|
|
while(TRUE)
|
|
{
|
|
|
|
lpLineCallStatus = I_lineGetCallStatus(lpLineCallStatus, g_hCall);
|
|
if (lpLineCallStatus == NULL)
|
|
goto errExit;
|
|
|
|
// Does CallStatus say we can dial now?
|
|
if ((lpLineCallStatus->dwCallFeatures) & LINECALLFEATURE_DIAL)
|
|
{
|
|
|
|
TSHELL_INFO(TEXT("Ok to continue dialing."));
|
|
|
|
break;
|
|
}
|
|
|
|
// We can't dial yet, so wait for a CALLSTATE message
|
|
|
|
TSHELL_INFO(TEXT("Waiting for dialing to be enabled."));
|
|
|
|
|
|
if (WaitForCallState(I_LINECALLSTATE_ANY) != SUCCESS)
|
|
goto errExit;
|
|
}
|
|
|
|
for (i = 0; i < g_sizeofNonDialable; i++)
|
|
if (chUnhandledCharacter == g_sNonDialable[i].szToken[0])
|
|
lpMsg = g_sNonDialable[i].szMsg;
|
|
|
|
TCHAR achTitle[MAX_PATH];
|
|
|
|
LoadString(hInst, IDS_DIALDIALOG, achTitle, MAX_PATH);
|
|
|
|
MessageBox(g_hDlgParentWindow, lpMsg, achTitle, MB_OK);
|
|
}
|
|
|
|
} // continue dialing until we dial all Dialable Substrings.
|
|
|
|
LocalFree(lpCallParams);
|
|
LocalFree(lpDS);
|
|
LocalFree(lpAddressCaps);
|
|
if (lpLineCallStatus)
|
|
LocalFree(lpLineCallStatus);
|
|
|
|
return TRUE;
|
|
|
|
errExit:
|
|
// if lineMakeCall has already been successfully called, there's a call in progress.
|
|
// let the invoking routine shut down the call.
|
|
// if the invoker did not clean up the call, it should be done here.
|
|
|
|
if (lpLineCallStatus)
|
|
LocalFree(lpLineCallStatus);
|
|
if (lpDS)
|
|
LocalFree(lpDS);
|
|
if (lpCallParams)
|
|
LocalFree(lpCallParams);
|
|
if (lpAddressCaps)
|
|
LocalFree(lpAddressCaps);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: CreateCallParams(LPLINECALLPARAMS, LPCSTR)
|
|
//
|
|
// PURPOSE: Allocates and fills a LINECALLPARAMS structure
|
|
//
|
|
// PARAMETERS:
|
|
// lpCallParams -
|
|
// lpszDisplayableAddress -
|
|
//
|
|
// RETURN VALUE:
|
|
// Returns a LPLINECALLPARAMS ready to use for dialing DATAMODEM calls.
|
|
// Returns NULL if unable to allocate the structure.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// If a non-NULL lpCallParams is passed in, it must have been allocated
|
|
// with LocalAlloc, and can potentially be freed and reallocated. It must
|
|
// also have the dwTotalSize field correctly set.
|
|
//
|
|
//
|
|
|
|
LPLINECALLPARAMS CreateCallParams (
|
|
LPLINECALLPARAMS lpCallParams, LPCSTR lpszDisplayableAddress)
|
|
{
|
|
size_t sizeDisplayableAddress;
|
|
|
|
if (lpszDisplayableAddress == NULL)
|
|
lpszDisplayableAddress = "";
|
|
|
|
sizeDisplayableAddress = strlen(lpszDisplayableAddress) + 1;
|
|
|
|
lpCallParams = (LPLINECALLPARAMS) CheckAndReAllocBuffer(
|
|
(LPVOID) lpCallParams,
|
|
sizeof(LINECALLPARAMS) + sizeDisplayableAddress,
|
|
TEXT("CreateCallParams: "));
|
|
|
|
if (lpCallParams == NULL)
|
|
return NULL;
|
|
|
|
// This is where we configure the line for DATAMODEM usage.
|
|
lpCallParams -> dwBearerMode = LINEBEARERMODE_VOICE;
|
|
lpCallParams -> dwMediaMode = LINEMEDIAMODE_DATAMODEM;
|
|
|
|
// This specifies that we want to use only IDLE calls and
|
|
// don't want to cut into a call that might not be IDLE (ie, in use).
|
|
lpCallParams -> dwCallParamFlags = LINECALLPARAMFLAGS_IDLE;
|
|
|
|
// if there are multiple addresses on line, use first anyway.
|
|
// It will take a more complex application than a simple tty app
|
|
// to use multiple addresses on a line anyway.
|
|
lpCallParams -> dwAddressMode = LINEADDRESSMODE_ADDRESSID;
|
|
lpCallParams -> dwAddressID = 0;
|
|
|
|
// Since we don't know where we originated, leave these blank.
|
|
lpCallParams -> dwOrigAddressSize = 0;
|
|
lpCallParams -> dwOrigAddressOffset = 0;
|
|
|
|
// Unimodem ignores these values.
|
|
(lpCallParams -> DialParams) . dwDialSpeed = 0;
|
|
(lpCallParams -> DialParams) . dwDigitDuration = 0;
|
|
(lpCallParams -> DialParams) . dwDialPause = 0;
|
|
(lpCallParams -> DialParams) . dwWaitForDialtone = 0;
|
|
|
|
// Address we are dialing.
|
|
lpCallParams -> dwDisplayableAddressOffset = sizeof(LINECALLPARAMS);
|
|
lpCallParams -> dwDisplayableAddressSize = sizeDisplayableAddress;
|
|
strcpy((LPSTR)lpCallParams + sizeof(LINECALLPARAMS),
|
|
lpszDisplayableAddress);
|
|
|
|
return lpCallParams;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: long WaitForReply(long)
|
|
//
|
|
// PURPOSE: Resynchronize by waiting for a LINE_REPLY
|
|
//
|
|
// PARAMETERS:
|
|
// lRequestID - The asynchronous request ID that we're
|
|
// on a LINE_REPLY for.
|
|
//
|
|
// RETURN VALUE:
|
|
// - 0 if LINE_REPLY responded with a success.
|
|
// - LINEERR constant if LINE_REPLY responded with a LINEERR
|
|
// - 1 if the line was shut down before LINE_REPLY is received.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This function allows us to resynchronize an asynchronous
|
|
// TAPI line call by waiting for the LINE_REPLY message. It
|
|
// waits until a LINE_REPLY is received or the line is shut down.
|
|
//
|
|
// Note that this could cause re-entrancy problems as
|
|
// well as mess with any message preprocessing that might
|
|
// occur on this thread (such as TranslateAccelerator).
|
|
//
|
|
// This function should to be called from the thread that did
|
|
// lineInitialize, or the PeekMessage is on the wrong thread
|
|
// and the synchronization is not guaranteed to work. Also note
|
|
// that if another PeekMessage loop is entered while waiting,
|
|
// this could also cause synchronization problems.
|
|
//
|
|
// One more note. This function can potentially be re-entered
|
|
// if the call is dropped for any reason while waiting. If this
|
|
// happens, just drop out and assume the wait has been canceled.
|
|
// This is signaled by setting bReentered to FALSE when the function
|
|
// is entered and TRUE when it is left. If bReentered is ever TRUE
|
|
// during the function, then the function was re-entered.
|
|
//
|
|
// This function times out and returns WAITERR_WAITTIMEDOUT
|
|
// after WAITTIMEOUT milliseconds have elapsed.
|
|
//
|
|
//
|
|
|
|
|
|
long WaitForReply (long lRequestID)
|
|
{
|
|
static BOOL bReentered;
|
|
bReentered = FALSE;
|
|
|
|
if (lRequestID > SUCCESS)
|
|
{
|
|
MSG msg;
|
|
DWORD dwTimeStarted;
|
|
|
|
g_bReplyRecieved = FALSE;
|
|
g_dwRequestedID = (DWORD) lRequestID;
|
|
|
|
// Initializing this just in case there is a bug
|
|
// that sets g_bReplyRecieved without setting the reply value.
|
|
g_lAsyncReply = LINEERR_OPERATIONFAILED;
|
|
|
|
dwTimeStarted = GetTickCount();
|
|
|
|
while(!g_bReplyRecieved)
|
|
{
|
|
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
// This should only occur if the line is shut down while waiting.
|
|
if (!g_bTapiInUse || bReentered)
|
|
{
|
|
bReentered = TRUE;
|
|
return WAITERR_WAITABORTED;
|
|
}
|
|
|
|
// Its a really bad idea to timeout a wait for a LINE_REPLY.
|
|
// If we are execting a LINE_REPLY, we should wait till we get
|
|
// it; it might take a long time to dial (for example).
|
|
|
|
// If 5 seconds go by without a reply, it might be a good idea
|
|
// to display a dialog box to tell the user that a
|
|
// wait is in progress and to give the user the capability to
|
|
// abort the wait.
|
|
}
|
|
|
|
bReentered = TRUE;
|
|
return g_lAsyncReply;
|
|
}
|
|
|
|
bReentered = TRUE;
|
|
return lRequestID;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: long WaitForCallState(DWORD)
|
|
//
|
|
// PURPOSE: Wait for the line to reach a specific CallState.
|
|
//
|
|
// PARAMETERS:
|
|
// dwDesiredCallState - specific CallState to wait for.
|
|
//
|
|
// RETURN VALUE:
|
|
// Returns 0 (SUCCESS) when we reach the Desired CallState.
|
|
// Returns WAITERR_WAITTIMEDOUT if timed out.
|
|
// Returns WAITERR_WAITABORTED if call was closed while waiting.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This function allows us to synchronously wait for a line
|
|
// to reach a specific LINESTATE or until the line is shut down.
|
|
//
|
|
// Note that this could cause re-entrancy problems as
|
|
// well as mess with any message preprocessing that might
|
|
// occur on this thread (such as TranslateAccelerator).
|
|
//
|
|
// One more note. This function can potentially be re-entered
|
|
// if the call is dropped for any reason while waiting. If this
|
|
// happens, just drop out and assume the wait has been canceled.
|
|
// This is signaled by setting bReentered to FALSE when the function
|
|
// is entered and TRUE when it is left. If bReentered is ever TRUE
|
|
// during the function, then the function was re-entered.
|
|
//
|
|
// This function should to be called from the thread that did
|
|
// lineInitialize, or the PeekMessage is on the wrong thread
|
|
// and the synchronization is not guaranteed to work. Also note
|
|
// that if another PeekMessage loop is entered while waiting,
|
|
// this could also cause synchronization problems.
|
|
//
|
|
// If the constant value I_LINECALLSTATE_ANY is used for the
|
|
// dwDesiredCallState, then WaitForCallState will return SUCCESS
|
|
// upon receiving any CALLSTATE messages.
|
|
//
|
|
//
|
|
//
|
|
|
|
long WaitForCallState(DWORD dwDesiredCallState)
|
|
{
|
|
MSG msg;
|
|
DWORD dwTimeStarted;
|
|
static BOOL bReentered;
|
|
|
|
bReentered = FALSE;
|
|
|
|
dwTimeStarted = GetTickCount();
|
|
|
|
g_bCallStateReceived = FALSE;
|
|
|
|
while ((dwDesiredCallState == I_LINECALLSTATE_ANY) ||
|
|
(g_dwCallState != dwDesiredCallState))
|
|
{
|
|
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
// If we are waiting for any call state and get one, succeed.
|
|
if ((dwDesiredCallState == I_LINECALLSTATE_ANY) &&
|
|
g_bCallStateReceived)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// This should only occur if the line is shut down while waiting.
|
|
if (!g_bTapiInUse || bReentered)
|
|
{
|
|
bReentered = TRUE;
|
|
|
|
TSHELL_INFO(TEXT("WAITABORTED"));
|
|
|
|
return WAITERR_WAITABORTED;
|
|
}
|
|
|
|
// If we don't get the reply in a reasonable time, we time out.
|
|
if (GetTickCount() - dwTimeStarted > WAITTIMEOUT)
|
|
{
|
|
bReentered = TRUE;
|
|
|
|
TSHELL_INFO(TEXT("WAITTIMEDOUT"));
|
|
|
|
return WAITERR_WAITTIMEDOUT;
|
|
}
|
|
|
|
}
|
|
|
|
bReentered = TRUE;
|
|
return SUCCESS;
|
|
}
|
|
|
|
//**************************************************
|
|
// lineCallback Function and Handlers.
|
|
//**************************************************
|
|
|
|
|
|
//
|
|
// FUNCTION: lineCallbackFunc(..)
|
|
//
|
|
// PURPOSE: Receive asynchronous TAPI events
|
|
//
|
|
// PARAMETERS:
|
|
// dwDevice - Device associated with the event, if any
|
|
// dwMsg - TAPI event that occurred.
|
|
// dwCallbackInstance - User defined data supplied when opening the line.
|
|
// dwParam1 - dwMsg specific information
|
|
// dwParam2 - dwMsg specific information
|
|
// dwParam3 - dwMsg specific information
|
|
//
|
|
// RETURN VALUE:
|
|
// none
|
|
//
|
|
// COMMENTS:
|
|
// This is the function where all asynchronous events will come.
|
|
// Almost all events will be specific to an open line, but a few
|
|
// will be general TAPI events (such as LINE_REINIT).
|
|
//
|
|
// Its important to note that this callback will *ALWAYS* be
|
|
// called in the context of the thread that does the lineInitialize.
|
|
// Even if another thread (such as the COMM threads) calls the API
|
|
// that would result in the callback being called, it will be called
|
|
// in the context of the main thread (since in this sample, the main
|
|
// thread does the lineInitialize).
|
|
//
|
|
//
|
|
|
|
|
|
void CALLBACK lineCallbackFunc(
|
|
DWORD dwDevice, DWORD dwMsg, DWORD_PTR dwCallbackInstance,
|
|
DWORD_PTR dwParam1, DWORD_PTR dwParam2, DWORD_PTR dwParam3)
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
OutputDebugLineCallback(
|
|
dwDevice, dwMsg, dwCallbackInstance,
|
|
dwParam1, dwParam2, dwParam3);
|
|
#endif
|
|
|
|
// All we do is dispatch the dwMsg to the correct handler.
|
|
switch(dwMsg)
|
|
{
|
|
case LINE_CALLSTATE:
|
|
DoLineCallState(dwDevice, dwMsg, dwCallbackInstance,
|
|
dwParam1, dwParam2, dwParam3);
|
|
break;
|
|
|
|
case LINE_CLOSE:
|
|
DoLineClose(dwDevice, dwMsg, dwCallbackInstance,
|
|
dwParam1, dwParam2, dwParam3);
|
|
break;
|
|
|
|
case LINE_LINEDEVSTATE:
|
|
DoLineDevState(dwDevice, dwMsg, dwCallbackInstance,
|
|
dwParam1, dwParam2, dwParam3);
|
|
break;
|
|
|
|
case LINE_REPLY:
|
|
DoLineReply(dwDevice, dwMsg, dwCallbackInstance,
|
|
dwParam1, dwParam2, dwParam3);
|
|
break;
|
|
|
|
case LINE_CREATE:
|
|
DoLineCreate(dwDevice, dwMsg, dwCallbackInstance,
|
|
dwParam1, dwParam2, dwParam3);
|
|
break;
|
|
|
|
default:
|
|
|
|
TSHELL_INFO(TEXT("lineCallbackFunc message ignored"));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: DoLineReply(..)
|
|
//
|
|
// PURPOSE: Handle LINE_REPLY asynchronous messages.
|
|
//
|
|
// PARAMETERS:
|
|
// dwDevice - Line Handle associated with this LINE_REPLY.
|
|
// dwMsg - Should always be LINE_REPLY.
|
|
// dwCallbackInstance - Unused by this sample.
|
|
// dwParam1 - Asynchronous request ID.
|
|
// dwParam2 - success or LINEERR error value.
|
|
// dwParam3 - Unused.
|
|
//
|
|
// RETURN VALUE:
|
|
// none
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// All line API calls that return an asynchronous request ID
|
|
// will eventually cause a LINE_REPLY message. Handle it.
|
|
//
|
|
// This sample assumes only one call at time, and that we wait
|
|
// for a LINE_REPLY before making any other line API calls.
|
|
//
|
|
// The only exception to the above is that we might shut down
|
|
// the line before receiving a LINE_REPLY.
|
|
//
|
|
//
|
|
|
|
void DoLineReply(
|
|
DWORD dwDevice, DWORD dwMessage, DWORD_PTR dwCallbackInstance,
|
|
DWORD_PTR dwParam1, DWORD_PTR dwParam2, DWORD_PTR dwParam3)
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
if ((long) dwParam2 != SUCCESS)
|
|
DBG_INFO((DBGARG, TEXT("LINE_REPLY error: %x"), (long) dwParam2));
|
|
else
|
|
TSHELL_INFO(TEXT("LINE_REPLY: successfully replied."));
|
|
#endif
|
|
|
|
// If we are currently waiting for this async Request ID
|
|
// then set the global variables to acknowledge it.
|
|
if (g_dwRequestedID == dwParam1)
|
|
{
|
|
g_bReplyRecieved = TRUE;
|
|
g_lAsyncReply = (long) dwParam2;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: DoLineClose(..)
|
|
//
|
|
// PURPOSE: Handle LINE_CLOSE asynchronous messages.
|
|
//
|
|
// PARAMETERS:
|
|
// dwDevice - Line Handle that was closed.
|
|
// dwMsg - Should always be LINE_CLOSE.
|
|
// dwCallbackInstance - Unused by this sample.
|
|
// dwParam1 - Unused.
|
|
// dwParam2 - Unused.
|
|
// dwParam3 - Unused.
|
|
//
|
|
// RETURN VALUE:
|
|
// none
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This message is sent when something outside our app shuts
|
|
// down a line in use.
|
|
//
|
|
// The hLine (and any hCall on this line) are no longer valid.
|
|
//
|
|
//
|
|
|
|
void DoLineClose(
|
|
DWORD dwDevice, DWORD dwMessage, DWORD_PTR dwCallbackInstance,
|
|
DWORD_PTR dwParam1, DWORD_PTR dwParam2, DWORD_PTR dwParam3)
|
|
{
|
|
// Line has been shut down. Clean up our internal variables.
|
|
g_hLine = NULL;
|
|
g_hCall = NULL;
|
|
HangupCall(__LINE__);
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: DoLineDevState(..)
|
|
//
|
|
// PURPOSE: Handle LINE_LINEDEVSTATE asynchronous messages.
|
|
//
|
|
// PARAMETERS:
|
|
// dwDevice - Line Handle that was closed.
|
|
// dwMsg - Should always be LINE_LINEDEVSTATE.
|
|
// dwCallbackInstance - Unused by this sample.
|
|
// dwParam1 - LINEDEVSTATE constant.
|
|
// dwParam2 - Depends on dwParam1.
|
|
// dwParam3 - Depends on dwParam1.
|
|
//
|
|
// RETURN VALUE:
|
|
// none
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// The LINE_LINEDEVSTATE message is received if the state of the line
|
|
// changes. Examples are RINGING, MAINTENANCE, MSGWAITON. Very few of
|
|
// these are relevant to this sample.
|
|
//
|
|
// Assuming that any LINEDEVSTATE that removes the line from use by TAPI
|
|
// will also send a LINE_CLOSE message.
|
|
//
|
|
//
|
|
|
|
void DoLineDevState(
|
|
DWORD dwDevice, DWORD dwMessage, DWORD_PTR dwCallbackInstance,
|
|
DWORD_PTR dwParam1, DWORD_PTR dwParam2, DWORD_PTR dwParam3)
|
|
{
|
|
switch(dwParam1)
|
|
{
|
|
case LINEDEVSTATE_RINGING:
|
|
|
|
TSHELL_INFO(TEXT("Line Ringing."));
|
|
|
|
break;
|
|
|
|
case LINEDEVSTATE_REINIT:
|
|
// This is an important case! Usually means that a service provider
|
|
// has changed in such a way that requires TAPI to REINIT.
|
|
// Note that there are both 'soft' REINITs and 'hard' REINITs.
|
|
// Soft REINITs don't actually require a full shutdown but is instead
|
|
// just an informational change that historically required a REINIT
|
|
// to force the application to deal with. TAPI API Version 1.3 apps
|
|
// will still need to do a full REINIT for both hard and soft REINITs.
|
|
|
|
switch(dwParam2)
|
|
{
|
|
// This is the hard REINIT. No reason given, just REINIT.
|
|
// TAPI is waiting for everyone to shutdown.
|
|
// Our response is to immediately shutdown any calls,
|
|
// shutdown our use of TAPI and notify the user.
|
|
case 0:
|
|
ShutdownTAPI();
|
|
TSHELL_INFO(TEXT("Tapi line configuration has changed."));
|
|
break;
|
|
|
|
case LINE_CREATE:
|
|
|
|
TSHELL_INFO(TEXT("Soft REINIT: LINE_CREATE."));
|
|
|
|
DoLineCreate(dwDevice, (DWORD)dwParam2, dwCallbackInstance,
|
|
dwParam3, 0, 0);
|
|
break;
|
|
|
|
case LINE_LINEDEVSTATE:
|
|
|
|
TSHELL_INFO(TEXT("Soft REINIT: LINE_LINEDEVSTATE."));
|
|
|
|
DoLineDevState(dwDevice, (DWORD)dwParam2, dwCallbackInstance,
|
|
dwParam3, 0, 0);
|
|
break;
|
|
|
|
// There might be other reasons to send a soft reinit.
|
|
// No need to to shutdown for these.
|
|
default:
|
|
|
|
TSHELL_INFO(TEXT("Ignoring soft REINIT"));
|
|
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case LINEDEVSTATE_OUTOFSERVICE:
|
|
TSHELL_INFO(TEXT("Line selected is now Out of Service."));
|
|
HangupCall(__LINE__);
|
|
break;
|
|
|
|
case LINEDEVSTATE_DISCONNECTED:
|
|
TSHELL_INFO(TEXT("Line selected is now disconnected."));
|
|
HangupCall(__LINE__);
|
|
break;
|
|
|
|
case LINEDEVSTATE_MAINTENANCE:
|
|
TSHELL_INFO(TEXT("Line selected is now out for maintenance."));
|
|
HangupCall(__LINE__);
|
|
break;
|
|
|
|
case LINEDEVSTATE_TRANSLATECHANGE:
|
|
if (g_hDialog)
|
|
PostMessage(g_hDialog, WM_COMMAND, IDC_CONFIGURATIONCHANGED, 0);
|
|
break;
|
|
|
|
case LINEDEVSTATE_REMOVED:
|
|
|
|
TSHELL_INFO(TEXT("A Line device has been removed;")
|
|
" no action taken.");
|
|
|
|
break;
|
|
|
|
default:
|
|
TSHELL_INFO(TEXT("Unhandled LINEDEVSTATE message"));
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: DoLineCreate(..)
|
|
//
|
|
// PURPOSE: Handle LINE_LINECREATE asynchronous messages.
|
|
//
|
|
// PARAMETERS:
|
|
// dwDevice - Unused.
|
|
// dwMsg - Should always be LINE_CREATE.
|
|
// dwCallbackInstance - Unused.
|
|
// dwParam1 - dwDeviceID of new Line created.
|
|
// dwParam2 - Unused.
|
|
// dwParam3 - Unused.
|
|
//
|
|
// RETURN VALUE:
|
|
// none
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This message is new for Windows 95. It is sent when a new line is
|
|
// added to the system. This allows us to handle new lines without having
|
|
// to REINIT. This allows for much more graceful Plug and Play.
|
|
//
|
|
// This sample just changes the number of devices available and can use
|
|
// it next time a call is made. It also tells the "Dial" dialog.
|
|
//
|
|
//
|
|
|
|
void DoLineCreate(
|
|
DWORD dwDevice, DWORD dwMessage, DWORD_PTR dwCallbackInstance,
|
|
DWORD_PTR dwParam1, DWORD_PTR dwParam2, DWORD_PTR dwParam3)
|
|
{
|
|
// dwParam1 is the Device ID of the new line.
|
|
// Add one to get the number of total devices.
|
|
if (g_dwNumDevs <= dwParam1)
|
|
g_dwNumDevs = (DWORD)dwParam1+1;
|
|
if (g_hDialog)
|
|
PostMessage(g_hDialog, WM_COMMAND, IDC_LINECREATE, 0);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: DoLineCallState(..)
|
|
//
|
|
// PURPOSE: Handle LINE_CALLSTATE asynchronous messages.
|
|
//
|
|
// PARAMETERS:
|
|
// dwDevice - Handle to Call who's state is changing.
|
|
// dwMsg - Should always be LINE_CALLSTATE.
|
|
// dwCallbackInstance - Unused by this sample.
|
|
// dwParam1 - LINECALLSTATE constant specifying state change.
|
|
// dwParam2 - Specific to dwParam1.
|
|
// dwParam3 - LINECALLPRIVILEGE change, if any.
|
|
//
|
|
// RETURN VALUE:
|
|
// none
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This message is received whenever a call changes state. Lots of
|
|
// things we do, ranging from notifying the user to closing the line
|
|
// to actually connecting to the target of our phone call.
|
|
//
|
|
// What we do is usually obvious based on the call state change.
|
|
//
|
|
|
|
void DoLineCallState(
|
|
DWORD dwDevice, DWORD dwMessage, DWORD_PTR dwCallbackInstance,
|
|
DWORD_PTR dwParam1, DWORD_PTR dwParam2, DWORD_PTR dwParam3)
|
|
{
|
|
LONG lErr;
|
|
|
|
|
|
// This sets the global g_dwCallState variable so if we are waiting
|
|
// for a specific call state change, we will know when it happens.
|
|
g_dwCallState = (DWORD)dwParam1;
|
|
g_bCallStateReceived = TRUE;
|
|
|
|
// dwParam3 contains changes to LINECALLPRIVILEGE, if there are any.
|
|
switch (dwParam3)
|
|
{
|
|
case 0:
|
|
break; // no change to call state
|
|
|
|
// close line if we are made monitor. Shouldn't happen!
|
|
case LINECALLPRIVILEGE_MONITOR:
|
|
|
|
TSHELL_INFO(TEXT("line given monitor privilege; closing"));
|
|
|
|
HangupCall(__LINE__);
|
|
return;
|
|
|
|
// close line if we are made owner. Shouldn't happen!
|
|
case LINECALLPRIVILEGE_OWNER:
|
|
|
|
TSHELL_INFO(TEXT("line given owner privilege; Ready to Answer"));
|
|
|
|
break;
|
|
|
|
default: // Shouldn't happen! All cases handled.
|
|
|
|
TSHELL_INFO(TEXT("Unknown LINECALLPRIVILEGE message: closing"));
|
|
|
|
HangupCall(__LINE__);
|
|
return;
|
|
}
|
|
|
|
// dwParam1 is the specific CALLSTATE change that is occurring.
|
|
switch (dwParam1)
|
|
{
|
|
case LINECALLSTATE_OFFERING:
|
|
|
|
TSHELL_INFO(TEXT("Line Offered"));
|
|
|
|
g_hCall = (HCALL) dwDevice;
|
|
lErr = lineAccept(g_hCall, NULL, 0);
|
|
|
|
#ifdef DEBUG
|
|
if (lErr < 0)
|
|
TSHELL_INFO(TEXT("lineAccept in Offering failed."));
|
|
#endif
|
|
|
|
lErr = lineAnswer(g_hCall, NULL, 0);
|
|
|
|
#ifdef DEBUG
|
|
if (lErr < 0)
|
|
TSHELL_INFO(TEXT("lineAnswer in Offering failed."));
|
|
#endif
|
|
|
|
break;
|
|
|
|
case LINECALLSTATE_ACCEPTED:
|
|
|
|
TSHELL_INFO(TEXT("Line Accepted"));
|
|
|
|
g_hCall = (HCALL) dwDevice;
|
|
lErr = lineAnswer(g_hCall, NULL, 0);
|
|
#ifdef DEBUG
|
|
if (lErr < 0)
|
|
TSHELL_INFO(TEXT("lineAnswer in Accepted failed."));
|
|
#endif
|
|
|
|
break;
|
|
|
|
case LINECALLSTATE_DIALTONE:
|
|
|
|
TSHELL_INFO(TEXT("Dial Tone"));
|
|
|
|
break;
|
|
|
|
case LINECALLSTATE_DIALING:
|
|
|
|
TSHELL_INFO(TEXT("Dialing"));
|
|
|
|
break;
|
|
|
|
case LINECALLSTATE_PROCEEDING:
|
|
|
|
TSHELL_INFO(TEXT("Proceeding"));
|
|
|
|
break;
|
|
|
|
case LINECALLSTATE_RINGBACK:
|
|
|
|
TSHELL_INFO(TEXT("RingBack"));
|
|
|
|
break;
|
|
|
|
case LINECALLSTATE_BUSY:
|
|
|
|
TSHELL_INFO(TEXT("Line busy, shutting down"));
|
|
|
|
HangupCall(__LINE__);
|
|
if (g_hConnectionEvent)
|
|
SetEvent(g_hConnectionEvent);
|
|
break;
|
|
|
|
case LINECALLSTATE_IDLE:
|
|
|
|
TSHELL_INFO(TEXT("Line idle"));
|
|
|
|
HangupCall(__LINE__);
|
|
if (g_hConnectionEvent)
|
|
SetEvent(g_hConnectionEvent);
|
|
break;
|
|
|
|
case LINECALLSTATE_SPECIALINFO:
|
|
|
|
TSHELL_INFO(
|
|
TEXT("Special Info, probably couldn't dial number"));
|
|
|
|
HangupCall(__LINE__);
|
|
if (g_hConnectionEvent)
|
|
SetEvent(g_hConnectionEvent);
|
|
break;
|
|
|
|
case LINECALLSTATE_DISCONNECTED:
|
|
{
|
|
LPTSTR pszReasonDisconnected;
|
|
|
|
switch (dwParam2)
|
|
{
|
|
case LINEDISCONNECTMODE_NORMAL:
|
|
pszReasonDisconnected = TEXT("Remote Party Disconnected");
|
|
break;
|
|
|
|
case LINEDISCONNECTMODE_UNKNOWN:
|
|
pszReasonDisconnected = TEXT("Disconnected: Unknown reason");
|
|
break;
|
|
|
|
case LINEDISCONNECTMODE_REJECT:
|
|
pszReasonDisconnected = TEXT("Remote Party rejected call");
|
|
break;
|
|
|
|
case LINEDISCONNECTMODE_PICKUP:
|
|
pszReasonDisconnected =
|
|
TEXT("Disconnected: Local phone picked up");
|
|
break;
|
|
|
|
case LINEDISCONNECTMODE_FORWARDED:
|
|
pszReasonDisconnected = TEXT("Disconnected: Forwarded");
|
|
break;
|
|
|
|
case LINEDISCONNECTMODE_BUSY:
|
|
pszReasonDisconnected = TEXT("Disconnected: Busy");
|
|
break;
|
|
|
|
case LINEDISCONNECTMODE_NOANSWER:
|
|
pszReasonDisconnected = TEXT("Disconnected: No Answer");
|
|
break;
|
|
|
|
case LINEDISCONNECTMODE_BADADDRESS:
|
|
pszReasonDisconnected = TEXT("Disconnected: Bad Address");
|
|
break;
|
|
|
|
case LINEDISCONNECTMODE_UNREACHABLE:
|
|
pszReasonDisconnected = TEXT("Disconnected: Unreachable");
|
|
break;
|
|
|
|
case LINEDISCONNECTMODE_CONGESTION:
|
|
pszReasonDisconnected = TEXT("Disconnected: Congestion");
|
|
break;
|
|
|
|
case LINEDISCONNECTMODE_INCOMPATIBLE:
|
|
pszReasonDisconnected = TEXT("Disconnected: Incompatible");
|
|
break;
|
|
|
|
case LINEDISCONNECTMODE_UNAVAIL:
|
|
pszReasonDisconnected = TEXT("Disconnected: Unavail");
|
|
break;
|
|
|
|
case LINEDISCONNECTMODE_NODIALTONE:
|
|
pszReasonDisconnected = TEXT("Disconnected: No Dial Tone");
|
|
break;
|
|
|
|
default:
|
|
pszReasonDisconnected =
|
|
TEXT("Disconnected: LINECALLSTATE; Bad Reason");
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
TSHELL_INFO(pszReasonDisconnected);
|
|
|
|
PostHangupCall();
|
|
if (g_hConnectionEvent)
|
|
SetEvent(g_hConnectionEvent);
|
|
break;
|
|
}
|
|
|
|
|
|
case LINECALLSTATE_CONNECTED: // CONNECTED!!!
|
|
{
|
|
LPVARSTRING lpVarString = NULL;
|
|
DWORD dwSizeofVarString = sizeof(VARSTRING) + 128;
|
|
HANDLE hCommFile = NULL;
|
|
long lReturn;
|
|
|
|
// Very first, make sure this isn't a duplicated message.
|
|
// A CALLSTATE message can be sent whenever there is a
|
|
// change to the capabilities of a line, meaning that it is
|
|
// possible to receive multiple CONNECTED messages per call.
|
|
// The CONNECTED CALLSTATE message is the only one in TapiComm
|
|
// where it would cause problems if it where sent more
|
|
// than once.
|
|
|
|
if (g_bConnected)
|
|
break;
|
|
|
|
g_bConnected = TRUE;
|
|
|
|
// Get the handle to the comm port from the driver so we can start
|
|
// communicating. This is returned in a LPVARSTRING structure.
|
|
do
|
|
{
|
|
// Allocate the VARSTRING structure
|
|
lpVarString = (LPVARSTRING) CheckAndReAllocBuffer((LPVOID) lpVarString,
|
|
dwSizeofVarString, TEXT("lineGetID: "));
|
|
|
|
if (lpVarString == NULL)
|
|
goto ErrorConnecting;
|
|
|
|
// Fill the VARSTRING structure
|
|
lReturn = lineGetID(0, 0, g_hCall, LINECALLSELECT_CALL,
|
|
lpVarString, "comm/datamodem");
|
|
|
|
if (HandleLineErr(lReturn))
|
|
; // Still need to check if structure was big enough.
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineGetID unhandled error: %x"), lReturn));
|
|
|
|
goto ErrorConnecting;
|
|
}
|
|
|
|
// If the VARSTRING wasn't big enough, loop again.
|
|
if ((lpVarString -> dwNeededSize) > (lpVarString -> dwTotalSize))
|
|
{
|
|
dwSizeofVarString = lpVarString -> dwNeededSize;
|
|
lReturn = -1; // Lets loop again.
|
|
}
|
|
}
|
|
while(lReturn != SUCCESS);
|
|
|
|
|
|
TSHELL_INFO(TEXT("Connected! Starting communications!"));
|
|
|
|
|
|
// Again, the handle to the comm port is contained in a
|
|
// LPVARSTRING structure. Thus, the handle is the very first
|
|
// thing after the end of the structure. Note that the name of
|
|
// the comm port is right after the handle, but I don't want it.
|
|
hCommFile =
|
|
*((LPHANDLE)((LPBYTE)lpVarString +
|
|
lpVarString -> dwStringOffset));
|
|
|
|
// Started communications!
|
|
LPLINECALLINFO lpCInfo;
|
|
|
|
lpCInfo = NULL;
|
|
lpCInfo = I_lineGetCallInfo(lpCInfo);
|
|
if (lpCInfo)
|
|
{
|
|
g_dwRate = lpCInfo->dwRate;
|
|
LocalFree(lpCInfo);
|
|
}
|
|
if (StartComm(hCommFile, g_hConnectionEvent))
|
|
{
|
|
LocalFree(lpVarString);
|
|
break;
|
|
}
|
|
|
|
// Couldn't start communications. Clean up instead.
|
|
ErrorConnecting:
|
|
|
|
// Its very important that we close all Win32 handles.
|
|
// The CommCode module is responsible for closing the hCommFile
|
|
// handle if it succeeds in starting communications.
|
|
if (hCommFile)
|
|
CloseHandle(hCommFile);
|
|
|
|
HangupCall(__LINE__);
|
|
|
|
if (lpVarString)
|
|
LocalFree(lpVarString);
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
|
|
TSHELL_INFO(TEXT("Unhandled LINECALLSTATE message"));
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//**************************************************
|
|
// line API Wrapper Functions.
|
|
//**************************************************
|
|
|
|
|
|
//
|
|
// FUNCTION: LPVOID CheckAndReAllocBuffer(LPVOID, size_t, LPCSTR)
|
|
//
|
|
// PURPOSE: Checks and ReAllocates a buffer if necessary.
|
|
//
|
|
// PARAMETERS:
|
|
// lpBuffer - Pointer to buffer to be checked. Can be NULL.
|
|
// sizeBufferMinimum - Minimum size that lpBuffer should be.
|
|
// szApiPhrase - Phrase to print if an error occurs.
|
|
//
|
|
// RETURN VALUE:
|
|
// Returns a pointer to a valid buffer that is guarenteed to be
|
|
// at least sizeBufferMinimum size.
|
|
// Returns NULL if an error occured.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This function is a helper function intended to make all of the
|
|
// line API Wrapper Functions much simplier. It allocates (or
|
|
// reallocates) a buffer of the requested size.
|
|
//
|
|
// The returned pointer has been allocated with LocalAlloc,
|
|
// so LocalFree has to be called on it when you're finished with it,
|
|
// or there will be a memory leak.
|
|
//
|
|
// Similarly, if a pointer is passed in, it *must* have been allocated
|
|
// with LocalAlloc and it could potentially be LocalFree()d.
|
|
//
|
|
// If lpBuffer == NULL, then a new buffer is allocated. It is
|
|
// normal to pass in NULL for this parameter the first time and only
|
|
// pass in a pointer if the buffer needs to be reallocated.
|
|
//
|
|
// szApiPhrase is used only for debugging purposes.
|
|
//
|
|
// It is assumed that the buffer returned from this function will be used
|
|
// to contain a variable sized structure. Thus, the dwTotalSize field
|
|
// is always filled in before returning the pointer.
|
|
//
|
|
//
|
|
|
|
LPVOID CheckAndReAllocBuffer(
|
|
LPVOID lpBuffer, size_t sizeBufferMinimum, LPTCH szApiPhrase)
|
|
{
|
|
size_t sizeBuffer;
|
|
|
|
if (lpBuffer == NULL) // Allocate the buffer if necessary.
|
|
{
|
|
sizeBuffer = sizeBufferMinimum;
|
|
lpBuffer = (LPVOID) LocalAlloc(LPTR, sizeBuffer);
|
|
|
|
if (lpBuffer == NULL)
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("%s, LocalAlloc : %x"), szApiPhrase, GetLastError()));
|
|
HandleNoMem();
|
|
return NULL;
|
|
}
|
|
}
|
|
else // If the structure already exists, make sure its good.
|
|
{
|
|
sizeBuffer = LocalSize((HLOCAL) lpBuffer);
|
|
|
|
if (sizeBuffer == 0) // Bad pointer?
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("%s, LocalSize : %x"), szApiPhrase, GetLastError()));
|
|
return NULL;
|
|
}
|
|
|
|
// Was the buffer big enough for the structure?
|
|
if (sizeBuffer < sizeBufferMinimum)
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("%s, Reallocating structure"), szApiPhrase));
|
|
LocalFree(lpBuffer);
|
|
return CheckAndReAllocBuffer(NULL, sizeBufferMinimum, szApiPhrase);
|
|
}
|
|
|
|
// Lets zero the buffer out.
|
|
memset(lpBuffer, 0, sizeBuffer);
|
|
}
|
|
|
|
((LPVARSTRING) lpBuffer ) -> dwTotalSize = (DWORD) sizeBuffer;
|
|
return lpBuffer;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// FUNCTION: DWORD I_lineNegotiateAPIVersion(DWORD)
|
|
//
|
|
// PURPOSE: Negotiate an API Version to use for a specific device.
|
|
//
|
|
// PARAMETERS:
|
|
// dwDeviceID - device to negotiate an API Version for.
|
|
//
|
|
// RETURN VALUE:
|
|
// Returns the API Version to use for this line if successful.
|
|
// Returns 0 if negotiations fall through.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This wrapper function not only negotiates the API, but handles
|
|
// LINEERR errors that can occur while negotiating.
|
|
//
|
|
//
|
|
|
|
DWORD I_lineNegotiateAPIVersion(DWORD dwDeviceID)
|
|
{
|
|
LINEEXTENSIONID LineExtensionID;
|
|
long lReturn;
|
|
DWORD dwLocalAPIVersion;
|
|
|
|
do
|
|
{
|
|
lReturn = lineNegotiateAPIVersion(g_hLineApp, dwDeviceID,
|
|
SAMPLE_TAPI_VERSION, SAMPLE_TAPI_VERSION,
|
|
&dwLocalAPIVersion, &LineExtensionID);
|
|
|
|
// DBG_INFO((DBGARG, TEXT("version %8x, %8x, %8x %8x return %8x"),
|
|
// SAMPLE_TAPI_VERSION, SAMPLE_TAPI_VERSION,
|
|
// dwLocalAPIVersion, LineExtensionID, lReturn));
|
|
|
|
if (lReturn == LINEERR_INCOMPATIBLEAPIVERSION)
|
|
{
|
|
|
|
TSHELL_INFO(TEXT("lineNegotiateAPIVersion, INCOMPATIBLEAPIVERSION."));
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (HandleLineErr(lReturn))
|
|
continue;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineNegotiateAPIVersion unhandled error: %x"), lReturn));
|
|
return 0;
|
|
}
|
|
}
|
|
while(lReturn != SUCCESS);
|
|
|
|
return dwLocalAPIVersion;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: I_lineGetDevCaps(LPLINEDEVCAPS, DWORD , DWORD)
|
|
//
|
|
// PURPOSE: Retrieve a LINEDEVCAPS structure for the specified line.
|
|
//
|
|
// PARAMETERS:
|
|
// lpLineDevCaps - Pointer to a LINEDEVCAPS structure to use.
|
|
// dwDeviceID - device to get the DevCaps for.
|
|
// dwAPIVersion - API Version to use while getting DevCaps.
|
|
//
|
|
// RETURN VALUE:
|
|
// Returns a pointer to a LINEDEVCAPS structure if successful.
|
|
// Returns NULL if unsuccessful.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This function is a wrapper around lineGetDevCaps to make it easy
|
|
// to handle the variable sized structure and any errors received.
|
|
//
|
|
// The returned structure has been allocated with LocalAlloc,
|
|
// so LocalFree has to be called on it when you're finished with it,
|
|
// or there will be a memory leak.
|
|
//
|
|
// Similarly, if a lpLineDevCaps structure is passed in, it *must*
|
|
// have been allocated with LocalAlloc and it could potentially be
|
|
// LocalFree()d.
|
|
//
|
|
// If lpLineDevCaps == NULL, then a new structure is allocated. It is
|
|
// normal to pass in NULL for this parameter unless you want to use a
|
|
// lpLineDevCaps that has been returned by a previous I_lineGetDevCaps
|
|
// call.
|
|
//
|
|
//
|
|
|
|
LPLINECALLINFO I_lineGetCallInfo(LPLINECALLINFO lpLineCallInfo)
|
|
{
|
|
size_t sizeofLineCallInfo = sizeof(LINECALLINFO) + 128;
|
|
long lReturn;
|
|
|
|
// Continue this loop until the structure is big enough.
|
|
while(TRUE)
|
|
{
|
|
// Make sure the buffer exists, is valid and big enough.
|
|
lpLineCallInfo =
|
|
(LPLINECALLINFO) CheckAndReAllocBuffer(
|
|
(LPVOID) lpLineCallInfo, // Pointer to existing buffer, if any
|
|
sizeofLineCallInfo, // Minimum size the buffer should be
|
|
TEXT("lineCallInfo")); // Phrase to tag errors, if any.
|
|
|
|
if (lpLineCallInfo == NULL)
|
|
return NULL;
|
|
|
|
// Make the call to fill the structure.
|
|
do
|
|
{
|
|
lReturn =
|
|
lineGetCallInfo(g_hCall, lpLineCallInfo);
|
|
|
|
if (HandleLineErr(lReturn))
|
|
continue;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineGetCallInfo unhandled error: %x"), lReturn));
|
|
|
|
LocalFree(lpLineCallInfo);
|
|
return NULL;
|
|
}
|
|
}
|
|
while (lReturn != SUCCESS);
|
|
|
|
// If the buffer was big enough, then succeed.
|
|
if ((lpLineCallInfo -> dwNeededSize) <= (lpLineCallInfo -> dwTotalSize))
|
|
return lpLineCallInfo;
|
|
|
|
// Buffer wasn't big enough. Make it bigger and try again.
|
|
sizeofLineCallInfo = lpLineCallInfo->dwNeededSize;
|
|
}
|
|
}
|
|
|
|
|
|
LPLINEDEVCAPS I_lineGetDevCaps(
|
|
LPLINEDEVCAPS lpLineDevCaps,
|
|
DWORD dwDeviceID, DWORD dwAPIVersion)
|
|
{
|
|
size_t sizeofLineDevCaps = sizeof(LINEDEVCAPS) + 128;
|
|
long lReturn;
|
|
|
|
// Continue this loop until the structure is big enough.
|
|
while(TRUE)
|
|
{
|
|
// Make sure the buffer exists, is valid and big enough.
|
|
lpLineDevCaps =
|
|
(LPLINEDEVCAPS) CheckAndReAllocBuffer(
|
|
(LPVOID) lpLineDevCaps, // Pointer to existing buffer, if any
|
|
sizeofLineDevCaps, // Minimum size the buffer should be
|
|
TEXT("lineGetDevCaps")); // Phrase to tag errors, if any.
|
|
|
|
if (lpLineDevCaps == NULL)
|
|
return NULL;
|
|
|
|
// Make the call to fill the structure.
|
|
do
|
|
{
|
|
lReturn =
|
|
lineGetDevCaps(g_hLineApp,
|
|
dwDeviceID, dwAPIVersion, 0, lpLineDevCaps);
|
|
|
|
if (HandleLineErr(lReturn))
|
|
continue;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineGetDevCaps unhandled error: %x"), lReturn));
|
|
|
|
LocalFree(lpLineDevCaps);
|
|
return NULL;
|
|
}
|
|
}
|
|
while (lReturn != SUCCESS);
|
|
|
|
// If the buffer was big enough, then succeed.
|
|
if ((lpLineDevCaps -> dwNeededSize) <= (lpLineDevCaps -> dwTotalSize))
|
|
return lpLineDevCaps;
|
|
|
|
// Buffer wasn't big enough. Make it bigger and try again.
|
|
sizeofLineDevCaps = lpLineDevCaps -> dwNeededSize;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// FUNCTION: I_lineGetAddressStatus(LPLINEADDRESSSTATUS, HLINE, DWORD)
|
|
//
|
|
// PURPOSE: Retrieve a LINEADDRESSSTATUS structure for the specified line.
|
|
|
|
|
|
//
|
|
// PARAMETERS:
|
|
// lpLineAddressStatus - Pointer to a LINEADDRESSSTATUS structure to use.
|
|
// hLine - Handle of line to get the AddressStatus of.
|
|
// dwAddressID - Address ID on the hLine to be used.
|
|
//
|
|
// RETURN VALUE:
|
|
// Returns a pointer to a LINEADDRESSSTATUS structure if successful.
|
|
// Returns NULL if unsuccessful.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This function is a wrapper around lineGetAddressStatus to make it easy
|
|
// to handle the variable sized structure and any errors received.
|
|
//
|
|
// The returned structure has been allocated with LocalAlloc,
|
|
// so LocalFree has to be called on it when you're finished with it,
|
|
// or there will be a memory leak.
|
|
//
|
|
// Similarly, if a lpLineAddressStatus structure is passed in, it *must*
|
|
// have been allocated with LocalAlloc and it could potentially be
|
|
// LocalFree()d.
|
|
//
|
|
// If lpLineAddressStatus == NULL, then a new structure is allocated. It
|
|
// is normal to pass in NULL for this parameter unless you want to use a
|
|
// lpLineAddressStatus that has been returned by previous
|
|
// I_lineGetAddressStatus call.
|
|
//
|
|
//
|
|
|
|
LPLINEADDRESSSTATUS I_lineGetAddressStatus(
|
|
LPLINEADDRESSSTATUS lpLineAddressStatus,
|
|
HLINE hLine, DWORD dwAddressID)
|
|
{
|
|
size_t sizeofLineAddressStatus = sizeof(LINEADDRESSSTATUS) + 128;
|
|
long lReturn;
|
|
|
|
// Continue this loop until the structure is big enough.
|
|
while(TRUE)
|
|
{
|
|
// Make sure the buffer exists, is valid and big enough.
|
|
lpLineAddressStatus =
|
|
(LPLINEADDRESSSTATUS) CheckAndReAllocBuffer(
|
|
(LPVOID) lpLineAddressStatus,
|
|
sizeofLineAddressStatus,
|
|
TEXT("lineGetAddressStatus"));
|
|
|
|
if (lpLineAddressStatus == NULL)
|
|
return NULL;
|
|
|
|
// Make the call to fill the structure.
|
|
do
|
|
{
|
|
lReturn =
|
|
lineGetAddressStatus(hLine, dwAddressID, lpLineAddressStatus);
|
|
|
|
if (HandleLineErr(lReturn))
|
|
continue;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineGetAddressStatus unhandled error: %x"), lReturn));
|
|
|
|
LocalFree(lpLineAddressStatus);
|
|
return NULL;
|
|
}
|
|
}
|
|
while (lReturn != SUCCESS);
|
|
|
|
// If the buffer was big enough, then succeed.
|
|
if ((lpLineAddressStatus -> dwNeededSize) <=
|
|
(lpLineAddressStatus -> dwTotalSize))
|
|
{
|
|
return lpLineAddressStatus;
|
|
}
|
|
|
|
// Buffer wasn't big enough. Make it bigger and try again.
|
|
sizeofLineAddressStatus = lpLineAddressStatus -> dwNeededSize;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: I_lineGetCallStatus(LPLINECALLSTATUS, HCALL)
|
|
//
|
|
// PURPOSE: Retrieve a LINECALLSTATUS structure for the specified line.
|
|
//
|
|
// PARAMETERS:
|
|
// lpLineCallStatus - Pointer to a LINECALLSTATUS structure to use.
|
|
// hCall - Handle of call to get the CallStatus of.
|
|
//
|
|
// RETURN VALUE:
|
|
// Returns a pointer to a LINECALLSTATUS structure if successful.
|
|
// Returns NULL if unsuccessful.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This function is a wrapper around lineGetCallStatus to make it easy
|
|
// to handle the variable sized structure and any errors received.
|
|
//
|
|
// The returned structure has been allocated with LocalAlloc,
|
|
// so LocalFree has to be called on it when you're finished with it,
|
|
// or there will be a memory leak.
|
|
//
|
|
// Similarly, if a lpLineCallStatus structure is passed in, it *must*
|
|
// have been allocated with LocalAlloc and it could potentially be
|
|
// LocalFree()d.
|
|
//
|
|
// If lpLineCallStatus == NULL, then a new structure is allocated. It
|
|
// is normal to pass in NULL for this parameter unless you want to use a
|
|
// lpLineCallStatus that has been returned by previous I_lineGetCallStatus
|
|
// call.
|
|
//
|
|
//
|
|
|
|
LPLINECALLSTATUS I_lineGetCallStatus(
|
|
LPLINECALLSTATUS lpLineCallStatus,
|
|
HCALL hCall)
|
|
{
|
|
size_t sizeofLineCallStatus = sizeof(LINECALLSTATUS) + 128;
|
|
long lReturn;
|
|
|
|
// Continue this loop until the structure is big enough.
|
|
while(TRUE)
|
|
{
|
|
// Make sure the buffer exists, is valid and big enough.
|
|
lpLineCallStatus =
|
|
(LPLINECALLSTATUS) CheckAndReAllocBuffer(
|
|
(LPVOID) lpLineCallStatus,
|
|
sizeofLineCallStatus,
|
|
TEXT("lineGetCallStatus"));
|
|
|
|
if (lpLineCallStatus == NULL)
|
|
return NULL;
|
|
|
|
// Make the call to fill the structure.
|
|
do
|
|
{
|
|
lReturn =
|
|
lineGetCallStatus(hCall, lpLineCallStatus);
|
|
|
|
if (HandleLineErr(lReturn))
|
|
continue;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineGetCallStatus unhandled error: %x"), lReturn));
|
|
LocalFree(lpLineCallStatus);
|
|
return NULL;
|
|
}
|
|
}
|
|
while (lReturn != SUCCESS);
|
|
|
|
// If the buffer was big enough, then succeed.
|
|
if ((lpLineCallStatus -> dwNeededSize) <=
|
|
(lpLineCallStatus -> dwTotalSize))
|
|
{
|
|
return lpLineCallStatus;
|
|
}
|
|
|
|
// Buffer wasn't big enough. Make it bigger and try again.
|
|
sizeofLineCallStatus = lpLineCallStatus -> dwNeededSize;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: I_lineTranslateAddress
|
|
// (LPLINETRANSLATEOUTPUT, DWORD, DWORD, LPCSTR)
|
|
//
|
|
// PURPOSE: Retrieve a LINECALLSTATUS structure for the specified line.
|
|
//
|
|
// PARAMETERS:
|
|
// lpLineTranslateOutput - Pointer to a LINETRANSLATEOUTPUT structure.
|
|
// dwDeviceID - Device that we're translating for.
|
|
// dwAPIVersion - API Version to use.
|
|
// lpszDialAddress - pointer to the DialAddress string to translate.
|
|
//
|
|
// RETURN VALUE:
|
|
// Returns a pointer to a LINETRANSLATEOUTPUT structure if successful.
|
|
// Returns NULL if unsuccessful.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This function is a wrapper around lineGetTranslateOutput to make it
|
|
// easy to handle the variable sized structure and any errors received.
|
|
//
|
|
// The returned structure has been allocated with LocalAlloc,
|
|
// so LocalFree has to be called on it when you're finished with it,
|
|
// or there will be a memory leak.
|
|
//
|
|
// Similarly, if a lpLineTranslateOutput structure is passed in, it
|
|
// *must* have been allocated with LocalAlloc and it could potentially be
|
|
// LocalFree()d.
|
|
//
|
|
// If lpLineTranslateOutput == NULL, then a new structure is allocated.
|
|
// It is normal to pass in NULL for this parameter unless you want to use
|
|
// a lpLineTranslateOutput that has been returned by previous
|
|
// I_lineTranslateOutput call.
|
|
//
|
|
//
|
|
|
|
LPLINETRANSLATEOUTPUT I_lineTranslateAddress(
|
|
LPLINETRANSLATEOUTPUT lpLineTranslateOutput,
|
|
DWORD dwDeviceID, DWORD dwAPIVersion,
|
|
LPCSTR lpszDialAddress)
|
|
{
|
|
size_t sizeofLineTranslateOutput = sizeof(LINETRANSLATEOUTPUT) + 128;
|
|
long lReturn;
|
|
|
|
// Continue this loop until the structure is big enough.
|
|
while(TRUE)
|
|
{
|
|
// Make sure the buffer exists, is valid and big enough.
|
|
lpLineTranslateOutput =
|
|
(LPLINETRANSLATEOUTPUT) CheckAndReAllocBuffer(
|
|
(LPVOID) lpLineTranslateOutput,
|
|
sizeofLineTranslateOutput,
|
|
TEXT("lineTranslateOutput"));
|
|
|
|
if (lpLineTranslateOutput == NULL)
|
|
return NULL;
|
|
|
|
// Make the call to fill the structure.
|
|
do
|
|
{
|
|
// Note that CALLWAITING is disabled
|
|
// (assuming the service provider can disable it)
|
|
lReturn =
|
|
lineTranslateAddress(g_hLineApp, dwDeviceID, dwAPIVersion,
|
|
lpszDialAddress, 0,
|
|
LINETRANSLATEOPTION_CANCELCALLWAITING,
|
|
lpLineTranslateOutput);
|
|
|
|
// If the address isn't translatable, notify the user.
|
|
if (lReturn == LINEERR_INVALADDRESS)
|
|
{
|
|
TCHAR achTitle[MAX_PATH];
|
|
TCHAR achMsg[MAX_PATH];
|
|
|
|
LoadString(hInst, IDS_WARNING, achTitle, MAX_PATH);
|
|
LoadString(hInst, IDS_BADTRANSLATE, achMsg, MAX_PATH);
|
|
|
|
MessageBox(g_hDlgParentWindow, achMsg, achTitle, MB_OK);
|
|
}
|
|
|
|
if (HandleLineErr(lReturn))
|
|
continue;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineTranslateOutput unhandled error: %x"), lReturn));
|
|
LocalFree(lpLineTranslateOutput);
|
|
return NULL;
|
|
}
|
|
}
|
|
while (lReturn != SUCCESS);
|
|
|
|
// If the buffer was big enough, then succeed.
|
|
if ((lpLineTranslateOutput -> dwNeededSize) <=
|
|
(lpLineTranslateOutput -> dwTotalSize))
|
|
{
|
|
return lpLineTranslateOutput;
|
|
}
|
|
|
|
// Buffer wasn't big enough. Make it bigger and try again.
|
|
sizeofLineTranslateOutput = lpLineTranslateOutput -> dwNeededSize;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: I_lineGetAddressCaps(LPLINEADDRESSCAPS, ..)
|
|
//
|
|
// PURPOSE: Retrieve a LINEADDRESSCAPS structure for the specified line.
|
|
//
|
|
// PARAMETERS:
|
|
// lpLineAddressCaps - Pointer to a LINEADDRESSCAPS, or NULL.
|
|
// dwDeviceID - Device to get the address caps for.
|
|
// dwAddressID - This sample always assumes the first address.
|
|
// dwAPIVersion - API version negotiated for the device.
|
|
// dwExtVersion - Always 0 for this sample.
|
|
//
|
|
// RETURN VALUE:
|
|
// Returns a pointer to a LINEADDRESSCAPS structure if successful.
|
|
// Returns NULL if unsuccessful.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This function is a wrapper around lineGetAddressCaps to make it easy
|
|
// to handle the variable sized structure and any errors received.
|
|
//
|
|
// The returned structure has been allocated with LocalAlloc,
|
|
// so LocalFree has to be called on it when you're finished with it,
|
|
// or there will be a memory leak.
|
|
//
|
|
// Similarly, if a lpLineAddressCaps structure is passed in, it *must*
|
|
// have been allocated with LocalAlloc and it could potentially be
|
|
// LocalFree()d. It also *must* have the dwTotalSize field set.
|
|
//
|
|
// If lpLineAddressCaps == NULL, then a new structure is allocated. It
|
|
// is normal to pass in NULL for this parameter unless you want to use a
|
|
// lpLineCallStatus that has been returned by previous I_lineGetAddressCaps
|
|
// call.
|
|
//
|
|
//
|
|
|
|
LPLINEADDRESSCAPS I_lineGetAddressCaps (
|
|
LPLINEADDRESSCAPS lpLineAddressCaps,
|
|
DWORD dwDeviceID, DWORD dwAddressID,
|
|
DWORD dwAPIVersion, DWORD dwExtVersion)
|
|
{
|
|
size_t sizeofLineAddressCaps = sizeof(LINEADDRESSCAPS) + 128;
|
|
long lReturn;
|
|
|
|
// Continue this loop until the structure is big enough.
|
|
while(TRUE)
|
|
{
|
|
// Make sure the buffer exists, is valid and big enough.
|
|
lpLineAddressCaps =
|
|
(LPLINEADDRESSCAPS) CheckAndReAllocBuffer(
|
|
(LPVOID) lpLineAddressCaps,
|
|
sizeofLineAddressCaps,
|
|
TEXT("lineGetAddressCaps"));
|
|
|
|
if (lpLineAddressCaps == NULL)
|
|
return NULL;
|
|
|
|
// Make the call to fill the structure.
|
|
do
|
|
{
|
|
lReturn =
|
|
lineGetAddressCaps(g_hLineApp,
|
|
dwDeviceID, dwAddressID, dwAPIVersion, dwExtVersion,
|
|
lpLineAddressCaps);
|
|
|
|
if (HandleLineErr(lReturn))
|
|
continue;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineGetAddressCaps unhandled error: %x"), lReturn));
|
|
LocalFree(lpLineAddressCaps);
|
|
return NULL;
|
|
}
|
|
}
|
|
while (lReturn != SUCCESS);
|
|
|
|
// If the buffer was big enough, then succeed.
|
|
if ((lpLineAddressCaps -> dwNeededSize) <=
|
|
(lpLineAddressCaps -> dwTotalSize))
|
|
{
|
|
return lpLineAddressCaps;
|
|
}
|
|
|
|
// Buffer wasn't big enough. Make it bigger and try again.
|
|
sizeofLineAddressCaps = lpLineAddressCaps -> dwNeededSize;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//**************************************************
|
|
// LINEERR Error Handlers
|
|
//**************************************************
|
|
|
|
|
|
//
|
|
// FUNCTION: HandleLineErr(long)
|
|
//
|
|
// PURPOSE: Handle several standard LINEERR errors
|
|
//
|
|
// PARAMETERS:
|
|
// lLineErr - Error code to be handled.
|
|
//
|
|
// RETURN VALUE:
|
|
// Return TRUE if lLineErr wasn't an error, or if the
|
|
// error was successfully handled and cleared up.
|
|
// Return FALSE if lLineErr was an unhandled error.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This is the main error handler for all TAPI line APIs.
|
|
// It handles (by correcting or just notifying the user)
|
|
// most of the errors that can occur while using TAPI line APIs.
|
|
//
|
|
// Note that many errors still return FALSE (unhandled) even
|
|
// if a dialog is displayed. Often, the dialog is just notifying
|
|
// the user why the action was canceled.
|
|
//
|
|
//
|
|
//
|
|
|
|
BOOL HandleLineErr(long lLineErr)
|
|
{
|
|
// lLineErr is really an async request ID, not an error.
|
|
if (lLineErr > SUCCESS)
|
|
return FALSE;
|
|
|
|
// All we do is dispatch the correct error handler.
|
|
switch(lLineErr)
|
|
{
|
|
case SUCCESS:
|
|
return TRUE;
|
|
|
|
case LINEERR_INVALCARD:
|
|
case LINEERR_INVALLOCATION:
|
|
case LINEERR_INIFILECORRUPT:
|
|
return HandleIniFileCorrupt();
|
|
|
|
case LINEERR_NODRIVER:
|
|
return HandleNoDriver();
|
|
|
|
case LINEERR_REINIT:
|
|
return HandleReInit();
|
|
|
|
case LINEERR_NOMULTIPLEINSTANCE:
|
|
return HandleNoMultipleInstance();
|
|
|
|
case LINEERR_NOMEM:
|
|
return HandleNoMem();
|
|
|
|
case LINEERR_OPERATIONFAILED:
|
|
return HandleOperationFailed();
|
|
|
|
case LINEERR_RESOURCEUNAVAIL:
|
|
return HandleResourceUnavail();
|
|
|
|
// Unhandled errors fail.
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// FUNCTION: HandleIniFileCorrupt
|
|
//
|
|
// PURPOSE: Handle INIFILECORRUPT error.
|
|
//
|
|
// PARAMETERS:
|
|
// none
|
|
//
|
|
// RETURN VALUE:
|
|
// TRUE - error was corrected.
|
|
// FALSE - error was not corrected.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This error shouldn't happen under Windows 95 anymore. The TAPI.DLL
|
|
// takes care of correcting this problem. If it does happen, just
|
|
// notify the user.
|
|
//
|
|
|
|
BOOL HandleIniFileCorrupt()
|
|
{
|
|
lineTranslateDialog(g_hLineApp, 0, SAMPLE_TAPI_VERSION,
|
|
g_hDlgParentWindow, NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: HandleNoDriver
|
|
//
|
|
// PURPOSE: Handle NODRIVER error.
|
|
//
|
|
// PARAMETERS:
|
|
// none
|
|
//
|
|
// RETURN VALUE:
|
|
// TRUE - error was corrected.
|
|
// FALSE - error was not corrected.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
//
|
|
|
|
BOOL HandleNoDriver()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: HandleNoMultipleInstance
|
|
//
|
|
// PURPOSE: Handle NOMULTIPLEINSTANCE error.
|
|
//
|
|
// PARAMETERS:
|
|
// none
|
|
//
|
|
// RETURN VALUE:
|
|
// TRUE - error was corrected.
|
|
// FALSE - error was not corrected.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
//
|
|
|
|
BOOL HandleNoMultipleInstance()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: HandleReInit
|
|
//
|
|
// PURPOSE: Handle REINIT error.
|
|
//
|
|
// PARAMETERS:
|
|
// none
|
|
//
|
|
// RETURN VALUE:
|
|
// TRUE - error was corrected.
|
|
// FALSE - error was not corrected.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
//
|
|
|
|
BOOL HandleReInit()
|
|
{
|
|
ShutdownTAPI();
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: HandleNoMem
|
|
//
|
|
// PURPOSE: Handle NOMEM error.
|
|
//
|
|
// PARAMETERS:
|
|
// none
|
|
//
|
|
// RETURN VALUE:
|
|
// TRUE - error was corrected.
|
|
// FALSE - error was not corrected.
|
|
//
|
|
// COMMENTS:
|
|
// This is also called if I run out of memory for LocalAlloc()s
|
|
//
|
|
//
|
|
|
|
BOOL HandleNoMem()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: HandleOperationFailed
|
|
//
|
|
// PURPOSE: Handle OPERATIONFAILED error.
|
|
//
|
|
// PARAMETERS:
|
|
// none
|
|
//
|
|
// RETURN VALUE:
|
|
// TRUE - error was corrected.
|
|
// FALSE - error was not corrected.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
//
|
|
|
|
BOOL HandleOperationFailed()
|
|
{
|
|
TSHELL_INFO(TEXT("TAPI Operation Failed for unknown reasons."));
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: HandleResourceUnavail
|
|
//
|
|
// PURPOSE: Handle RESOURCEUNAVAIL error.
|
|
//
|
|
// PARAMETERS:
|
|
// none
|
|
//
|
|
// RETURN VALUE:
|
|
// TRUE - error was corrected.
|
|
// FALSE - error was not corrected.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
//
|
|
|
|
BOOL HandleResourceUnavail()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: HandleNoDevicesInstalled
|
|
//
|
|
// PURPOSE: Handle cases when we know NODEVICE error
|
|
// is returned because there are no devices installed.
|
|
//
|
|
// PARAMETERS:
|
|
// none
|
|
//
|
|
// RETURN VALUE:
|
|
// TRUE - error was corrected.
|
|
// FALSE - error was not corrected.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This function is not part of standard error handling
|
|
// but is only used when we know that the NODEVICE error
|
|
// means that no devices are installed.
|
|
//
|
|
//
|
|
|
|
BOOL HandleNoDevicesInstalled()
|
|
{
|
|
if (LaunchModemControlPanelAdd())
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: LaunchModemControlPanelAdd
|
|
//
|
|
// PURPOSE: Launch Add Modem Control Panel applet.
|
|
//
|
|
// PARAMETERS:
|
|
// none
|
|
//
|
|
// RETURN VALUE:
|
|
// TRUE - Control Panel launched successfully.
|
|
// FALSE - It didn't.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
//
|
|
|
|
BOOL LaunchModemControlPanelAdd()
|
|
{
|
|
PROCESS_INFORMATION piProcInfo;
|
|
STARTUPINFO siStartupInfo;
|
|
|
|
siStartupInfo.cb = sizeof(STARTUPINFO);
|
|
siStartupInfo.lpReserved = NULL;
|
|
siStartupInfo.lpDesktop = NULL;
|
|
siStartupInfo.lpTitle = NULL;
|
|
siStartupInfo.dwFlags = STARTF_USESHOWWINDOW;
|
|
siStartupInfo.wShowWindow = SW_SHOWNORMAL;
|
|
siStartupInfo.cbReserved2 = 0;
|
|
siStartupInfo.lpReserved2 = NULL;
|
|
|
|
// The string to launch the modem control panel is *VERY* likely
|
|
// to change on NT. If nothing else, this is 'contrl32' on NT
|
|
// instead of 'control'.
|
|
if (CreateProcess(
|
|
NULL,
|
|
"CONTROL.EXE MODEM.CPL,,ADD",
|
|
NULL, NULL, FALSE,
|
|
NORMAL_PRIORITY_CLASS,
|
|
NULL, NULL,
|
|
&siStartupInfo,
|
|
&piProcInfo))
|
|
{
|
|
CloseHandle(piProcInfo.hThread);
|
|
|
|
|
|
// Control panel 'Add New Modem' has been launched. Now we should
|
|
// wait for it to go away before continueing.
|
|
|
|
// If we WaitForSingleObject for the control panel to exit, then we
|
|
// get into a deadlock situation if we need to respond to any messages
|
|
// from the control panel.
|
|
|
|
// If we use a PeekMessage loop to wait, we run into
|
|
// message re-entrancy problems. (The user can get back to our UI
|
|
// and click 'dial' again).
|
|
|
|
// Instead, we take the easy way out and return FALSE to abort
|
|
// the current operation.
|
|
|
|
CloseHandle(piProcInfo.hProcess);
|
|
}
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("Unable to LaunchModemControlPanelAdd: %x"), GetLastError()));
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//**************************************************
|
|
//
|
|
// All the functions from this point on are used solely by the "Dial" dialog.
|
|
// This dialog is used to get both the 'phone number' address,
|
|
// the line device to be used as well as allow the user to configure
|
|
// dialing properties and the line device.
|
|
//
|
|
//**************************************************
|
|
|
|
//
|
|
// FUNCTION: DWORD I_lineNegotiateLegacyAPIVersion(DWORD)
|
|
//
|
|
// PURPOSE: Negotiate an API Version to use for a specific device.
|
|
//
|
|
// PARAMETERS:
|
|
// dwDeviceID - device to negotiate an API Version for.
|
|
//
|
|
// RETURN VALUE:
|
|
// Returns the API Version to use for this line if successful.
|
|
// Returns 0 if negotiations fall through.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This wrapper is slightly different from the I_lineNegotiateAPIVersion.
|
|
// This wrapper allows TapiComm to negotiate an API version between
|
|
// 1.3 and SAMPLE_TAPI_VERSION. Normally, this sample is specific to
|
|
// API Version SAMPLE_TAPI_VERSION. However, there are a few times when
|
|
// TapiComm needs to get information from a service provider, but also knows
|
|
// that a lower API Version would be ok. This allows TapiComm to recognize
|
|
// legacy service providers even though it can't use them. 1.3 is the
|
|
// lowest API Version a legacy service provider should support.
|
|
//
|
|
//
|
|
|
|
DWORD I_lineNegotiateLegacyAPIVersion(DWORD dwDeviceID)
|
|
{
|
|
LINEEXTENSIONID LineExtensionID;
|
|
long lReturn;
|
|
DWORD dwLocalAPIVersion;
|
|
|
|
do
|
|
{
|
|
lReturn = lineNegotiateAPIVersion(g_hLineApp, dwDeviceID,
|
|
0x00010003, SAMPLE_TAPI_VERSION,
|
|
&dwLocalAPIVersion, &LineExtensionID);
|
|
|
|
if (lReturn == LINEERR_INCOMPATIBLEAPIVERSION)
|
|
{
|
|
|
|
TSHELL_INFO(TEXT("INCOMPATIBLEAPIVERSION in Dial Dialog."));
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (HandleLineErr(lReturn))
|
|
continue;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineNegotiateAPIVersion in Dial Dialog unhandled error: %x"), lReturn));
|
|
return 0;
|
|
}
|
|
}
|
|
while(lReturn != SUCCESS);
|
|
|
|
return dwLocalAPIVersion;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: long VerifyUsableLine(DWORD)
|
|
//
|
|
// PURPOSE: Verifies that a specific line device is useable by TapiComm.
|
|
//
|
|
// PARAMETERS:
|
|
// dwDeviceID - The ID of the line device to be verified
|
|
//
|
|
// RETURN VALUE:
|
|
// Returns SUCCESS if dwDeviceID is a usable line device.
|
|
// Returns a LINENOTUSEABLE_ constant otherwise.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// VerifyUsableLine takes the give device ID and verifies step by step
|
|
// that the device supports all the features that TapiComm requires.
|
|
//
|
|
//
|
|
|
|
long VerifyUsableLine(DWORD dwDeviceID)
|
|
{
|
|
LPLINEDEVCAPS lpLineDevCaps = NULL;
|
|
LPLINEADDRESSSTATUS lpLineAddressStatus = NULL;
|
|
LPVARSTRING lpVarString = NULL;
|
|
DWORD dwAPIVersion;
|
|
long lReturn;
|
|
long lUsableLine = SUCCESS;
|
|
HLINE hLine = 0;
|
|
|
|
DBG_INFO((DBGARG, TEXT("Testing Line ID '0x%lx'"),dwDeviceID));
|
|
|
|
// The line device must support an API Version that TapiComm does.
|
|
dwAPIVersion = I_lineNegotiateAPIVersion(dwDeviceID);
|
|
if (dwAPIVersion == 0)
|
|
return LINENOTUSEABLE_ERROR;
|
|
|
|
lpLineDevCaps = I_lineGetDevCaps(lpLineDevCaps,
|
|
dwDeviceID, dwAPIVersion);
|
|
|
|
if (lpLineDevCaps == NULL)
|
|
return LINENOTUSEABLE_ERROR;
|
|
|
|
// Must support LINEBEARERMODE_VOICE
|
|
if (!(lpLineDevCaps->dwBearerModes & LINEBEARERMODE_VOICE ))
|
|
{
|
|
lUsableLine = LINENOTUSEABLE_NOVOICE;
|
|
|
|
TSHELL_INFO(TEXT("LINEBEARERMODE_VOICE not supported"));
|
|
|
|
goto DeleteBuffers;
|
|
}
|
|
|
|
// Must support LINEMEDIAMODE_DATAMODEM
|
|
if (!(lpLineDevCaps->dwMediaModes & LINEMEDIAMODE_DATAMODEM))
|
|
{
|
|
lUsableLine = LINENOTUSEABLE_NODATAMODEM;
|
|
|
|
TSHELL_INFO(TEXT("LINEMEDIAMODE_DATAMODEM not supported"));
|
|
|
|
goto DeleteBuffers;
|
|
}
|
|
|
|
// Must be able to make calls
|
|
if (!(lpLineDevCaps->dwLineFeatures & LINEFEATURE_MAKECALL))
|
|
{
|
|
lUsableLine = LINENOTUSEABLE_NOMAKECALL;
|
|
|
|
TSHELL_INFO(TEXT("LINEFEATURE_MAKECALL not supported"));
|
|
|
|
goto DeleteBuffers;
|
|
}
|
|
|
|
// It is necessary to open the line so we can check if
|
|
// there are any call appearances available. Other TAPI
|
|
// applications could be using all call appearances.
|
|
// Opening the line also checks for other possible problems.
|
|
do
|
|
{
|
|
lReturn = lineOpen(g_hLineApp, dwDeviceID, &hLine,
|
|
dwAPIVersion, 0, 0,
|
|
LINECALLPRIVILEGE_NONE, LINEMEDIAMODE_DATAMODEM,
|
|
0);
|
|
|
|
if(lReturn == LINEERR_ALLOCATED)
|
|
{
|
|
|
|
TSHELL_INFO(TEXT("Line is already in use by a non-TAPI app or another Service Provider."));
|
|
|
|
lUsableLine = LINENOTUSEABLE_ALLOCATED;
|
|
goto DeleteBuffers;
|
|
}
|
|
|
|
if (HandleLineErr(lReturn))
|
|
continue;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineOpen unhandled error: %x"), lReturn));
|
|
lUsableLine = LINENOTUSEABLE_ERROR;
|
|
goto DeleteBuffers;
|
|
}
|
|
}
|
|
while(lReturn != SUCCESS);
|
|
|
|
// Get LineAddressStatus to make sure the line isn't already in use.
|
|
lpLineAddressStatus =
|
|
I_lineGetAddressStatus(lpLineAddressStatus, hLine, 0);
|
|
|
|
if (lpLineAddressStatus == NULL)
|
|
{
|
|
lUsableLine = LINENOTUSEABLE_ERROR;
|
|
goto DeleteBuffers;
|
|
}
|
|
|
|
// Are there any available call appearances (ie: is it in use)?
|
|
if ( !((lpLineAddressStatus -> dwAddressFeatures) &
|
|
LINEADDRFEATURE_MAKECALL) )
|
|
{
|
|
|
|
TSHELL_INFO(TEXT("LINEADDRFEATURE_MAKECALL not available"));
|
|
|
|
lUsableLine = LINENOTUSEABLE_INUSE;
|
|
goto DeleteBuffers;
|
|
}
|
|
|
|
// Make sure the "comm/datamodem" device class is supported
|
|
// Note that we don't want any of the 'extra' information
|
|
// normally returned in the VARSTRING structure. All we care
|
|
// about is if lineGetID succeeds.
|
|
do
|
|
{
|
|
lpVarString = (LPVARSTRING) CheckAndReAllocBuffer((LPVOID) lpVarString,
|
|
sizeof(VARSTRING),TEXT("VerifyUsableLine:lineGetID: "));
|
|
|
|
if (lpVarString == NULL)
|
|
{
|
|
lUsableLine = LINENOTUSEABLE_ERROR;
|
|
goto DeleteBuffers;
|
|
}
|
|
|
|
lReturn = lineGetID(hLine, 0, 0, LINECALLSELECT_LINE,
|
|
lpVarString, "comm/datamodem");
|
|
|
|
if (HandleLineErr(lReturn))
|
|
continue;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineGetID unhandled error: %x"), lReturn));
|
|
lUsableLine = LINENOTUSEABLE_NOCOMMDATAMODEM;
|
|
goto DeleteBuffers;
|
|
}
|
|
}
|
|
while(lReturn != SUCCESS);
|
|
|
|
|
|
TSHELL_INFO(TEXT("Line is suitable and available for use."));
|
|
|
|
|
|
DeleteBuffers:
|
|
|
|
if (hLine)
|
|
lineClose(hLine);
|
|
if (lpLineAddressStatus)
|
|
LocalFree(lpLineAddressStatus);
|
|
if (lpLineDevCaps)
|
|
LocalFree(lpLineDevCaps);
|
|
if (lpVarString)
|
|
LocalFree(lpVarString);
|
|
|
|
hLine = NULL;
|
|
lpLineAddressStatus = NULL;
|
|
lpLineDevCaps = NULL;
|
|
lpVarString = NULL;
|
|
return lUsableLine;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: void FillTAPILine(HWND)
|
|
//
|
|
// PURPOSE: Fills the 'TAPI Line' control with the available line devices.
|
|
//
|
|
// PARAMETERS:
|
|
// hwndDlg - handle to the current "Dial" dialog
|
|
//
|
|
// RETURN VALUE:
|
|
// none
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This function enumerates through all the TAPI line devices and
|
|
// queries each for the device name. The device name is then put into
|
|
// the 'TAPI Line' control. These device names are kept in order rather
|
|
// than sorted. This allows "Dial" to know which device ID the user
|
|
// selected just by the knowing the index of the selected string.
|
|
//
|
|
// There are default values if there isn't a device name, if there is
|
|
// an error on the device, or if the device name is an empty string.
|
|
// The device name is also checked to make sure it is null terminated.
|
|
//
|
|
// Note that a Legacy API Version is negotiated. Since the fields in
|
|
// the LINEDEVCAPS structure that we are interested in haven't moved, we
|
|
// can negotiate a lower API Version than this sample is designed for
|
|
// and still be able to access the necessary structure members.
|
|
//
|
|
// The first line that is usable by TapiComm is selected as the 'default'
|
|
// line. Also note that if there was a previously selected line, this
|
|
// remains the default line. This would likely only occur if this
|
|
// function is called after the dialog has initialized once; for example,
|
|
// if a new line is added.
|
|
//
|
|
//
|
|
|
|
void FillTAPILine(HWND hwndDlg)
|
|
{
|
|
DWORD dwDeviceID;
|
|
DWORD dwAPIVersion;
|
|
LPLINEDEVCAPS lpLineDevCaps = NULL;
|
|
char szLineUnavail[] = "Line Unavailable";
|
|
char szLineUnnamed[] = "Line Unnamed";
|
|
char szLineNameEmpty[] = "Line Name is Empty";
|
|
LPSTR lpszLineName;
|
|
long lReturn;
|
|
DWORD dwDefaultDevice = MAXDWORD;
|
|
|
|
// Make sure the control is empty. If it isn't,
|
|
// hold onto the currently selected ID and then reset it.
|
|
if (SendDlgItemMessage(hwndDlg, IDC_TAPILINE, CB_GETCOUNT, 0, 0))
|
|
{
|
|
dwDefaultDevice = (DWORD)SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
|
|
CB_GETCURSEL, 0, 0);
|
|
SendDlgItemMessage(hwndDlg, IDC_TAPILINE, CB_RESETCONTENT, 0, 0);
|
|
}
|
|
|
|
for (dwDeviceID = 0; dwDeviceID < g_dwNumDevs; dwDeviceID ++)
|
|
{
|
|
dwAPIVersion = I_lineNegotiateLegacyAPIVersion(dwDeviceID);
|
|
if (dwAPIVersion)
|
|
{
|
|
lpLineDevCaps = I_lineGetDevCaps(lpLineDevCaps,
|
|
dwDeviceID, dwAPIVersion);
|
|
if (lpLineDevCaps)
|
|
{
|
|
if ((lpLineDevCaps -> dwLineNameSize) &&
|
|
(lpLineDevCaps -> dwLineNameOffset) &&
|
|
(lpLineDevCaps -> dwStringFormat == STRINGFORMAT_ASCII))
|
|
{
|
|
// This is the name of the device.
|
|
lpszLineName = ((char *) lpLineDevCaps) +
|
|
lpLineDevCaps -> dwLineNameOffset;
|
|
|
|
if (lpszLineName[0] != '\0')
|
|
{
|
|
// Reverse indented to make this fit
|
|
|
|
// Make sure the device name is null terminated.
|
|
if (lpszLineName[lpLineDevCaps->dwLineNameSize -1] != '\0')
|
|
{
|
|
// If the device name is not null terminated, null
|
|
// terminate it. Yes, this looses the end character.
|
|
// Its a bug in the service provider.
|
|
lpszLineName[lpLineDevCaps->dwLineNameSize-1] = '\0';
|
|
|
|
DBG_INFO((DBGARG, TEXT("Device name for device 0x%lx is not null terminated."),
|
|
dwDeviceID));
|
|
}
|
|
}
|
|
else // Line name started with a NULL.
|
|
lpszLineName = szLineNameEmpty;
|
|
}
|
|
else // DevCaps doesn't have a valid line name. Unnamed.
|
|
lpszLineName = szLineUnnamed;
|
|
}
|
|
else // Couldn't GetDevCaps. Line is unavail.
|
|
lpszLineName = szLineUnavail;
|
|
}
|
|
else // Couldn't NegotiateAPIVersion. Line is unavail.
|
|
lpszLineName = szLineUnavail;
|
|
|
|
// Put the device name into the control
|
|
lReturn = (long)SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
|
|
CB_ADDSTRING, 0, (LPARAM) (LPCTSTR) lpszLineName);
|
|
|
|
// If this line is usable and we don't have a default initial
|
|
// line yet, make this the initial line.
|
|
if ((lpszLineName != szLineUnavail) &&
|
|
(dwDefaultDevice == MAXDWORD) &&
|
|
(VerifyUsableLine(dwDeviceID) == SUCCESS))
|
|
{
|
|
dwDefaultDevice = dwDeviceID;
|
|
}
|
|
}
|
|
|
|
if (lpLineDevCaps)
|
|
LocalFree(lpLineDevCaps);
|
|
|
|
if (dwDefaultDevice == MAXDWORD)
|
|
dwDefaultDevice = 0;
|
|
|
|
// Set the initial default line
|
|
SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
|
|
CB_SETCURSEL, dwDefaultDevice, 0);
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: BOOL VerifyAndWarnUsableLine(HWND)
|
|
//
|
|
// PURPOSE: Verifies the line device selected by the user.
|
|
//
|
|
// PARAMETERS:
|
|
// hwndDlg - The handle to the current "Dial" dialog.
|
|
//
|
|
// RETURN VALUE:
|
|
// Returns TRUE if the currently selected line device is useable
|
|
// by TapiComm. Returns FALSE if it isn't.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This function is very specific to the "Dial" dialog. It gets
|
|
// the device selected by the user from the 'TAPI Line' control and
|
|
// VerifyUsableLine to make sure this line device is usable. If the
|
|
// line isn't useable, it notifies the user and disables the 'Dial'
|
|
// button so that the user can't initiate a call with this line.
|
|
//
|
|
// This function is also responsible for filling in the line specific
|
|
// icon found on the "Dial" dialog.
|
|
//
|
|
//
|
|
|
|
BOOL VerifyAndWarnUsableLine(HWND hwndDlg)
|
|
{
|
|
DWORD dwDeviceID;
|
|
long lReturn;
|
|
HICON hIcon = 0;
|
|
HWND hControlWnd;
|
|
|
|
// Get the selected line device.
|
|
dwDeviceID = (DWORD)SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
|
|
CB_GETCURSEL, 0, 0);
|
|
|
|
// Get the "comm" device icon associated with this line device.
|
|
lReturn = lineGetIcon(dwDeviceID, "comm", &hIcon);
|
|
|
|
if (lReturn == SUCCESS)
|
|
SendDlgItemMessage(hwndDlg, IDC_LINEICON, STM_SETICON,
|
|
(WPARAM) hIcon, 0);
|
|
else
|
|
// Any failure to get an icon makes us use the default icon.
|
|
SendDlgItemMessage(hwndDlg, IDC_LINEICON, WM_SETTEXT,
|
|
0, (LPARAM) (LPCTSTR) "TapiComm");
|
|
|
|
/* // It turns out that TAPI will always return an icon, even if
|
|
// the device class isn't supported by the TSP or even if the TSP
|
|
// doesn't return any icons at all. This code is unnecessary.
|
|
// The only reason lineGetIcon would fail is due to resource problems.
|
|
|
|
else
|
|
{
|
|
// If the line doesn't have a "comm" device icon, use its default one.
|
|
lReturn = lineGetIcon(dwDeviceID, NULL, &hIcon);
|
|
if (lReturn == SUCCESS)
|
|
{
|
|
|
|
TSHELL_INFO(TEXT("Line doesn't support a \"comm\" icon."));
|
|
|
|
SendDlgItemMessage(hwndDlg, IDC_LINEICON, STM_SETICON,
|
|
(WPARAM) hIcon, 0);
|
|
}
|
|
else
|
|
{
|
|
// If lineGetIcon fails, just use TapiComms icon.
|
|
DBG_INFO((DBGARG, TEXT("lineGetIcon: %x"), lReturn));
|
|
|
|
SendDlgItemMessage(hwndDlg, IDC_LINEICON, WM_SETTEXT,
|
|
0, (LPARAM) (LPCTSTR) "TapiComm");
|
|
}
|
|
}
|
|
*/
|
|
|
|
// Verify if the device is usable by TapiComm.
|
|
lReturn = VerifyUsableLine(dwDeviceID);
|
|
|
|
// Enable or disable the 'Dial' button, depending on if the line is ok.
|
|
// Make sure there is a number to dial before enabling the button.
|
|
hControlWnd = GetDlgItem(hwndDlg, IDC_DIAL);
|
|
|
|
//
|
|
// Store Canon
|
|
//
|
|
if (g_szTranslatedNumber[0] = 0x00)
|
|
{
|
|
EnableWindow(hControlWnd, FALSE);
|
|
}
|
|
else
|
|
EnableWindow(hControlWnd, (lReturn == SUCCESS));
|
|
|
|
// Any errors on this line prevent us from configuring it
|
|
// or using dialing properties.
|
|
if (lReturn == LINENOTUSEABLE_ERROR)
|
|
{
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_CONFIGURELINE), FALSE);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_DIALINGPROPERTIES), FALSE);
|
|
}
|
|
else
|
|
{
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_CONFIGURELINE), TRUE);
|
|
if (SendDlgItemMessage(hwndDlg, IDC_USEDIALINGRULES, BM_GETCHECK, 0, 0))
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_DIALINGPROPERTIES), TRUE);
|
|
}
|
|
|
|
switch(lReturn)
|
|
{
|
|
case SUCCESS:
|
|
g_dwDeviceID = dwDeviceID;
|
|
return TRUE;
|
|
|
|
case LINENOTUSEABLE_ERROR:
|
|
TSHELL_INFO(TEXT("The selected line is incompatible with DirectPlay"));
|
|
break;
|
|
case LINENOTUSEABLE_NOVOICE:
|
|
TSHELL_INFO(TEXT("The selected line doesn't support VOICE capabilities",));
|
|
break;
|
|
case LINENOTUSEABLE_NODATAMODEM:
|
|
TSHELL_INFO(TEXT("The selected line doesn't support DATAMODEM capabilities",));
|
|
break;
|
|
case LINENOTUSEABLE_NOMAKECALL:
|
|
TSHELL_INFO(TEXT("The selected line doesn't support MAKECALL capabilities",));
|
|
break;
|
|
case LINENOTUSEABLE_ALLOCATED:
|
|
TSHELL_INFO(TEXT("The selected line is already in use by a non-TAPI application",));
|
|
break;
|
|
case LINENOTUSEABLE_INUSE:
|
|
TSHELL_INFO(TEXT("The selected line is already in use by a TAPI application",));
|
|
break;
|
|
|
|
case LINENOTUSEABLE_NOCOMMDATAMODEM:
|
|
TSHELL_INFO(TEXT("The selected line doesn't support the COMM/DATAMODEM device class",));
|
|
break;
|
|
}
|
|
|
|
// g_dwDeviceID == MAXDWORD mean the selected device isn't usable.
|
|
g_dwDeviceID = MAXDWORD;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: void FillCountryCodeList(HWND, DWORD)
|
|
//
|
|
// PURPOSE: Fill the 'Country Code' control
|
|
//
|
|
// PARAMETERS:
|
|
// hwndDlg - handle to the current "Dial" dialog
|
|
// dwDefaultCountryID - ID of the 'default' country to be selected
|
|
//
|
|
// RETURN VALUE:
|
|
// none
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This function fills the 'Country Code' control with country names.
|
|
// The country code is appended to the end of the name and the names
|
|
// are added to the control sorted. Because the country code is
|
|
// embedded in the string along with the country name, there is no need
|
|
// for any of the country information structures to be kept around. The
|
|
// country code can be extracted from the selected string at any time.
|
|
//
|
|
//
|
|
|
|
void FillCountryCodeList(HWND hwndDlg, DWORD dwDefaultCountryID)
|
|
{
|
|
LPLINECOUNTRYLIST lpLineCountryList = NULL;
|
|
DWORD dwSizeofCountryList = sizeof(LINECOUNTRYLIST);
|
|
long lReturn;
|
|
DWORD dwCountry;
|
|
LPLINECOUNTRYENTRY lpLineCountryEntries;
|
|
char szRenamedCountry[256];
|
|
|
|
// Get the country information stored in TAPI
|
|
do
|
|
{
|
|
lpLineCountryList = (LPLINECOUNTRYLIST) CheckAndReAllocBuffer(
|
|
(LPVOID) lpLineCountryList, dwSizeofCountryList,
|
|
TEXT("FillCountryCodeList"));
|
|
|
|
if (lpLineCountryList == NULL)
|
|
return;
|
|
|
|
lReturn = lineGetCountry (0, SAMPLE_TAPI_VERSION, lpLineCountryList);
|
|
|
|
if (HandleLineErr(lReturn))
|
|
;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineGetCountry unhandled error: %x"), lReturn));
|
|
LocalFree(lpLineCountryList);
|
|
return;
|
|
}
|
|
|
|
if ((lpLineCountryList -> dwNeededSize) >
|
|
(lpLineCountryList -> dwTotalSize))
|
|
{
|
|
dwSizeofCountryList = lpLineCountryList ->dwNeededSize;
|
|
lReturn = -1; // Lets loop again.
|
|
}
|
|
}
|
|
while (lReturn != SUCCESS);
|
|
|
|
// Find the first country entry
|
|
lpLineCountryEntries = (LPLINECOUNTRYENTRY)
|
|
(((LPBYTE) lpLineCountryList)
|
|
+ lpLineCountryList -> dwCountryListOffset);
|
|
|
|
// Now enumerate through all the countries
|
|
for (dwCountry = 0;
|
|
dwCountry < lpLineCountryList -> dwNumCountries;
|
|
dwCountry++)
|
|
{
|
|
// append the country code to the country name
|
|
wsprintf(szRenamedCountry,"%s (%lu)",
|
|
(((LPSTR) lpLineCountryList) +
|
|
lpLineCountryEntries[dwCountry].dwCountryNameOffset),
|
|
lpLineCountryEntries[dwCountry].dwCountryCode);
|
|
|
|
// Now put this country name / code string into the combobox
|
|
lReturn = (long)SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE, CB_ADDSTRING,
|
|
0, (LPARAM) (LPCTSTR) szRenamedCountry);
|
|
|
|
// If this country is the default country, select it.
|
|
if (lpLineCountryEntries[dwCountry].dwCountryID
|
|
== dwDefaultCountryID)
|
|
{
|
|
SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE, CB_SETCURSEL, lReturn, 0);
|
|
}
|
|
}
|
|
|
|
LocalFree(lpLineCountryList);
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: void FillLocationInfo(HWND, LPSTR, LPDWORD, LPSTR)
|
|
//
|
|
// PURPOSE: Fill (or refill) the 'Your Location' control
|
|
//
|
|
// PARAMETERS:
|
|
// hwndDlg - handle to the current "Dial" dialog
|
|
// lpszCurrentLocation - Name of current location, or NULL
|
|
// lpdwCountryID - location to store the current country ID or NULL
|
|
// lpszAreaCode - location to store the current area code or NULL
|
|
//
|
|
// RETURN VALUE:
|
|
// none
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This function is moderately multipurpose.
|
|
//
|
|
// If lpszCurrentLocation is NULL, then the 'Your Location' control
|
|
// is filled with all the locations stored in TAPI and the TAPI 'default'
|
|
// location is selected. This is done during initialization and
|
|
// also after the 'Dialing Properties' dialog has been displayed.
|
|
// This last is done because the user can change the current location
|
|
// or add and delete locations while in the 'Dialing Properties' dialog.
|
|
//
|
|
// If lpszCurrentLocation is a valid string pointer, then it is assumed
|
|
// that the 'Your Location' control is already filled and that the user
|
|
// is selecting a specific location. In this case, all of the existing
|
|
// TAPI locations are enumerated until the specified location is found.
|
|
// At this point, the specified location is set to the current location.
|
|
//
|
|
// In either case, if lpdwCountryID is not NULL, it is filled with the
|
|
// country ID for the current location. If lpszAreaCode is not NULL, it
|
|
// is filled with the area code defined for the current location. These
|
|
// values can be used later to initialize other "Dial" controls.
|
|
//
|
|
// This function also fills the 'Calling Card' control based on
|
|
// the information stored in the current location.
|
|
//
|
|
//
|
|
|
|
void FillLocationInfo(HWND hwndDlg, LPSTR lpszCurrentLocation,
|
|
LPDWORD lpdwCountryID, LPSTR lpszAreaCode)
|
|
{
|
|
LPLINETRANSLATECAPS lpTranslateCaps = NULL;
|
|
DWORD dwSizeofTranslateCaps = sizeof(LINETRANSLATECAPS);
|
|
long lReturn;
|
|
DWORD dwCounter;
|
|
LPLINELOCATIONENTRY lpLocationEntry;
|
|
LPLINECARDENTRY lpLineCardEntry = NULL;
|
|
DWORD dwPreferredCardID = MAXDWORD;
|
|
TCHAR achMsg[MAX_PATH];
|
|
|
|
// First, get the TRANSLATECAPS
|
|
do
|
|
{
|
|
lpTranslateCaps = (LPLINETRANSLATECAPS) CheckAndReAllocBuffer(
|
|
(LPVOID) lpTranslateCaps, dwSizeofTranslateCaps,
|
|
TEXT(TEXT("FillLocationInfo")));
|
|
|
|
if (lpTranslateCaps == NULL)
|
|
return;
|
|
|
|
lReturn = lineGetTranslateCaps(g_hLineApp, SAMPLE_TAPI_VERSION,
|
|
lpTranslateCaps);
|
|
|
|
if (HandleLineErr(lReturn))
|
|
;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineGetTranslateCaps unhandled error: %x"), lReturn));
|
|
LocalFree(lpTranslateCaps);
|
|
return;
|
|
}
|
|
|
|
if ((lpTranslateCaps -> dwNeededSize) >
|
|
(lpTranslateCaps -> dwTotalSize))
|
|
{
|
|
dwSizeofTranslateCaps = lpTranslateCaps ->dwNeededSize;
|
|
lReturn = -1; // Lets loop again.
|
|
}
|
|
}
|
|
while(lReturn != SUCCESS);
|
|
|
|
// Find the location information in the TRANSLATECAPS
|
|
lpLocationEntry = (LPLINELOCATIONENTRY)
|
|
(((LPBYTE) lpTranslateCaps) + lpTranslateCaps->dwLocationListOffset);
|
|
|
|
// If lpszCurrentLocation, then make that location 'current'
|
|
if (lpszCurrentLocation)
|
|
{
|
|
// loop through all locations, looking for a location match
|
|
for(dwCounter = 0;
|
|
dwCounter < lpTranslateCaps -> dwNumLocations;
|
|
dwCounter++)
|
|
{
|
|
if (strcmp((((LPSTR) lpTranslateCaps) +
|
|
lpLocationEntry[dwCounter].dwLocationNameOffset),
|
|
lpszCurrentLocation)
|
|
== 0)
|
|
{
|
|
// Found it! Set the current location.
|
|
lineSetCurrentLocation(g_hLineApp,
|
|
lpLocationEntry[dwCounter].dwPermanentLocationID);
|
|
|
|
// Set the return values.
|
|
if (lpdwCountryID)
|
|
*lpdwCountryID = lpLocationEntry[dwCounter].dwCountryID;
|
|
|
|
if (lpszAreaCode)
|
|
strcpy(lpszAreaCode, (((LPSTR) lpTranslateCaps) +
|
|
lpLocationEntry[dwCounter].dwCityCodeOffset));
|
|
|
|
// Store the preferred card ID for later use.
|
|
dwPreferredCardID = lpLocationEntry[dwCounter].dwPreferredCardID;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Was a match for lpszCurrentLocation found?
|
|
if (dwPreferredCardID == MAXDWORD)
|
|
{
|
|
|
|
TSHELL_INFO(TEXT("lpszCurrentLocation not found"));
|
|
|
|
LoadString( hInst, IDS_LOCATIONERR, achMsg, sizeof(achMsg));
|
|
|
|
SendDlgItemMessage(hwndDlg, IDC_CALLINGCARD, WM_SETTEXT, 0,
|
|
(LPARAM) achMsg);
|
|
LocalFree(lpTranslateCaps);
|
|
return;
|
|
}
|
|
}
|
|
else // fill the combobox and use the TAPI 'current' location.
|
|
{
|
|
// First empty the combobox
|
|
SendDlgItemMessage(hwndDlg, IDC_LOCATION, CB_RESETCONTENT, 0, 0);
|
|
|
|
// enumerate all the locations
|
|
for(dwCounter = 0;
|
|
dwCounter < lpTranslateCaps -> dwNumLocations;
|
|
dwCounter++)
|
|
{
|
|
// Put each one into the combobox
|
|
lReturn = (long)SendDlgItemMessage(hwndDlg, IDC_LOCATION, CB_ADDSTRING,
|
|
0, (LPARAM) (((LPBYTE) lpTranslateCaps) +
|
|
lpLocationEntry[dwCounter].dwLocationNameOffset));
|
|
|
|
// Is this location the 'current' location?
|
|
if (lpLocationEntry[dwCounter].dwPermanentLocationID ==
|
|
lpTranslateCaps->dwCurrentLocationID)
|
|
{
|
|
// Return the requested information
|
|
if (lpdwCountryID)
|
|
*lpdwCountryID = lpLocationEntry[dwCounter].dwCountryID;
|
|
|
|
if (lpszAreaCode)
|
|
strcpy(lpszAreaCode, (((LPSTR) lpTranslateCaps) +
|
|
lpLocationEntry[dwCounter].dwCityCodeOffset));
|
|
|
|
// Set this to be the active location.
|
|
SendDlgItemMessage(hwndDlg, IDC_LOCATION, CB_SETCURSEL, lReturn, 0);
|
|
dwPreferredCardID = lpLocationEntry[dwCounter].dwPreferredCardID;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now locate the prefered card and display it.
|
|
|
|
lpLineCardEntry = (LPLINECARDENTRY)
|
|
(((LPBYTE) lpTranslateCaps) + lpTranslateCaps->dwCardListOffset);
|
|
|
|
for(dwCounter = 0;
|
|
dwCounter < lpTranslateCaps -> dwNumCards;
|
|
dwCounter++)
|
|
{
|
|
if (lpLineCardEntry[dwCounter].dwPermanentCardID == dwPreferredCardID)
|
|
{
|
|
SendDlgItemMessage(hwndDlg, IDC_CALLINGCARD, WM_SETTEXT, 0,
|
|
(LPARAM) (((LPBYTE) lpTranslateCaps) +
|
|
lpLineCardEntry[dwCounter].dwCardNameOffset));
|
|
break;
|
|
}
|
|
}
|
|
|
|
LocalFree(lpTranslateCaps);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// FUNCTION: void UseDialingRules(HWND)
|
|
//
|
|
// PURPOSE: Enable/disable Dialing Rule controls
|
|
//
|
|
// PARAMETERS:
|
|
// hwndDlg - handle to the current "Dial" dialog
|
|
//
|
|
// RETURN VALUE:
|
|
// none
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// The sole purpose of this function is to enable or disable
|
|
// the controls that apply to dialing rules if the
|
|
// "Use Country Code and Area Code" checkbox is checked or unchecked,
|
|
// as appropriate.
|
|
//
|
|
//
|
|
|
|
void UseDialingRules(HWND hwndDlg)
|
|
{
|
|
HWND hControl;
|
|
BOOL bEnableWindow;
|
|
|
|
bEnableWindow = (BOOL)SendDlgItemMessage(hwndDlg,
|
|
IDC_USEDIALINGRULES, BM_GETCHECK, 0, 0);
|
|
|
|
hControl = GetDlgItem(hwndDlg, IDC_STATICCOUNTRYCODE);
|
|
EnableWindow(hControl, bEnableWindow);
|
|
|
|
hControl = GetDlgItem(hwndDlg, IDC_COUNTRYCODE);
|
|
EnableWindow(hControl, bEnableWindow);
|
|
|
|
hControl = GetDlgItem(hwndDlg, IDC_STATICAREACODE);
|
|
EnableWindow(hControl, bEnableWindow);
|
|
|
|
hControl = GetDlgItem(hwndDlg, IDC_AREACODE);
|
|
EnableWindow(hControl, bEnableWindow);
|
|
|
|
hControl = GetDlgItem(hwndDlg, IDC_STATICLOCATION);
|
|
EnableWindow(hControl, bEnableWindow);
|
|
|
|
hControl = GetDlgItem(hwndDlg, IDC_LOCATION);
|
|
EnableWindow(hControl, bEnableWindow);
|
|
|
|
hControl = GetDlgItem(hwndDlg, IDC_STATICCALLINGCARD);
|
|
EnableWindow(hControl, bEnableWindow);
|
|
|
|
hControl = GetDlgItem(hwndDlg, IDC_CALLINGCARD);
|
|
EnableWindow(hControl, bEnableWindow);
|
|
|
|
if (IsWindowEnabled(GetDlgItem(hwndDlg, IDC_CONFIGURELINE)))
|
|
{
|
|
hControl = GetDlgItem(hwndDlg, IDC_DIALINGPROPERTIES);
|
|
EnableWindow(hControl, bEnableWindow);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: void DisplayPhoneNumber(HWND)
|
|
//
|
|
// PURPOSE: Create, Translate and Display the Phone Number
|
|
//
|
|
// PARAMETERS:
|
|
// hwndDlg - handle to the current "Dial" dialog
|
|
//
|
|
// RETURN VALUE:
|
|
// none
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This function uses the information stored in many other controls
|
|
// to build the phone number, translate it, and display it. Also
|
|
// makes sure the Dial button is enabled or disabled, based on if the
|
|
// number can be dialed or not.
|
|
//
|
|
// There are actually three phone numbers generated during this
|
|
// process: canonical, dialable and displayable. Normally, only the
|
|
// displayable number is shown to the user; the other two numbers are
|
|
// to be used by the program internally. However, for demonstration
|
|
// purposes (and because it is cool for developers to see these numbers),
|
|
// all three numbers are displayed.
|
|
//
|
|
|
|
void DisplayPhoneNumber(HWND hwndDlg)
|
|
{
|
|
char szPreTranslatedNumber[128] = "";
|
|
int nPreTranslatedSize = 0;
|
|
char szTempBuffer[512];
|
|
int i;
|
|
DWORD dwDeviceID;
|
|
LPLINETRANSLATEOUTPUT lpLineTranslateOutput = NULL;
|
|
|
|
// Disable the 'dial' button if there isn't a number to dial
|
|
if (0 == SendDlgItemMessage(hwndDlg, IDC_PHONENUMBER,
|
|
WM_GETTEXTLENGTH, 0, 0))
|
|
{
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_DIAL), FALSE);
|
|
return;
|
|
}
|
|
|
|
// If we use the dialing rules, lets make canonical format.
|
|
// Canonical format is explained in the TAPI documentation and the
|
|
// string format needs to be followed very strictly.
|
|
if (SendDlgItemMessage(hwndDlg, IDC_USEDIALINGRULES,
|
|
BM_GETCHECK, 0, 0))
|
|
{
|
|
// First character *has* to be the plus sign.
|
|
szPreTranslatedNumber[0] = '+';
|
|
nPreTranslatedSize = 1;
|
|
|
|
// The country code *has* to be next.
|
|
// Country code was stored in the string with the country
|
|
// name and needs to be extracted at this point.
|
|
i = (int)SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE,
|
|
CB_GETCURSEL, 0, 0);
|
|
SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE,
|
|
CB_GETLBTEXT, (WPARAM) i, (LPARAM) (LPCTSTR) szTempBuffer);
|
|
|
|
// Country code is at the end of the string, surounded by parens.
|
|
// This makes it easy to identify the country code.
|
|
i = strlen(szTempBuffer);
|
|
while(szTempBuffer[--i] != '(');
|
|
|
|
while(szTempBuffer[++i] != ')')
|
|
szPreTranslatedNumber[nPreTranslatedSize++] = szTempBuffer[i];
|
|
|
|
// Next is the area code.
|
|
i = (int)SendDlgItemMessage(hwndDlg, IDC_AREACODE, WM_GETTEXT,
|
|
510, (LPARAM) (LPCTSTR) szTempBuffer);
|
|
|
|
// Note that the area code is optional. If it is included,
|
|
// then it has to be preceeded by *exactly* one space and it
|
|
// *has* to be surrounded by parens.
|
|
if (i)
|
|
nPreTranslatedSize +=
|
|
wsprintf(&szPreTranslatedNumber[nPreTranslatedSize],
|
|
" (%s)", szTempBuffer);
|
|
|
|
// There has to be *exactly* one space before the rest of the number.
|
|
szPreTranslatedNumber[nPreTranslatedSize++] = ' ';
|
|
|
|
// At this point, the phone number is appended to the
|
|
// canonical number. The next step is the same whether canonical
|
|
// format is used or not; just the prepended area code and
|
|
// country code are different.
|
|
}
|
|
|
|
SendDlgItemMessage(hwndDlg, IDC_PHONENUMBER, WM_GETTEXT,
|
|
510, (LPARAM) (LPCTSTR) szTempBuffer);
|
|
|
|
strcat(&szPreTranslatedNumber[nPreTranslatedSize], szTempBuffer);
|
|
|
|
dwDeviceID = (DWORD)SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
|
|
CB_GETCURSEL, 0, 0);
|
|
|
|
// Translate the address!
|
|
lpLineTranslateOutput = I_lineTranslateAddress(
|
|
lpLineTranslateOutput, dwDeviceID, SAMPLE_TAPI_VERSION,
|
|
szPreTranslatedNumber);
|
|
|
|
// Unable to translate it?
|
|
if (lpLineTranslateOutput == NULL)
|
|
{
|
|
g_szTranslatedNumber[0] = 0x00;
|
|
g_szDisplayableAddress[0] = 0x00;
|
|
g_szDialableAddress[0] = 0x00;
|
|
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_DIAL), FALSE);
|
|
return;
|
|
}
|
|
|
|
// Is the selected device useable with TapiComm?
|
|
if (g_dwDeviceID != MAXDWORD)
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_DIAL), TRUE);
|
|
|
|
// Fill the appropriate phone number controls.
|
|
strcpy( g_szTranslatedNumber, szPreTranslatedNumber);
|
|
strcpy( g_szDialableAddress, ((LPSTR) lpLineTranslateOutput +
|
|
lpLineTranslateOutput -> dwDialableStringOffset));
|
|
|
|
strcpy( g_szDisplayableAddress, ((LPSTR) lpLineTranslateOutput +
|
|
lpLineTranslateOutput -> dwDisplayableStringOffset));
|
|
|
|
LocalFree(lpLineTranslateOutput);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: void PreConfigureDevice(HWND, DWORD)
|
|
//
|
|
// PURPOSE:
|
|
//
|
|
// PARAMETERS:
|
|
// hwndDlg - handle to the current "Dial" dialog
|
|
// dwDeviceID - line device to be configured
|
|
//
|
|
// RETURN VALUE:
|
|
// none
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// At one point, PreConfigureDevice used lineConfigDialog to
|
|
// configure the device. This has the unfortunate effect of configuring
|
|
// the device immediately, even if it is in use by another TAPI app.
|
|
// This can be really bad if data communications are already in
|
|
// progress (like with RAS).
|
|
//
|
|
// Now, PreConfigureDevice uses lineConfigDialogEdit to give the
|
|
// user the configuration UI, but it doesn't actually do anything to
|
|
// the line device. TapiComm stores the configuration information so
|
|
// that it can be set later, just before making the call.
|
|
//
|
|
//
|
|
|
|
void PreConfigureDevice(HWND hwndDlg, DWORD dwDeviceID)
|
|
{
|
|
long lReturn;
|
|
LPVARSTRING lpVarString = NULL;
|
|
DWORD dwSizeofVarString = sizeof(VARSTRING);
|
|
|
|
// If there isn't already any device configuration information,
|
|
// then we need to get some.
|
|
if (g_lpDeviceConfig == NULL)
|
|
{
|
|
do
|
|
{
|
|
lpVarString = (LPVARSTRING) CheckAndReAllocBuffer(
|
|
(LPVOID) lpVarString, dwSizeofVarString,
|
|
TEXT("PreConfigureDevice - lineGetDevConfig: "));
|
|
|
|
if (lpVarString == NULL)
|
|
return;
|
|
|
|
lReturn = lineGetDevConfig(dwDeviceID, lpVarString,
|
|
"comm/datamodem");
|
|
|
|
if (HandleLineErr(lReturn))
|
|
;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineGetDevCaps unhandled error: %x"), lReturn));
|
|
LocalFree(lpVarString);
|
|
return;
|
|
}
|
|
|
|
if ((lpVarString -> dwNeededSize) > (lpVarString -> dwTotalSize))
|
|
{
|
|
dwSizeofVarString = lpVarString -> dwNeededSize;
|
|
lReturn = -1; // Lets loop again.
|
|
}
|
|
}
|
|
while (lReturn != SUCCESS);
|
|
|
|
g_dwSizeDeviceConfig = lpVarString -> dwStringSize;
|
|
|
|
// The extra byte allocated is in case dwStringSize is 0.
|
|
g_lpDeviceConfig = CheckAndReAllocBuffer(
|
|
g_lpDeviceConfig, g_dwSizeDeviceConfig+1,
|
|
TEXT("PreConfigureDevice - Allocate device config: "));
|
|
|
|
if (!g_lpDeviceConfig)
|
|
{
|
|
LocalFree(lpVarString);
|
|
return;
|
|
}
|
|
|
|
memcpy(g_lpDeviceConfig,
|
|
((LPBYTE) lpVarString + lpVarString -> dwStringOffset),
|
|
g_dwSizeDeviceConfig);
|
|
}
|
|
|
|
// Next make the lineConfigDialogEdit call.
|
|
|
|
// Note that we determine the initial size of the VARSTRING
|
|
// structure based on the known size of the existing configuration
|
|
// information. I make the assumption that this configuration
|
|
// information is very unlikely to grow by more than 5K or by
|
|
// more than 5 times. This is a *very* conservative number.
|
|
// We do *not* want lineConfigDialogEdit to fail just because there
|
|
// wasn't enough room to stored the data. This would require the user
|
|
// to go through configuration again and that would be annoying.
|
|
|
|
dwSizeofVarString = 5 * g_dwSizeDeviceConfig + 5000;
|
|
|
|
do
|
|
{
|
|
lpVarString = (LPVARSTRING) CheckAndReAllocBuffer(
|
|
(LPVOID) lpVarString, dwSizeofVarString,
|
|
TEXT("PreConfigureDevice - lineConfigDialogEdit: "));
|
|
|
|
if (lpVarString == NULL)
|
|
return;
|
|
|
|
lReturn = lineConfigDialogEdit(dwDeviceID, hwndDlg, "comm/datamodem",
|
|
g_lpDeviceConfig, g_dwSizeDeviceConfig, lpVarString);
|
|
|
|
if (HandleLineErr(lReturn))
|
|
;
|
|
else
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("lineConfigDialogEdit unhandled error: %x"), lReturn));
|
|
LocalFree(lpVarString);
|
|
return;
|
|
}
|
|
|
|
if ((lpVarString -> dwNeededSize) > (lpVarString -> dwTotalSize))
|
|
{
|
|
// We had been conservative about making sure the structure was
|
|
// big enough. Unfortunately, not conservative enough. Hopefully,
|
|
// this will not happen a second time because we are *DOUBLING*
|
|
// the NeededSize.
|
|
dwSizeofVarString = (lpVarString -> dwNeededSize) * 2;
|
|
lReturn = -1; // Lets loop again.
|
|
}
|
|
}
|
|
while (lReturn != SUCCESS);
|
|
|
|
// Store the configuration information into a global structure
|
|
// so it can be set at a later time.
|
|
g_dwSizeDeviceConfig = lpVarString -> dwStringSize;
|
|
g_lpDeviceConfig = CheckAndReAllocBuffer(
|
|
g_lpDeviceConfig, g_dwSizeDeviceConfig+1,
|
|
TEXT("PreConfigureDevice - Reallocate device config: "));
|
|
|
|
if (!g_lpDeviceConfig)
|
|
{
|
|
LocalFree(lpVarString);
|
|
return;
|
|
}
|
|
|
|
memcpy(g_lpDeviceConfig,
|
|
((LPBYTE) lpVarString + lpVarString -> dwStringOffset),
|
|
g_dwSizeDeviceConfig);
|
|
|
|
LocalFree(lpVarString);
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: BOOL GetAddressToDial
|
|
//
|
|
// PURPOSE: Get an address to dial from the user.
|
|
//
|
|
// PARAMETERS:
|
|
// none
|
|
//
|
|
// RETURN VALUE:
|
|
// TRUE if a valid device and phone number have been entered by
|
|
// the user. FALSE if the user canceled the dialing process.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// All this function does is launch the "Dial" dialog.
|
|
//
|
|
//
|
|
|
|
BOOL GetAddressToDial()
|
|
{
|
|
BOOL bRet;
|
|
|
|
bRet = (BOOL)DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_DIALDIALOG), g_hDlgParentWindow,
|
|
DialDialogProc, 0);
|
|
g_hDialog = NULL;
|
|
g_hDlgParentWindow = g_hWndMainWindow;
|
|
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: DialDialogProc(HWND, UINT, WPARAM, LPARAM)
|
|
//
|
|
// PURPOSE: Dialog callback procedure for the dialing dialog
|
|
//
|
|
// PARAMETERS:
|
|
// hwndDlg - Dialog calling the callback.
|
|
// uMsg - Dialog message.
|
|
// wParam - uMsg specific.
|
|
// lParam - uMsg specific.
|
|
//
|
|
// RETURN VALUE:
|
|
// returns 0 - command handled.
|
|
// returns non-0 - command unhandled
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This is the dialog to get the phone number and line device
|
|
// from the user. All the relavent information is stored in global
|
|
// variables to be used later if the dialog returns successfully.
|
|
//
|
|
//
|
|
|
|
|
|
INT_PTR CALLBACK DialDialogProc(
|
|
HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
// Static variables to store the information from last time the
|
|
// "Dial" dialog was displayed. That way the phone number can be
|
|
// typed once but used several times.
|
|
|
|
static TCHAR szCountryName[512] = TEXT("");
|
|
static TCHAR szAreaCode[256] = TEXT("");
|
|
static TCHAR szPhoneNumber[512] = TEXT("");
|
|
static DWORD dwUsedDeviceID = MAXDWORD;
|
|
static BOOL bUsedCountryAndArea = FALSE;
|
|
static BOOL bHistoryValid = FALSE;
|
|
|
|
switch(uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
DWORD dwCountryID = 0;
|
|
|
|
|
|
// Store the Dialog Window so it can be dismissed if necessary
|
|
g_hDialog = hwndDlg;
|
|
|
|
// This dialog should be parent to all dialogs.
|
|
g_hDlgParentWindow = hwndDlg;
|
|
|
|
// Initialize the Dialog Box. Lots to do here.
|
|
|
|
FillTAPILine(hwndDlg);
|
|
if (g_lpDeviceConfig)
|
|
{
|
|
LocalFree(g_lpDeviceConfig);
|
|
g_lpDeviceConfig = NULL;
|
|
}
|
|
|
|
// If there is a valid history, use it to initialize the controls.
|
|
if (bHistoryValid)
|
|
{
|
|
FillLocationInfo(hwndDlg, NULL, NULL, NULL);
|
|
FillCountryCodeList(hwndDlg, 0);
|
|
|
|
SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE, CB_SELECTSTRING,
|
|
(WPARAM) -1, (LPARAM) (LPCTSTR) szCountryName);
|
|
|
|
SendDlgItemMessage(hwndDlg, IDC_PHONENUMBER, WM_SETTEXT, 0,
|
|
(LPARAM) (LPCTSTR) szPhoneNumber);
|
|
|
|
SendDlgItemMessage(hwndDlg, IDC_USEDIALINGRULES,
|
|
BM_SETCHECK, (WPARAM) bUsedCountryAndArea, 0);
|
|
|
|
SendDlgItemMessage(hwndDlg, IDC_TAPILINE, CB_SETCURSEL,
|
|
g_dwDeviceID, 0);
|
|
}
|
|
else
|
|
{
|
|
FillLocationInfo(hwndDlg, NULL, &dwCountryID, szAreaCode);
|
|
FillCountryCodeList(hwndDlg, dwCountryID);
|
|
SendDlgItemMessage(hwndDlg, IDC_USEDIALINGRULES,
|
|
BM_SETCHECK, 1, 0);
|
|
}
|
|
|
|
SendDlgItemMessage(hwndDlg, IDC_AREACODE, WM_SETTEXT,
|
|
0, (LPARAM) (LPCTSTR) szAreaCode);
|
|
|
|
UseDialingRules(hwndDlg);
|
|
DisplayPhoneNumber(hwndDlg);
|
|
VerifyAndWarnUsableLine(hwndDlg);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDC_TAPILINE:
|
|
if (HIWORD(wParam) == CBN_SELENDOK)
|
|
{
|
|
if (g_lpDeviceConfig)
|
|
{
|
|
LocalFree(g_lpDeviceConfig);
|
|
g_lpDeviceConfig = NULL;
|
|
}
|
|
DisplayPhoneNumber(hwndDlg);
|
|
VerifyAndWarnUsableLine(hwndDlg);
|
|
}
|
|
return TRUE;
|
|
|
|
case IDC_CONFIGURELINE:
|
|
{
|
|
DWORD dwDeviceID;
|
|
dwDeviceID = (DWORD)SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
|
|
CB_GETCURSEL, 0, 0);
|
|
PreConfigureDevice(hwndDlg, dwDeviceID);
|
|
DisplayPhoneNumber(hwndDlg);
|
|
return TRUE;
|
|
}
|
|
|
|
case IDC_COUNTRYCODE:
|
|
if (HIWORD(wParam) == CBN_SELENDOK)
|
|
DisplayPhoneNumber(hwndDlg);
|
|
return TRUE;
|
|
|
|
case IDC_AREACODE:
|
|
case IDC_PHONENUMBER:
|
|
if (HIWORD(wParam) == EN_CHANGE)
|
|
DisplayPhoneNumber(hwndDlg);
|
|
return TRUE;
|
|
|
|
case IDC_USEDIALINGRULES:
|
|
if (HIWORD(wParam) == BN_CLICKED)
|
|
{
|
|
UseDialingRules(hwndDlg);
|
|
DisplayPhoneNumber(hwndDlg);
|
|
}
|
|
return TRUE;
|
|
|
|
case IDC_LOCATION:
|
|
if (HIWORD(wParam) == CBN_CLOSEUP)
|
|
{
|
|
char szCurrentLocation[128];
|
|
int nCurrentSelection;
|
|
|
|
nCurrentSelection = (int)SendDlgItemMessage(hwndDlg,
|
|
IDC_LOCATION, CB_GETCURSEL, 0, 0);
|
|
SendDlgItemMessage(hwndDlg, IDC_LOCATION,
|
|
CB_GETLBTEXT, nCurrentSelection,
|
|
(LPARAM) (LPCTSTR) szCurrentLocation);
|
|
|
|
// If the user selected a 'location', make it current.
|
|
FillLocationInfo(hwndDlg, szCurrentLocation, NULL, NULL);
|
|
DisplayPhoneNumber(hwndDlg);
|
|
}
|
|
return TRUE;
|
|
|
|
case IDC_DIALINGPROPERTIES:
|
|
{
|
|
DWORD dwDeviceID;
|
|
long lReturn;
|
|
|
|
dwDeviceID = (DWORD)SendDlgItemMessage(hwndDlg, IDC_TAPILINE,
|
|
CB_GETCURSEL, 0, 0);
|
|
|
|
lReturn = lineTranslateDialog(g_hLineApp, dwDeviceID,
|
|
SAMPLE_TAPI_VERSION, hwndDlg, g_szTranslatedNumber);
|
|
|
|
#ifdef DEBUG
|
|
if (lReturn != SUCCESS)
|
|
DBG_INFO((DBGARG, TEXT("lineTranslateDialog: %x"), lReturn));
|
|
#endif
|
|
|
|
// The user could have changed the default location, or
|
|
// added or removed a location while in the 'Dialing
|
|
// Properties' dialog. Refill the Location Info.
|
|
FillLocationInfo(hwndDlg, NULL, NULL, NULL);
|
|
DisplayPhoneNumber(hwndDlg);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hwndDlg, FALSE);
|
|
return TRUE;
|
|
|
|
case IDC_DIAL:
|
|
{
|
|
// The Dial button has to be enabled and the line has
|
|
// to be currently usable to continue.
|
|
if (!(IsWindowEnabled((HWND)lParam) &&
|
|
VerifyAndWarnUsableLine(hwndDlg)))
|
|
return TRUE;
|
|
|
|
DisplayPhoneNumber(hwndDlg);
|
|
|
|
// Store all the relavent information in static
|
|
// variables so they will be available the next time a
|
|
// number is dialed.
|
|
SendDlgItemMessage(hwndDlg, IDC_COUNTRYCODE,
|
|
WM_GETTEXT, 511, (LPARAM) (LPCTSTR) szCountryName);
|
|
|
|
SendDlgItemMessage(hwndDlg, IDC_AREACODE,
|
|
WM_GETTEXT, 255, (LPARAM) (LPCTSTR) szAreaCode);
|
|
|
|
SendDlgItemMessage(hwndDlg, IDC_PHONENUMBER,
|
|
WM_GETTEXT, 511, (LPARAM) (LPCTSTR) szPhoneNumber);
|
|
|
|
bUsedCountryAndArea = (BOOL) SendDlgItemMessage(hwndDlg,
|
|
IDC_USEDIALINGRULES, BM_GETCHECK, 0, 0);
|
|
|
|
bHistoryValid = TRUE;
|
|
|
|
EndDialog(hwndDlg, TRUE);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// This message is actually posted to the dialog from the
|
|
// lineCallbackFunc when it receives a
|
|
// LINEDEVSTATE_TRANSLATECHANGE message. Notify the user and
|
|
// retranslate the number. Also refill the Location Info
|
|
// since this could have been generated by a location change.
|
|
case IDC_CONFIGURATIONCHANGED:
|
|
{
|
|
FillLocationInfo(hwndDlg, NULL, NULL, NULL);
|
|
DisplayPhoneNumber(hwndDlg);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// If we get a LINE_CREATE message, all that needs to be done
|
|
// is to reset this controls contents. The selected line
|
|
// won't change and no lines will be removed.
|
|
case IDC_LINECREATE:
|
|
{
|
|
FillTAPILine(hwndDlg);
|
|
return TRUE;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|