In this chapter, you'll learn how to build a simple TAPI dialer application in C using the Basic Telephony level of service. This application will be used to highlight the basic operations required to build TAPI applications (in any language).
You'll learn how to perform line initialization, locate a usable outbound line, and open it in preparation for dialing. You'll also learn how to place an outbound call and use the TAPI line callback function to monitor call progress. Finally, you'll learn how to safely close down a line after the call has been completed.
When you are done with the example in this chapter, you'll understand the basics of writing TAPI applications and know how to use Basic Telephony services in your own applications.
Note |
The project in this chapter was written using the Microsoft Visual C++ 4.1 compiler. However, the code is compatible with the Microsoft VC 2.0 compiler. If you do not have a C compiler, you can still get a lot out of the chapter. The same techniques covered here will be used when you build TAPI applications in Microsoft Visual Basic 4.0 later in this section of the book. |
Before starting the review of the TAPIOUT project, it is a good idea to cover the minimal steps needed to place an outbound call using TAPI services. There are really only a few steps:
After you complete these steps, you can use the messages received by the registered callback function to track call progress and respond accordingly. The next few sections cover the outbound calling steps in greater detail.
The first thing you need to do to start a TAPI session is to call the lineInitialize routine to initialize the link between your application and the TAPI service provider. The lineInitialize routine includes a pointer to the callback function in your code that will handle all messages.
After successful initialization, the routine returns a value in the lineHandle parameter. You'll use this value throughout your TAPI session. You will also get a count of the total number of TAPI lines defined for this workstation. You'll use that information to check the API version and line parameters of each line before you attempt to place a call.
If an error occurs, a non-zero value is returned. You can check the errors using a case switch and present a message to the user.
After you successfully open the TAPI session, you need to call the lineNegotiateAPIVersion function for each line in the collection. The total number of lines was returned as part of the lineInitialize routine. You need to check this value because it is possible that you will be requesting a version of TAPI this is not available for this machine.
You pass your API version request to the function and get a value back that is the version of TAPI that the workstation can provide to your application. You can also get a pointer to a structure that holds information about vendor-specific extension services available on this workstation. This is a method for allowing non-TAPI services to be recognized using the TAPI interface.
Once you have successfully negotiated an API version, you must use the lineOpen function to pass through each line and request the appropriate level of service. For example, if you wanted to place an interactive voice call, you'd use the lineOpen function to locate a line that supports interactive voice.
This is an important point. It is quite possible that the workstation will have several TAPI devices defined, but only one may provide the type of service you need (voice, fax, data, and so on). If there is no device available (none exists or the current one is busy), you'll get an error message. However, if an appropriate line is available, you'll receive a zero as a return code and a value indicating the handle of the open line. You'll use this value in subsequent TAPI calls.
Once you locate an appropriate line, you can set calling parameters using the LINECALLPARAMS structure. You use this structure to tell TAPI the speed and media type (data, voice, and so on) of your call and other values.
Setting the LINECALLPARAMS structure is optional. If you do not set any value for the LINECALLPARAMS, Microsoft TAPI will use default values. For most calls, the default values will work just fine.
The last step in the process is actually placing the call using the lineMakeCall function. This function passes the string that contains the phone number to call, a handle for the open line (you got that from lineOpen) and, optionally, a pointer to the LINECALLPARAMS structure.
If the call is placed successfully, a call handle is returned. You'll use this call handle in subsequent TAPI functions. If there is trouble making the call, the return code is non-zero and can be checked for appropriate action.
It is important to note that at this point the call has been placed but not completed. All TAPI knows for sure is that digits have been dialed and the phone line is active. TAPI will continue to receive status information and route that to your application through the callback function registered when you called lineInitialize. The quality of the status information (dialing, ringing, busy, idle, and so on) is all determined by the hardware vendor and TAPI service provider application. The more sophisticated the hardware, the more accurate the progress information.
For example, standard data/fax modems do not report call progress information. When you place your TAPI call, you'll be notified by the hardware that the call is in progress and will see nothing else until the call is completed or a time-out occurs. Other hardware (advanced voice/data modems) may provide additional call-progress data. High-end telephony cards provide the most accurate information.
Now that you know the basics of placing an outbound call using TAPI, it's time to review the TAPIOUT project on the CD-ROM that accompanies this book.
The TAPIOUT project that ships on the CD-ROM is a C program that allows users to enter a phone number and use TAPI to place an outbound call. This project is very rudimentary. There are no extra bells and whistles. However, the code in this project gives a good review of what it takes to provide basic TAPI services. The next several sections of this chapter review the TAPIOUT project step-by-step. You'll see how you can use the TAPI functions described earlier in the chapter to create a functional TAPI dialer.
If you have a copy of Microsoft VC++ 2.0 or later, start it now and load the TAPIOUT.MAK (or TAPIOUT.MDP) project from the CD-ROM. You can follow along with the examples in the chapter
Tip |
You'll need to have the TAPI SDK installed on your machine before you can compile this project. Once you load the project, be sure to select Tools | Update All Dependencies to resolve references to the TAPI.H and TAPI32.LIB files in the project. You may need to reload these files into the project using Insert | Files into Project... |
This first step in the process is declaring all the needed includes, defines, function prototypes, and global variables. Listing 24.1 shows how this looks in the TAPIOUT project.
Listing 24.1. The initial declarations of the TAPIOUT project.
// ******************************************************************
// SIMPLE OUTBOUND TAPI DIALER APPLICATION
// ******************************************************************
//
// Title: TAPIOut
// Version: 1.0 - 05/24/96 (MCA)
//
// Equip: VC++ 4.0 / Win95 / TAPI SDK
//
// Client: MAPI, SAPI, TAPI Developer's Guide (SAMS 1996)
//
// Desc: Simple dialog to show how to use TAPI to place outbound
// calls. Takes dialing string and shows progress as the
// program attempts to complete the call.
//
// Files: TAPIOUT.C
// TAPIOUT.RC
// TAPIOUT.DEF
// RESOURCE.H
// TAPI.H
// TAPI32.LIB
//
// ******************************************************************
// ****************************************
// Includes and defines
//
#include "windows.h"
#include "tapi.h"
#include "resource.h"
#define tapiVersionCur (MAKELONG(4,1)) // ver 1.4
#define TAPI_LINE_REPLY 5000
#define TAPI_LINECALLSTATE_CONNECTED 5001
#define TAPI_LINECALLSTATE_IDLE 5002
#define TAPI_LINECALLSTATE_DISCONNECTED 5003
#define TAPI_LINECALLSTATE_BUSY 5004
#define TAPI_LINECALLSTATE_AccEPTED 5005
#define TAPI_LINECALLSTATE_PROCEEDING 5006
#define TAPI_LINECALLSTATE_OFFERING 5007
#define TAPI_LINECALLSTATE_DIALTONE 5008
#define TAPI_LINECALLSTATE_DIALING 5009
// ******************************************
// global declares
//
LONG PlaceCall( HWND, LPTSTR );
void CALLBACK LineCallBackProc(DWORD hDevice,DWORD dwMessage,DWORD dwInstance,DWORD ÂdwParam1,DWORD dwParam2,DWORD dwParam3);
BOOL WINAPI MainDialog(HWND hDlg, WORD msg, WORD wParam, LONG lParam);
void ShowProgress( HWND hWnd, LPTSTR OutputString );
void SetVarProps( HWND hWnd, DWORD hDevice );
LINECALLPARAMS LineParams; // need this structure
DWORD lines; // count of available lines
HINSTAncE hInst; // this instance of the app
HWND MainWin, ButtonWnd; // window handles
HLINEAPP LineHandle = NULL; // tapi line handle
Notice the inclusion of the TAPI.H and WINDOWS.H files. You'll need these on your system if you want to compile this project. You'll also need the TAPI32.LIB file.
The defines added here make it easy to provide a local message handler that responds to predefined TAPI messages. You'll see how these are used in the MainDialog and lineCallBack routines later in this chapter.
Notice also the declaration of global handles and a lineParams structure. You'll use these throughout the project.
The WinMain code for this
project is quite simple. Declare a message queue, get the current
instance of this program, and then call the main dialog box. All
other activity is generated by the dialog box. Listing 24.2 shows
the code for the WinMain
routine.
Listing 24.2. The WinMain routine for the TAPIOUT project.
// *******************************************
// Initial Entry
//
// establish a message queue
// get the instance handle
// start the user dialog
//
int PASCAL WinMain( HANDLE hInstance, HANDLE hPrev, LPSTR lpCmd, int nShow )
{
SetMessageQueue( 100 );
hInst = hInstance;
DialogBox( hInstance, MAKEINTRESOURCE( ID_MAIN_SCREEN ), NULL, MainDialog );
return( FALSE );
}
The main dialog box contains only a few controls. An input box for the phone number, a list box to show the status messages supplied by TAPI, and three command buttons (PlaceCall, Disconnect, and Exit). Figure 24.1 shows the layout of the main dialog box.
Figure 24.1 : The main dialog box of the TAPIOUT project
The code for the main dialog box is a bit lengthy; however, it is rather simple, too. The code can be broken down into three main sections:
The first part of the MainDialog code responds to the initial loading of the dialog box and to user actions on the command buttons. Listing 24.3 shows how this code looks.
Listing 24.3. The first part of the MainDialog code.
// ********************************************
// Main Dialog to handle user interface
//
BOOL WINAPI MainDialog(HWND hDlg, WORD msg, WORD wParam, LONG lParam)
{
switch (msg)
{
case WM_INITDIALOG: // when dialgo first starts up
{
// Set the necessary properties to null
SetProp( hDlg, "HCALL", NULL );
SetProp( hDlg, "HLINE", NULL );
SetProp( hDlg, "HCOMM", NULL );
break;
}
case WM_COMMAND: // user pressed a button
{
switch( wParam )
{
case ID_CALL: // user press PLACE CALL
{
char PhoneNumber[ 100 ]; // save some space
HCALL hCall; // declare a local handle
//
// Gotta call going? - Uh, oh!
hCall = (HCALL)GetProp( hDlg, "HCALL" );
if( hCall != NULL )
{
MessageBox( hDlg,"Please Disconnect before making another
&nbs p; Âcall!",
&nbs p; " Tapi Error",
&nbs p; MB_ICONSTOP );
break;
}
//
// Get digits from input box
GetDlgItemText( hDlg, ID_PHONE, PhoneNumber, sizeof( ÂPhoneNumber ) );
//
// place the call (check return value)
if( PlaceCall( hDlg, PhoneNumber ) < 0 )
ShowProgress( hDlg, "Unable to start a TAPI Function" );
break;
}
case ID_DISCONNECT: // user press DISCONNECT
{
LONG retcode; // some local stuff
HCALL hCall;
HANDLE hComm;
//
// try to get the handles
hCall = (HCALL)GetProp( hDlg, "HCALL" );
hComm = (HANDLE)GetProp( hDlg, "HCOMM" );
//
// if we have a comm handle, drop it
if( hComm != NULL )
{
CloseHandle( hComm );
SetProp( hDlg, "HCALL", NULL );
}
//
// if we have a call handle, drop it
if( hCall != NULL )
{
retcode = lineDrop( hCall, NULL, 0 );
ShowProgress( hDlg, "Call is Dropped" );
SetProp( hDlg, "HCALL", NULL );
}
break;
}
case IDOK: // user pressed the EXIT button
{
HCALL hCall; // declare some local vars
HLINE hLine;
HANDLE hComm;
//
// load the values
hCall = (HCALL)GetProp( hDlg, "HCALL" );
hLine = (HLINE)GetProp( hDlg, "HLINE" );
hComm = (HANDLE)GetProp( hDlg, "HCOMM" );
//
// if we have a comm handle, close it
if( hComm != NULL )
{
CloseHandle( hComm );
SetProp( hDlg, "HCOMM", NULL );
}
//
// if we have a call handle, close it
if( hCall != NULL )
{
lineDrop( hCall, NULL, 0 );
SetProp( hDlg, "HCALL", NULL );
}
//
// if we have a line handle, close it
if( hLine != NULL )
{
lineClose( hLine );
SetProp( hDlg, "HLINE", NULL );
}
//
// close down open line
if( LineHandle != NULL )
{
lineShutdown( LineHandle );
LineHandle = NULL;
}
//
// drop the save properties
RemoveProp( hDlg, "HCALL" );
RemoveProp( hDlg, "HLINE" );
RemoveProp( hDlg, "HCOMM" );
//
// close down the dialog
EndDialog( hDlg, FALSE );
break;
&nbs p; }
The code here deserves some review. First, when the dialog box first starts, three properties are created. These will hold values used throughout the dialog. The next event is the pressing of the ID_CALL button. This tells the dialog box to attempt to place a call. The first step is to check to see if a call is already in progress. If so, a message is displayed to the user. If no call is currently in progress, the phone number is gathered from the input box and then passed to the PlaceCall function for final dispatch (you'll see the PlaceCall function in the next section of this chapter).
If the user presses the ID_DISCONNECT button, the program checks the comm and call handles and, if they are set, clears them using the ClearHandle and lineDrop functions.
Finally, when the user presses the Exit button (IDOK), the same types of routines executed in ID_DISCONNECT must also occur here. In addition to the ClearHandle and lineDrop functions, the lineClose and lineShutdown routines are called. This performs final closure on all TAPI services for this session.
The second section of the MainDialog is used to respond to TAPI messages received via the lineCallBack function and passed onto the MainDialog. The only message that needs attention is when a call goes idle. If a call goes idle, the MainDialog needs to close down the line resources just as if the user had pressed the Disconnect button. Listing 24.4 shows how this code looks in the TAPIOUT project.
Listing 24.4. Responding to the TAPI Messages in the MainDialog.
//
// **************************************
// respond to TAPI Messages
// **************************************
//
case TAPI_LINE_REPLY:
{
ShowProgress( hDlg, "Line Reply" );
break;
}
//
case TAPI_LINECALLSTATE_CONNECTED:
{
ShowProgress( hDlg, "Line Call State is Connected" );
break;
}
//
case TAPI_LINECALLSTATE_IDLE:
{
LONG retcode; // local stuff
HLINE hLine;
//
// call went idle, do cleanup
hLine = (HLINE)GetProp( hDlg, "HLINE" );
//
// if we have a live line, close it
if( hLine != NULL )
{
retcode = lineClose( hLine );
SetProp( hDlg, "HLINE", (HANDLE)NULL );
}
ShowProgress( hDlg, "Line Call State is idle" );
break;
&nbs p; }
Notice that, unlike the disconnect which performs both a lineDrop and a lineClose, the idle line handler only calls lineClose. This is because idle lines have already experienced the lineDrop. This is why they are idle!
The last section of the MainDialog is used to simply post status messages to the list box on the dialog box. The code is added here to show you progress during the call. You may not need to code these messages at all in production applications. Listing 24.5 shows how this code looks.
Listing 24.5. Posting TAPI messages from the MainDialog.
//
// *********************************************
// respond to forwarded TAPI messages
// *********************************************
//
case TAPI_LINECALLSTATE_DISCONNECTED:
{
ShowProgress( hDlg, "Line Call State is Disconnected" );
break;
}
//
case TAPI_LINECALLSTATE_BUSY:
{
ShowProgress( hDlg, "Line Call State is Busy" );
break;
}
//
case TAPI_LINECALLSTATE_AccEPTED:
{
ShowProgress( hDlg, "Line Call State is Accepted" );
break;
}
//
case TAPI_LINECALLSTATE_PROCEEDING:
{
ShowProgress( hDlg, "Line Call State is Proceeding" );
break;
}
//
case TAPI_LINECALLSTATE_OFFERING:
{
ShowProgress( hDlg, "Line Call State is Offering" );
break;
}
//
case TAPI_LINECALLSTATE_DIALTONE:
{
ShowProgress( hDlg, "Line Call State is DialTone" );
break;
}
//
case TAPI_LINECALLSTATE_DIALING:
{
ShowProgress( hDlg, "Line Call State is Dialing" );
break;
}
default:
break;
} // switch (wParam)
break;
} // case WM_COMMAND
default:
break;
} // switch (msg)
return (FALSE);
} // main dialog
The real heart of the project is the PlaceCall function. This routine is the one that actually places the requested call. The code here follows the outline in the first part of this chapter. The steps of initialize, check API, look for open line, set call parameters, and place call are all here in this one routine. You can use this code as a shell routine to place in your other TAPI applications. Listing 24.6 shows the code for the PlaceCall function.
Listing 24.6. The code for the PlaceCall function.
// ***********************************************
// This routine places the actual call
//
LONG PlaceCall( HWND hWnd, LPTSTR PhoneNumber )
{
LONG retcode; // local returns
DWORD i; // counter for lines
DWORD ApiVersion; // expected API version
DWORD RetApiVersion; // return version
LINEEXTENSIONID ExtensionID; // struc for API call
HLINE hLine; // local line handle
HCALL hCall; // local call handle
//
// make sure you have a phone number
if( lstrlen( PhoneNumber ) < 1 )
return( -1 );
//
// Initialize the line, register the callback
if( LineHandle == NULL )
retcode = lineInitialize( &LineHandle, hInst, Â(LINECALLBACK)LineCallBackProc, "TAPI Out", &lines );
if( retcode < 0 )
return( retcode );
//
// go through all lines to get API and properties
// if you find one that has the right properties,
// jump out and continue to next section of code
//
hLine = (HLINE)GetProp( hWnd, "HLINE" );
if( hLine == NULL )
{
for( i=0; i < lines; i++ )
{
// Negotiate the API Version for each line
ApiVersion = tapiVersionCur;
retcode = lineNegotiateAPIVersion( LineHandle, i, ApiVersion, ÂApiVersion, &RetApiVersion,
&ExtensionID );
retcode = lineOpen( LineHandle, i, &hLine, RetApiVersion, 0, Â(DWORD)hWnd,
LINECALLPRIVILEGE_OWNER | LINECALLPRIVILEGE_MONITOR,
LINEMEDIAMODE_DATAMODEM, NULL );
if( retcode == 0 )
break;
}
if( retcode != 0 )
return( -1 );
}
//
// found a good line
SetProp( hWnd, "HLINE",(HANDLE)(HLINE)hLine );
//
// now set of properties of the line for outbound dialing
memset( &LineParams, 0, sizeof( LINECALLPARAMS ) );
LineParams.dwTotalSize = sizeof( LINECALLPARAMS );
LineParams.dwMinRate = 9600; // setting data rates
LineParams.dwMaxRate = 9600; //
LineParams.dwMediaMode = LINEMEDIAMODE_DATAMODEM; // doing a data call
//
// finally place the call!
retcode = lineMakeCall( hLine, &hCall, PhoneNumber, 0, &LineParams );
return( retcode ); // tell'em how it turned out!
}
This code hardly needs review-you've seen this explained before. The key points to remember are:
It is also important to remember that once TAPI performs the lineMakeCall successfully you still are not connected to your called party. You need to check callback messages to check the status of your call.
There are two helper routines in the TAPIOUT project. The first, ShowProgress, is used to post messages to the list box on the main dialog form. Listing 24.7 shows how this code looks.
Listing 24.7. Posting messages to the main dialog box.
// ************************************************
// update list box to show TAPI progress
//
void ShowProgress( HWND hDlg, LPTSTR OutputString )
{
DWORD dwIndex;
int i;
dwIndex = SendDlgItemMessage( hDlg, ID_STATUS_LIST, LB_ADDSTRING, 0, Â(LPARAM)(LPSTR)OutputString );
if( dwIndex == LB_ERR ) // clear some space for full box
{
for( i = 0; i < 10; i++ )
SendDlgItemMessage( hDlg, ID_STATUS_LIST, LB_DELETESTRING, 0, 0 );
// now send the message
dwIndex = SendDlgItemMessage( hDlg, ID_STATUS_LIST, LB_ADDSTRING, 0, Â(LPARAM)(LPSTR)OutputString );
}
SendDlgItemMessage( hDlg, ID_STATUS_LIST, LB_SETCURSEL, (WPARAM)dwIndex, 0 );
return;
}
The second routine is used to pick the line ID out of the LINECALLINFO structure and store it in the dialog properties set. Listing 24.8 shows the SetVarProps routine.
Listing 24.8. The code for the SetVarProps routine.
// ***************************************************
// get line handle from LINECALLINFO structure
//
void SetVarProps( HWND hWnd, DWORD hDevice )
{
LINECALLINFO LineCallInfo;
memset( &LineCallInfo, 0, sizeof( LINECALLINFO ) );
SetProp( hWnd, "HCALL", (HANDLE)(HCALL)hDevice );
LineCallInfo.dwTotalSize = sizeof( LINECALLINFO );
lineGetCallInfo( (HCALL)hDevice, &LineCallInfo );
SetProp( hWnd, "HLINE", (HANDLE)(HLINE)LineCallInfo.hLine );
return;
}
The lineCallBackProc is the routine registered (using lineInitialize) to receive all TAPI messages for this application. Since this routine must handle all messages from TAPI, it can get a bit long. In this chapter, the code is broken into three segments:
The first section of the lineCallBackProc contains code to respond to changes in the LINE_CALLSTATE message. Only two messages get our attention here: LINE_CALLSTATE_IDLE and LINE_CALLSTATE_CONNECTED. Listing 24.9 shows the code that responds to these two messages.
Listing 24.9. Responding to the LINE_CALLSTATE_IDLE and LINE_CALLSTATE_CONNECTED messages.
// *******************************************
// The callback to handle TAPI messages
//
// This routine handles all messages generated by TAPI services.
// Most of these messages are ignored here or just passsed on to
// the main dialog for posting to the progress window.
//
void CALLBACK LineCallBackProc(DWORD hDevice,DWORD dwMessage,DWORD dwInstance,DWORD ÂdwParam1,DWORD dwParam2,DWORD dwParam3)
{
switch (dwMessage)
{
case LINE_CALLSTATE: // review the call state messages
{
switch( dwParam1 )
{
case LINECALLSTATE_IDLE: // went idle
{
LONG retcode;
LINECALLINFO LineCallInfo;
//
// load call info into structure
memset( &LineCallInfo, 0, sizeof( LINECALLINFO ) );
LineCallInfo.dwTotalSize = sizeof( LINECALLINFO );
lineGetCallInfo( (HCALL)hDevice, &LineCallInfo );
//
// deallocate the call
retcode = lineDeallocateCall( (HCALL)hDevice );
//
// post message to main dialog
PostMessage((HWND)dwInstance, WM_COMMAND, ÂTAPI_LINECALLSTATE_IDLE, (LPARAM)(HLINE)LineCallInfo.hLine );
break;
}
case LINECALLSTATE_CONNECTED: // hey, we got through!
{
//
// local vars for processing
LPVARSTRING lpVarStringStruct = NULL;
size_t sizeofVarStringStruct = sizeof( VARSTRING ) + 1024;
HANDLE CommFile = NULL;
long lreturn;
// get the comm handle. Be sure to drop this handle when
// the call is done or you'll get device unavailable errors
// and have to REBOOT!
lpVarStringStruct = LocalAlloc( 0, sizeofVarStringStruct );
do
{
memset( lpVarStringStruct, 0, sizeofVarStringStruct );
lpVarStringStruct->dwTotalSize = Â(DWORD)sizeofVarStringStruct;
lreturn = lineGetID( 0, 0, (HCALL)hDevice, ÂLINECALLSELECT_CALL, lpVarStringStruct, "comm/datamodem" );
} while( lreturn != 0 );
//
// get comm device handle and save it to properties area
CommFile = *( (LPHANDLE )( ( LPBYTE )lpVarStringStruct + ÂlpVarStringStruct->dwStringOffset ) );
SetProp( (HWND)dwInstance, "HCOMM", CommFile );
SetVarProps( (HWND)dwInstance, hDevice );
//
// tell main dialog we got through
PostMessage( (HWND)dwInstance, WM_COMMAND, ÂTAPI_LINECALLSTATE_CONNECTED, (LPARAM)(HANDLE)CommFile );
LocalFree( lpVarStringStruct ); // drop mem space
break;
&nbs p; }
Notice that the LINE_CALLSTATE_CONNECTED routine contains code that retrieves the comm handle and gets the lineID. You'll need these values in subsequent calls to TAPI services. The LINE_CALLSTATE_IDLE routine simply de-allocates the device handle to free up resources for the next call.
The second segment of the callback routine passes messages to the main dialog box for display. These are added here to show you how the messages work and how call progress can be reported. You may not need to add these messages to your production applications. Listing 24.10 shows the portion of lineCallBackProc that posts messages to the MainDialog routine.
Listing 24.10. Passing messages from lineCallBack to MainDialog.
case LINECALLSTATE_AccEPTED: // just pass message on...
{
SetVarProps( (HWND)dwInstance, hDevice );
PostMessage( (HWND)dwInstance, WM_COMMAND, ÂTAPI_LINECALLSTATE_AccEPTED,(LPARAM)(HCALL)hDevice );
break;
}
case LINECALLSTATE_PROCEEDING: // post progress message
{
SetVarProps( (HWND)dwInstance, hDevice );
PostMessage( (HWND)dwInstance, WM_COMMAND, ÂTAPI_LINECALLSTATE_PROCEEDING,(LPARAM)(HCALL)hDevice );
break;
}
case LINECALLSTATE_OFFERING: // pass it on
{
SetVarProps( (HWND)dwInstance, hDevice );
PostMessage( (HWND)dwInstance, WM_COMMAND, ÂTAPI_LINECALLSTATE_OFFERING, (LPARAM)(HCALL)hDevice );
break;
}
case LINECALLSTATE_DIALTONE: // pass it on
{
SetVarProps( (HWND)dwInstance, hDevice );
PostMessage( (HWND)dwInstance, WM_COMMAND, ÂTAPI_LINECALLSTATE_DIALTONE, (LPARAM)(HCALL)hDevice );
break;
}
case LINECALLSTATE_DIALING: // pas it on...
{
SetVarProps( (HWND)dwInstance, hDevice );
PostMessage( (HWND)dwInstance, WM_COMMAND, ÂTAPI_LINECALLSTATE_DIALING, (LPARAM)(HCALL)hDevice );
break;
}
case LINECALLSTATE_BUSY: // pass it on...
{
SetVarProps( (HWND)dwInstance, hDevice );
PostMessage( (HWND)dwInstance, WM_COMMAND, ÂTAPI_LINECALLSTATE_BUSY, 0 );
break;
}
case LINECALLSTATE_DISCONNECTED: // pass it on...
{
SetVarProps( (HWND)dwInstance, hDevice );
PostMessage( (HWND)dwInstance, WM_COMMAND, ÂTAPI_LINECALLSTATE_DISCONNECTED, (LPARAM)(HCALL)hDevice );
break;
}
}
break;
}
In the final section of the lineCallBack routine, several other potential messages are listed, but no code is added. This is just to give you an idea of the other messages that can be received by lineCallBackProc. Listing 24.11 shows the final section of the lineCallBackProc routine.
Listing 24.11. The last section of lineCallBackProc.
case LINE_LINEDEVSTATE: // we'll ignore these for now...
switch (dwParam1)
{
case LINEDEVSTATE_REINIT:
break;
case LINEDEVSTATE_RINGING:
break;
}
break;
case LINE_CLOSE: // the line has been closed!
{
break;
}
case LINE_REPLY: // pass on TAPI_REPLY messages
{
PostMessage( (HWND)dwInstance, WM_COMMAND, TAPI_LINE_REPLY, 0 );
break;
}
//
// other messages that we'll ignore here
//
case LINE_REQUEST:
case LINE_ADDRESSSTATE:
break;
case LINE_CALLINFO:
break;
case LINE_DEVSPECIFIC:
break;
case LINE_DEVSPECIFICFEATURE:
break;
case LINE_GATHERDIGITS:
break;
case LINE_GENERATE:
break;
case LINE_MONITORDIGITS:
break;
case LINE_MONITORMEDIA:
break;
case LINE_MONITORTONE:
break;
} /* switch */
} /* LineCallBackProc */
That concludes the code review for the TAPIOUT project. In the next section, you'll get to test the TAPIOUT project.
If you have a C compiler that can handle the TAPIOUT code, compile it now. If you do not have a compiler, you can load the TAPIOUT.EXE from the CD-ROM that ships with the book.
When you first load TAPIOUT you'll see a simple dialog box. Enter a valid phone number in the input box and press PlaceCall. You'll see several messages appear in the status box as TAPI attempts to complete your call (see Figure 24.2).
Figure 24.2 : Running the TAPIOUT project
In a production application you would add code that responded to the LINE_CALLSTATE_CONNECTED message by notifying the user to pick up a connected handset (for voice calls), beginning a data send (for fax or data calls), or possibly starting a recorded message (for automated voice calls).
In this chapter you learned how to use the Basic Telephony API functions to write a short dialer program in C. The techniques you learned here will be used throughout all the TAPI projects in this book. Even the projects written in Visual Basic 4.0 will use the same API calls in the same order.
In the next chapter, you'll learn about the details of hardware configurations including the limitations and advantages of standard modem cards, voice-modem cards, and telephony hardware.