TAPI

TAPI interacts with the phone in some of the same ways as the RAS API. In fact, some RAS functions require information obtained from TAPI. But TAPI is very different from RAS. While RAS focuses on creating a seamless link between local and remote networks, TAPI's goal is the creation of a seamless link between the computer and the phone system. Recent additions to TAPI have included all the hooks required to support a full-fledged call center application. One of the things strangely lacking from TAPI is a reasonable facility to allow faxing. While technically MAPI covers this feature, it is not MAPI's major focus ”which is enabling applications to integrate e-mail support.

TAPI Versions

Windows 2000 supports some new versions of TAPI. TAPI 2.1 is mostly compatible with previous versions of TAPI, providing a function call API. TAPI 3, which is also new with Windows 2000, is an entirely new COM interface for TAPI. Both interfaces provide essentially the same functionality, although some of the more advanced call center features are more fully developed and easier to use in TAPI 3.

One of the downsides of TAPI 3 is that it is not optimized for a service environment. For instance, TAPI 3 requires a call to CoInitialize , which indirectly creates windows. Applications that create windows must have a message pump, which is something commonly required for Windows GUI applications but is not required for service applications. In addition, these same constraints require special versions of waiting functions, so rather than use something like WaitForSingleObject or even the simple Sleep , you must use the CoWaitForMultipleHandles function. This complication, as well as TAPI 2.1's reasonably complete functionality, favors TAPI 2.1 over TAPI 3 in a service application. Thus, the examples in this book will use TAPI 2.1.

TAPI allows the developer to specify the version of TAPI that the application will support. Just as important, the version supported can be negotiated; an application can determine the current version of TAPI supported on the system it runs and negotiate the version of the interface it will support. TAPI versions are returned to the application and sent to TAPI as a DWORD, with the high-order word containing the major version number and the low-order word containing the minor version number. For instance, 0x20001 returned as a version means TAPI 2.1.

TAPI Devices

Another TAPI feature is the device ID. This is a number that identifies a particular device on the system, commonly a modem. Device IDs range from 0 through one less than the number of devices returned by TAPI. Under Windows NT 4, the number of devices reported seems simple and consistent: on a machine containing a single device, the number of devices returned is 1, and device ID 0 is that single device. Under Windows 2000 (as of Windows 2000 RC 1), the number of devices returned seems a bit unusual, generally seen as a number greater than 1000, with the device at device ID 1000 corresponding to the device at device ID 0 on a similarly equipped Windows NT 4.x machine.

TAPI Errors

Unlike many Win32 APIs, TAPI errors are not reported using the GetLastError Win32 API function. Errors are returned directly from the TAPI functions, and the error numbers are less than zero, meaning that properly interpreting them requires a signed number. In some cases TAPI errors are returned in data elements of structures declared as DWORDs, meaning that they are unsigned. For instance, one way that information is obtained about the progress of asynchronous TAPI operations is through a function that gets a message from TAPI. To properly interpret the error code in these cases, the structure data element must be cast as a long .

Getting Feedback from TAPI

Getting feedback about the success or failure of an operation is one of the most important tasks of any API. Within TAPI, many operations return a code indicating success or failure, but that code refers to the beginning of the operation, not to the success of the operation itself. For instance, one of the functions used in this chapter's example programs is lineMakeCall . This is an asynchronous function, meaning that it returns before the actual operation requested is complete.

NOTE
The capitalization of TAPI function names is a bit different than most Win32 API function naming conventions. While virtually all Win32 functions begin with an initial capital letter, none of the TAPI functions do. They are broadly grouped into functions beginning with line and functions beginning with phone . The phone functions are used to control a telephone handset, allowing lights to be turned on and off, the line flashed, and so on. We will focus on the line functions, useful for the examples in this chapter. The general structure of the phone functions is similar to that of the line functions.

There are several ways that TAPI can give feedback to an application about the progress of an asynchronous operation. They are

  • completion ports
  • hidden windows
  • event objects

Completion ports will be discussed in Chapter 12. A hidden window is not an ideal solution for a service since this implies an operational message pump, something a service is unlikely to have. That leaves event objects, one of the kernel synchronization objects I discussed in Chapter 2.

A TAPI Example

One of the many examples of what TAPI can be used for is allowing a service application to let someone know that the service is having a problem, using a standard numeric pager.

Listing 7-1 is the header file for the TAPI21Sample program in Listing 7-2. TAPI21Sample is a simple console mode program that demonstrates the TAPI functions required to initiate a call. TAPI21Sample simply calls a beeper service and places a beep. When you look at the code, you might very well say to yourself: Is all this necessary? The answer is yes ”this is what TAPI requires.

Listing 7-1

StdAfx.h

 //stdafx.h:includefileforsampleTAPIapplication. // #if!defined(AFX_STDAFX_H__ECF1090B_57E8_11D3_B4AA_00C04F79B510__ INCLUDED_) #defineAFX_STDAFX_H__ECF1090B_57E8_11D3_B4AA_00C04F79B510__INCLUDED_ #if_MSC_VER>1000 #pragmaonce #endif//_MSC_VER>1000 #defineWIN32_LEAN_AND_MEAN//Excluderarelyusedstufffrom //Windowsheaders. #include<stdio.h> #include<windows.h> #include<tapi.h> //TODO:referenceadditionalheadersyourprogramrequireshere. //{{AFX_INSERT_LOCATION}} //MicrosoftVisualC++willinsertadditionaldeclarations //immediatelybeforethepreviousline. #endif//!defined(AFX_STDAFX_H__ECF1090B_57E8_11D3_B4AA_00C04F79B510__ INCLUDED_) 

Listing 7-2

TAPI21Sample.cpp

 //TAPI21Sample.cpp:ConsoleapplicationtotestTAPI2 // #include"stdafx.h" //Forwarddeclaration voidReportTAPIError(LONGret,char*op); intCallBeeper(char*beeperNumber,char*beepTo); intmain(intargc,char*argv[]) { //Replacethesestringswithyourbeeperaccessnumberandthe //numbertosendtothebeeper. CallBeeper("555-5166","555-1605"); return0; } intCallBeeper(char*beeperNumber,char*beepTo) { HLINEAPPhLineApp; HLINEhLine; charszFriendlyAppName[255]; charszAddress[255]; DWORDdwNumDevs=0; DWORDdwAPIVersion=0x00020000; DWORDdwCallbackInstance=43;//Justaluckynumber, //NOTinterpretedbyTAPI DWORDdwStatus; DWORDdwDeviceID=0; LONGret; LINEINITIALIZEEXPARAMSLineInitializeExParams; LINEEXTENSIONIDExtensionID; LPLINEDEVCAPSDevCaps=NULL; strcpy(szFriendlyAppName,"TAPIBeeperProgram"); memset((void*)&LineInitializeExParams,0, sizeof(LINEINITIALIZEEXPARAMS)); LineInitializeExParams.dwTotalSize= sizeof(LINEINITIALIZEEXPARAMS); LineInitializeExParams.dwOptions= LINEINITIALIZEEXOPTION_USEEVENT; printf("TAPIBeeperProgram\n"); ret=lineInitializeEx(&hLineApp,NULL,NULL,szFriendlyAppName, &dwNumDevs,&dwAPIVersion,&LineInitializeExParams); ReportTAPIError(ret,"lineInitializeEx"); if(ret==0) { //Itworked! printf("\nMAPIreports\nNumberofDevices:%d\nVersion%08x", dwNumDevs,dwAPIVersion); //Loopthroughavailabledevicestillwefindagoodone. for(dwDeviceID=0;dwDeviceID<dwNumDevs;dwDeviceID++) { ret=lineNegotiateAPIVersion(hLineApp, dwDeviceID, 0x00020000, 0x00020001, &dwAPIVersion, &ExtensionID); if(ret==0) { ret=lineOpen(hLineApp,dwDeviceID,&hLine, dwAPIVersion,0,dwCallbackInstance, LINECALLPRIVILEGE_NONE,0,0); ReportTAPIError(ret,"lineOpen"); if(ret==0) { break; } } } ReportTAPIError(ret,"lineNegotiateAPIVersion"); if(ret==0) { HCALLhCall; charszLineName[255]; DevCaps=(LPLINEDEVCAPS)newchar[1024]; DevCaps->dwTotalSize=1024; ret=lineGetDevCaps(hLineApp,dwDeviceID, dwAPIVersion,0,DevCaps); ReportTAPIError(ret,"lineGetDevCaps"); if(ret>=0&&DevCaps->dwLineNameSize>0) { if(DevCaps->dwLineNameSize>(254)) { strncpy(szLineName, (LPSTR)DevCaps+ DevCaps->dwLineNameOffset,254); szLineName[254]=' 
 //  TAPI21Sample.cpp  :  Console  application  to  test  TAPI  2 // #include  "stdafx.h" //  Forward  declaration void  ReportTAPIError(LONG  ret,  char  *op); int  CallBeeper(char  *beeperNumber,  char  *beepTo); int  main(int  argc,  char*  argv[]) {         //  Replace  these  strings  with  your  beeper  access  number  and  the         //    number  to  send  to  the  beeper.         CallBeeper("555-5166",  "555-1605");         return  0; } int  CallBeeper(char  *beeperNumber,  char  *beepTo) {         HLINEAPP  hLineApp;         HLINE  hLine;         char  szFriendlyAppName[255];         char  szAddress[255];         DWORD  dwNumDevs  =  0;         DWORD  dwAPIVersion  =  0x00020000;         DWORD  dwCallbackInstance  =  43;    //  Just  a  lucky  number,                                                                         //    NOT  interpreted  by  TAPI         DWORD  dwStatus;         DWORD  dwDeviceID  =  0;         LONG  ret;         LINEINITIALIZEEXPARAMS  LineInitializeExParams;         LINEEXTENSIONID  ExtensionID;         LPLINEDEVCAPS  DevCaps  =  NULL; strcpy (szFriendlyAppName,  "TAPI  Beeper  Program"); memset ((void  *)&LineInitializeExParams,  0,                 sizeof(LINEINITIALIZEEXPARAMS));         LineInitializeExParams.dwTotalSize  =                 sizeof(LINEINITIALIZEEXPARAMS);         LineInitializeExParams.dwOptions  =                 LINEINITIALIZEEXOPTION_USEEVENT;         printf("TAPI  Beeper  Program\n");         ret  =  lineInitializeEx(&hLineApp,  NULL,  NULL,  szFriendlyAppName,                 &dwNumDevs,  &dwAPIVersion,  &LineInitializeExParams);         ReportTAPIError(ret,  "lineInitializeEx");         if  (ret  ==  0)         {                 //  It  worked!                 printf("\nMAPI reports \nNumber  of  Devices:  %d\nVersion  %08x",                         dwNumDevs,  dwAPIVersion);                 //  Loop  through  available  devices  till  we  find  a  good  one.                 for  (dwDeviceID  =  0;  dwDeviceID  <  dwNumDevs;  dwDeviceID++)                 {                         ret  =  lineNegotiateAPIVersion(hLineApp,                                 dwDeviceID,                                 0x00020000,                                 0x00020001,                                 &dwAPIVersion,                                 &ExtensionID);                         if  (ret  ==  0)                         {                                 ret  =  lineOpen(hLineApp,  dwDeviceID,  &hLine,                                         dwAPIVersion,  0,  dwCallbackInstance,                                         LINECALLPRIVILEGE_NONE,  0,  0);                                 ReportTAPIError(ret,  "lineOpen");                                 if  (ret  ==  0)                                 {                                         break;                                 }                         }                 }                 ReportTAPIError(ret,  "lineNegotiateAPIVersion");                 if  (ret  ==  0)                 {                         HCALL  hCall;                         char  szLineName[255];                         DevCaps  =  (LPLINEDEVCAPS)new  char[1024];                         DevCaps->dwTotalSize  =  1024;                         ret  =  lineGetDevCaps(hLineApp,  dwDeviceID,                                 dwAPIVersion,  0,  DevCaps);                         ReportTAPIError(ret,  "lineGetDevCaps");                         if  (ret  >=  0  &&  DevCaps->dwLineNameSize  >  0)                         {                                 if  (DevCaps->dwLineNameSize  >  (254))                                 { strncpy (szLineName,                                                 (LPSTR)  DevCaps  +                                                 DevCaps->dwLineNameOffset,  254);                                         szLineName[254]  =  '\0';                                 }                                 else                                 {                                         lstrcpy(szLineName,                                                 (LPSTR)  DevCaps  +                                                 DevCaps->dwLineNameOffset);                                 }                                 printf("\nDevice name %s  ",  szLineName);                         }                         delete  (char  *)DevCaps;                         //  Set  what  TAPI  calls  the  address,  which  is                         //    really  just  a  phone  number.                         sprintf(szAddress,  "%s,,,%s#",  beeperNumber,  beepTo);                         ret  =  lineMakeCall(hLine,  &hCall,  szAddress,  0,  0);                         if  (ret  >  0)                         {                                 printf("\nlineMakeCall  returned  %d",  ret);                         }                         ReportTAPIError(ret,  "lineMakeCall");                         while  ((dwStatus  =  WaitForSingleObject(LineInitializeExParams.Handles.hEvent,  20000))  ==                                 WAIT_OBJECT_0)                         {                                 LINEMESSAGE  lm;                                 //  TAPI's  returning  some  information.                                 if  ((ret  =  lineGetMessage(hLineApp,  &lm,  0))  !=  0)                                 {                                         ReportTAPIError(ret,  "lineGetMessage");                                         return  (ret);                                 }                                 switch  (lm.dwMessageID)                                 {                                 case  LINE_REPLY:                                         if  ((long)lm.dwParam2  <  0)                                         {                                                 printf("\nError  occured...Request  ID  %d",                                                         lm.dwParam1);                                         }                                         break;                                 case  LINE_CALLSTATE:                                         if  (LINECALLSTATE_DIALTONE&lm.dwParam1)                                         {                                                 printf("\nDialtone detected ");                                         }                                         if  (LINECALLSTATE_DIALING&lm.dwParam1)                                         {                                                 printf("\nDialing");                                         }                                         if  (LINECALLSTATE_RINGBACK&lm.dwParam1)                                         {                                                 printf("\nRinging...");                                         }                                         if  (LINECALLSTATE_BUSY&lm.dwParam1)                                         {                                                 printf("\nBusy...");                                         }                                         if  (LINECALLSTATE_PROCEEDING&lm.dwParam1)                                         {                                                 printf("\nProceeding...");                                         }                                                                                         if  (LINECALLSTATE_CONNECTED&lm.dwParam1)                                         {                                                 printf("\nConnected...");                                         }                                                                                         if  (LINECALLSTATE_DISCONNECTED&lm.dwParam1)                                         {                                                 printf("\nDisconnected...");                                                 lineClose(hLine);                                         }                                         if  (LINECALLSTATE_IDLE&lm.dwParam1)                                         {                                                 printf("\nIdle...");                                                 lineClose(hLine);                                         }                                 }  //  ...switch  (lm.dwMessageID)                         }  //  ...while  ((dwStatus  =  WaitForSingleObject(}  //  ...if  (ret  ==  0)         }         lineShutdown(hLineApp);         return  0; } void  ReportTAPIError(LONG  ret,  char  *op) {         switch  (ret)         {         case  LINEERR_INVALAPPNAME:                 printf("\nop=%s  Ret=LINEERR_INVALAPPNAME",  op);                 break;         case  LINEERR_OPERATIONFAILED:                 printf("\nop=%s  Ret=LINEERR_OPERATIONFAILED",  op);                 break;         case  LINEERR_INIFILECORRUPT:                 printf("\nop=%s  Ret=LINEERR_INIFILECORRUPT",  op);                 break;         case  LINEERR_INVALPOINTER:                 printf("\nop=%s  Ret=LINEERR_INVALPOINTER",  op);                 break;         case  LINEERR_REINIT:                 printf("\nop=%s  Ret=LINEERR_REINIT",  op);                 break;         case  LINEERR_NOMEM:                 printf("\nop=%s  Ret=LINEERR_NOMEM",  op);                 break;         case  LINEERR_INVALPARAM:                 printf("\nop=%s  Ret=LINEERR_INVALPARAM",  op);                 break;         case  LINEERR_BADDEVICEID:                 printf("\nop=%s  Ret=LINEERR_BADDEVICEID",  op);                 break;         case  LINEERR_NODRIVER:                 printf("\nop=%s  Ret=LINEERR_NODRIVER",  op);                 break;         case  LINEERR_INCOMPATIBLEAPIVERSION:                 printf("\nop=%s  Ret=LINEERR_INCOMPATIBLEAPIVERSION",  op);                 break;         case  LINEERR_INVALAPPHANDLE:                 printf("\nop=%s  Ret=LINEERR_INVALAPPHANDLE",  op);                 break;         case  LINEERR_RESOURCEUNAVAIL:                 printf("\nop=%s  Ret=LINEERR_RESOURCEUNAVAIL",  op);                 break;         case  LINEERR_UNINITIALIZED:                 printf("\nop=%s  Ret=LINEERR_UNINITIALIZED",  op);                 break;         case  LINEERR_OPERATIONUNAVAIL:                 printf("\nop=%s  Ret=LINEERR_OPERATIONUNAVAIL",  op);                 break;         case  LINEERR_NODEVICE:                 printf("\nop=%s  Ret=LINEERR_NODEVICE",  op);                 break;         case  LINEERR_ALLOCATED:                 printf("\nop=%s  Ret=LINEERR_ALLOCATED",  op);                 break;         case  LINEERR_LINEMAPPERFAILED:                 printf("\nop=%s  Ret=LINEERR_LINEMAPPERFAILED",  op);                 break;         case  LINEERR_INCOMPATIBLEEXTVERSION:                 printf("\nop=%s  Ret=LINEERR_INCOMPATIBLEEXTVERSION",  op);                 break;         case  LINEERR_INVALMEDIAMODE:                 printf("\nop=%s  Ret=LINEERR_INVALMEDIAMODE",  op);                 break;         case  LINEERR_STRUCTURETOOSMALL:                 printf("\nop=%s  Ret=LINEERR_STRUCTURETOOSMALL",  op);                 break;         case  LINEERR_INVALPRIVSELECT:                 printf("\nop=%s  Ret=LINEERR_INVALPRIVSELECT",  op);                 break;         case  LINEERR_ADDRESSBLOCKED:                 printf("\nop=%s  Ret=LINEERR_ADDRESSBLOCKED",  op);                 break;         case  LINEERR_DIALBILLING:                 printf("\nop=%s  Ret=LINEERR_DIALBILLING",  op);                 break;         case  LINEERR_DIALDIALTONE:                 printf("\nop=%s  Ret=LINEERR_DIALDIALTONE",  op);                 break;         case  LINEERR_NOTOWNER:                 printf("\nop=%s  Ret=LINEERR_NOTOWNER",  op);                 break;         case  LINEERR_DIALPROMPT:                 printf("\nop=%s  Ret=LINEERR_DIALPROMPT",  op);                 break;         case  LINEERR_DIALQUIET:                 printf("\nop=%s  Ret=LINEERR_DIALQUIET",  op);                 break;         case  LINEERR_INVALCALLHANDLE:                 printf("\nop=%s  Ret=LINEERR_INVALCALLHANDLE",  op);                 break;         case  LINEERR_INVALCALLSTATE:                 printf("\nop=%s  Ret=LINEERR_INVALCALLSTATE",  op);                 break;         case  LINEERR_INVALCOUNTRYCODE:                 printf("\nop=%s  Ret=LINEERR_INVALCOUNTRYCODE",  op);                 break;         } } 
'; } else { lstrcpy(szLineName, (LPSTR)DevCaps+ DevCaps->dwLineNameOffset); } printf("\nDevicename%s",szLineName); } delete(char*)DevCaps; //SetwhatTAPIcallstheaddress,whichis //reallyjustaphonenumber. sprintf(szAddress,"%s,,,%s#",beeperNumber,beepTo); ret=lineMakeCall(hLine,&hCall,szAddress,0,0); if(ret>0) { printf("\nlineMakeCallreturned%d",ret); } ReportTAPIError(ret,"lineMakeCall"); while((dwStatus=WaitForSingleObject(LineInitializeExParams.Handles.hEvent,20000))== WAIT_OBJECT_0) { LINEMESSAGElm; //TAPI'sreturningsomeinformation. if((ret=lineGetMessage(hLineApp,&lm,0))!=0) { ReportTAPIError(ret,"lineGetMessage"); return(ret); } switch(lm.dwMessageID) { caseLINE_REPLY: if((long)lm.dwParam2<0) { printf("\nErroroccured...RequestID%d", lm.dwParam1); } break; caseLINE_CALLSTATE: if(LINECALLSTATE_DIALTONE&lm.dwParam1) { printf("\nDialtonedetected"); } if(LINECALLSTATE_DIALING&lm.dwParam1) { printf("\nDialing"); } if(LINECALLSTATE_RINGBACK&lm.dwParam1) { printf("\nRinging..."); } if(LINECALLSTATE_BUSY&lm.dwParam1) { printf("\nBusy..."); } if(LINECALLSTATE_PROCEEDING&lm.dwParam1) { printf("\nProceeding..."); } if(LINECALLSTATE_CONNECTED&lm.dwParam1) { printf("\nConnected..."); } if(LINECALLSTATE_DISCONNECTED&lm.dwParam1) { printf("\nDisconnected..."); lineClose(hLine); } if(LINECALLSTATE_IDLE&lm.dwParam1) { printf("\nIdle..."); lineClose(hLine); } }//...switch(lm.dwMessageID) }//...while((dwStatus=WaitForSingleObject(}//...if(ret==0) } lineShutdown(hLineApp); return0; } voidReportTAPIError(LONGret,char*op) { switch(ret) { caseLINEERR_INVALAPPNAME: printf("\nop=%sRet=LINEERR_INVALAPPNAME",op); break; caseLINEERR_OPERATIONFAILED: printf("\nop=%sRet=LINEERR_OPERATIONFAILED",op); break; caseLINEERR_INIFILECORRUPT: printf("\nop=%sRet=LINEERR_INIFILECORRUPT",op); break; caseLINEERR_INVALPOINTER: printf("\nop=%sRet=LINEERR_INVALPOINTER",op); break; caseLINEERR_REINIT: printf("\nop=%sRet=LINEERR_REINIT",op); break; caseLINEERR_NOMEM: printf("\nop=%sRet=LINEERR_NOMEM",op); break; caseLINEERR_INVALPARAM: printf("\nop=%sRet=LINEERR_INVALPARAM",op); break; caseLINEERR_BADDEVICEID: printf("\nop=%sRet=LINEERR_BADDEVICEID",op); break; caseLINEERR_NODRIVER: printf("\nop=%sRet=LINEERR_NODRIVER",op); break; caseLINEERR_INCOMPATIBLEAPIVERSION: printf("\nop=%sRet=LINEERR_INCOMPATIBLEAPIVERSION",op); break; caseLINEERR_INVALAPPHANDLE: printf("\nop=%sRet=LINEERR_INVALAPPHANDLE",op); break; caseLINEERR_RESOURCEUNAVAIL: printf("\nop=%sRet=LINEERR_RESOURCEUNAVAIL",op); break; caseLINEERR_UNINITIALIZED: printf("\nop=%sRet=LINEERR_UNINITIALIZED",op); break; caseLINEERR_OPERATIONUNAVAIL: printf("\nop=%sRet=LINEERR_OPERATIONUNAVAIL",op); break; caseLINEERR_NODEVICE: printf("\nop=%sRet=LINEERR_NODEVICE",op); break; caseLINEERR_ALLOCATED: printf("\nop=%sRet=LINEERR_ALLOCATED",op); break; caseLINEERR_LINEMAPPERFAILED: printf("\nop=%sRet=LINEERR_LINEMAPPERFAILED",op); break; caseLINEERR_INCOMPATIBLEEXTVERSION: printf("\nop=%sRet=LINEERR_INCOMPATIBLEEXTVERSION",op); break; caseLINEERR_INVALMEDIAMODE: printf("\nop=%sRet=LINEERR_INVALMEDIAMODE",op); break; caseLINEERR_STRUCTURETOOSMALL: printf("\nop=%sRet=LINEERR_STRUCTURETOOSMALL",op); break; caseLINEERR_INVALPRIVSELECT: printf("\nop=%sRet=LINEERR_INVALPRIVSELECT",op); break; caseLINEERR_ADDRESSBLOCKED: printf("\nop=%sRet=LINEERR_ADDRESSBLOCKED",op); break; caseLINEERR_DIALBILLING: printf("\nop=%sRet=LINEERR_DIALBILLING",op); break; caseLINEERR_DIALDIALTONE: printf("\nop=%sRet=LINEERR_DIALDIALTONE",op); break; caseLINEERR_NOTOWNER: printf("\nop=%sRet=LINEERR_NOTOWNER",op); break; caseLINEERR_DIALPROMPT: printf("\nop=%sRet=LINEERR_DIALPROMPT",op); break; caseLINEERR_DIALQUIET: printf("\nop=%sRet=LINEERR_DIALQUIET",op); break; caseLINEERR_INVALCALLHANDLE: printf("\nop=%sRet=LINEERR_INVALCALLHANDLE",op); break; caseLINEERR_INVALCALLSTATE: printf("\nop=%sRet=LINEERR_INVALCALLSTATE",op); break; caseLINEERR_INVALCOUNTRYCODE: printf("\nop=%sRet=LINEERR_INVALCOUNTRYCODE",op); break; } }
NOTE
This application could be made much more elaborate with an alphanumeric pager using the TAP protocol, but numeric pagers are simpler and more numerous than the alphanumeric kind. Information on the TAP protocol can be found at http://www.pcia.com/.

The main function in Listing 7-2 does nothing more than call the CallBeeper function. CallBeeper accepts two strings as parameters. The first, beeperNumber , is the beeper access number to dial to get to the beeper, and the second, beepTo , is the number that is sent to the beeper. CallBeeper places a delay between the two numbers so that the beepTo string is sent when the service expects it.

Using lineInitializeEx

The first step in any TAPI application is a call to lineInitializeEx :

 LONGWINAPIlineInitializeEx(LPHLINEAPPlphLineApp, HINSTANCEhInstance, LINECALLBACKlpfnCallback, LPCSTRlpszFriendlyAppName, LPDWORDlpdwNumDevs, LPDWORDlpdwAPIVersion, LPLINEINITIALIZEEXPARAMSlpLineInitializeExParams); 

The first parameter is the address of a handle to be filled in by lineInitializeEx upon a successful call. This handle is used for many of the other TAPI calls required. The second parameter is hInstance . In most cases, this can be set to NULL to allow TAPI to use the module handle of the root executable of the process making the call. Next is lpfnCallback , a callback function used when neither the event handle nor the completion port method of asynchronous notifications is selected. A pointer to the "friendly" name of the application ”used by the user interface to display a user -friendly application name ”is passed as the lpszFriendlyAppName parameter. The number of devices is placed by TAPI in the DWORD pointed to by lpdwNumDevs , and the API version is placed by TAPI into the DWORD pointed to by lpdwAPIVersion .

The final parameter, lpLineInitializeExParams , points to a structure of type LINEINITIALIZEEXPARAMS:

 typedefstructlineinitializeexparams_tag{ DWORDdwTotalSize; DWORDdwNeededSize; DWORDdwUsedSize; DWORDdwOptions; union { HANDLEhEvent; HANDLEhCompletionPort; }Handles; DWORDdwCompletionKey; }LINEINITIALIZEEXPARAMS; 

Upon entry to lineInitializeEx , the dwTotalSize must be initialized to the size of the buffer allocated to this structure. The asynchronous notification type is specified in dwOptions and is one of the following values: LINEINITIALIZEEXOPTION_ USECOMPLETIONPORT, LINEINITIALIZEEXOPTION_USEEVENT, or LINEINITIALIZEEXOPTION_USEHIDDENWINDOW. Applications using TAPI 3 and later might also use LINEINITIALIZEEXOPTION_CALLHUBTRACKING to enable call hub tracking notifications. The TAPI21Sample example in Listing 7-2 uses LINEINITIALIZEEXOPTION_USEEVENT to specify that the hEvent member of the Handles union of the structure should contain an event handle upon a successful call to lineInitializeEx . Note that an older initialization function, lineInitialize , is not supported in TAPI 2 or later.

After the call to lineInitializeEx is completed, TAPI21Sample calls the local ReportTAPIError function. Since no convenient function is provided to convert a numeric return value to a string, ReportTAPIError provides this functionality, in this case printing the result to the screen. ReportTAPIError prints the error condition and its associated constant because the error conditions returned by TAPI are large negative numbers. (While you might remember that the standard Win32 API error 2 means the file cannot be found and 5 means access is denied , it is unlikely you will remember that -2147483646 means LINEERR_BADDEVICEID.)

Finding a TAPI device and negotiating versions

If the return value from lineInitializeEx is 0, we have the number of TAPI devices that the system supports as well as the TAPI version. In TAPI21Sample these devices are just displayed, but the version number in particular might be used to make decisions about what level of functionality can be supported. Simply knowing the version of TAPI returned by lineInitializeEx is not sufficient. When you want to use a specific device, the version of the interface must be negotiated between the application and TAPI for that device. This negotiation is performed using lineNegotiateAPIVersion :

 LONGWINAPIlineNegotiateAPIVersion(HLINEAPPhLineApp, DWORDdwDeviceID, DWORDdwAPILowVersion, DWORDdwAPIHighVersion, LPDWORDlpdwAPIVersion, LPLINEEXTENSIONIDlpExtensionID); 

The handle returned from the call to lineInitializeEx is passed as the first parameter to lineNegotiateAPIVersion . The device ID is passed as the second parameter. This is a number that must be between 0 and the number of devices minus one. The next two parameters, dwAPILowVersion and dwAPIHighVersion , are used to let the application let TAPI know what versions it is willing to support. Before TAPI 3.0, programmers commonly specified a reasonable number for dwAPILowVersion and a very high number for dwAPIHighVersion , expecting that all future TAPI versions would support essentially the same framework. This tactic is not recommended, and current applications that use this tactic can expect to break when run on a machine that tries to use TAPI 3.

The next parameter is a pointer to a DWORD that TAPI fills with the version to be used, if the call succeeds. The final parameter is lpExtensionID , a pointer to a LINEEXTENSIONID structure. This parameter is used to identify service provider-specific extensions. TAPI21Sample uses no service provider-specific extensions.

TAPI21Sample uses the following code segment to determine the device ID to be used. (This information is required because it is possible that multiple devices will be available, and they might not all support the service or the version you request.)

 //Loopthroughavailabledevicestillwefindagoodone. for(dwDeviceID=0;dwDeviceID<dwNumDevs;dwDeviceID++) { ret=lineNegotiateAPIVersion(hLineApp, dwDeviceID, 0x00020000, 0x00020001, &dwAPIVersion, &ExtensionID); if(ret==0) { ret=lineOpen(hLineApp,dwDeviceID,&hLine, dwAPIVersion,0,dwCallbackInstance, LINECALLPRIVILEGE_NONE,0,0); ReportTAPIError(ret,"lineOpen"); if(ret==0) { break; } } } 

This code fragment loops through the available devices and tries to negotiate a compatible version. Upon success, lineOpen is called. If this call succeeds, the loop is exited and the device ID in dwDeviceID is used for the rest of the processing. The prototype for lineOpen is as follows :

 LONGWINAPIlineOpen(HLINEAPPhLineApp, DWORDdwDeviceID, LPHLINElphLine, DWORDdwAPIVersion, DWORDdwExtVersion, DWORDdwCallbackInstance, DWORDdwPrivileges, DWORDdwMediaModes, LPLINECALLPARAMSconstlpCallParams); 

The first parameter is the handle obtained from the call to lineInitializeEx . The device ID to open is passed as dwDeviceID , and lphLine points to the buffer where lineOpen will place a line handle upon success. Version information is contained in the next two parameters. The API version returned by the previous call to lineNegotiateAPIVersion is passed as dwAPIVersion , and dwExtVersion contains the extension version previously negotiated. In TAPI21Sample, no extensions are used so dwExtVersion is 0.

The sixth parameter, dwCallbackInstance , is a context identifier that is passed back to the application by TAPI with messages associated with this line. This number is not interpreted by TAPI, but simply passed back to the application. The privileges the application requests on the line are passed in the dwPrivileges parameter. Table 7-1 outlines the constants and combinations allowed for this parameter.

NOTE
TAPI structures have some unusual properties. For instance, LINECALLPARAMS contains a member named dwOrigAddressOffset . This variable holds the offset from the beginning of the structure to the variable length value of the originating address. This differs from many other Win32 structures that contain true embedded pointers or embedded character arrays to contain strings. Another example of a structure that uses this type of variable is the LINEDEVCAPS structure, described in the next section. You cannot simply allocate a buffer the size of the declared structure, since this will not allow the fields that are placed after the declared structure to be sent.

Table 7-1 Possible Values for the dwPrivileges Parameter to lineOpen

Privilege Meaning
LINECALLPRIVILEGE_NONE The application can make only outgoing calls.
LINECALLPRIVILEGE_MONITOR The application can monitor only incoming and outgoing calls.
LINECALLPRIVILEGE_OWNER The application can own only incoming calls of the types specified by dwMediaMode .
LINECALLPRIVILEGE_MONITOR + LINECALLPRIVILEGE_OWNER The application can be the owner of calls of the types specified by dwMediaMode , but it is a monitor for calls it does not own.
LINECALLPRIVILEGE_SINGLEADDRESS The application is interested only in new calls that appear on the address specified by the dwAddressID member of the lpCallParams , which must be specified. (More details are available in the TAPI documentation.)
LINECALLPRIVILEGE_PROXY The application is willing to handle certain types of requests from other applications that have the line open. Requests are delivered to the application using LINE_PROXYREQUEST messages. (More details are available in the TAPI documentation.)

Interestingly, the privilege required for the task performed by TAPI21Sample is the somewhat misnamed LINECALLPRIVILEGE_NONE because this program is only interested in making outgoing calls. Any combination not listed in Table 7-1 is not allowed and will result in an error.

TAPI can interact with many different types of calls, including but not limited to interactive voice, automated voice, data modem, G3 fax, and Telephony Devices for the Deaf. The types of calls the application is interested in are specified in the dwMediaModes parameter. This parameter is meaningful only if the dwPrivileges parameter includes LINECALLPRIVILEGE_OWNER. The final parameter is lpCallParams , a pointer to a buffer containing a LINECALLPARAMS structure. This structure is only used if a special value for dwDeviceID is used ”LINEMAPPER. This special constant tells lineOpen to search for a matching device based upon the values in the LINECALLPARAMS structure.

Getting the capabilities of a TAPI line

Once lineOpen succeeds, we try to get information about the device ID selected. This requires calling lineGetDevCaps :

 LONGWINAPIlineGetDevCaps(HLINEAPPhLineApp, DWORDdwDeviceID, DWORDdwAPIVersion, DWORDdwExtVersion, LPLINEDEVCAPSlpLineDevCaps); 

The first argument is the handle returned by lineInitializeEx . The device ID, API version, and extension version are passed in dwDeviceID, dwAPIVersion, and dwExtVersion , respectively. These values are the ones previously determined via calls to lineNegotiateAPIVersion .

The final argument, lpLineDevCaps , is a structure that contains within it offsets that point to variable-length buffers just beyond the declared area of the buffer. To support this behavior, the first three elements of this structure are dwTotalSize , dwNeededSize , and dwUsedSize . The total size should be set before the call to lineGetDevCaps , the needed size is the size that must be allocated in order to hold all the information that could be returned, and the used size is the size of the buffer that contains useful information.

The following fragment from TAPI21Sample illustrates the method to get device capabilities from TAPI.

 DevCaps=(LPLINEDEVCAPS)newchar[1024]; DevCaps->dwTotalSize=1024; ret=lineGetDevCaps(hLineApp,dwDeviceID, dwAPIVersion,0,DevCaps); ReportTAPIError(ret,"lineGetDevCaps"); if(ret>=0&&DevCaps->dwLineNameSize>0) { if(DevCaps->dwLineNameSize>(254)) { strncpy(szLineName, (LPSTR)DevCaps+ DevCaps->dwLineNameOffset,254); szLineName[254]=' 
 DevCaps  =  (LPLINEDEVCAPS)new  char[1024]; DevCaps->dwTotalSize  =  1024; ret  =  lineGetDevCaps(hLineApp,  dwDeviceID,         dwAPIVersion,  0,  DevCaps); ReportTAPIError(ret,  "lineGetDevCaps"); if  (ret  >=  0  &&  DevCaps->dwLineNameSize  >  0) {         if  (DevCaps->dwLineNameSize  >  (254))         {                 strncpy  (szLineName,                         (LPSTR)  DevCaps  +                           DevCaps->dwLineNameOffset,  254);                 szLineName[254]  =  '\0';         }         else         {                 lstrcpy(szLineName,  (LPSTR)  DevCaps  +                         DevCaps->dwLineNameOffset);         }         printf("\nDevice  name  %s  ",  szLineName); } delete  (char  *)DevCaps; 
'; } else { lstrcpy(szLineName,(LPSTR)DevCaps+ DevCaps->dwLineNameOffset); } printf("\nDevicename%s",szLineName); } delete(char*)DevCaps;

This code allocates a buffer and sets dwTotalSize to the size of the buffer before calling lineGetDevCaps . After the successful call to lineGetDevCaps , it checks dwLineNameSize to see whether the name of the line is present. This name is commonly the name of the modem, for instance "Sportster 28800-33600 External." The name, if present, is located dwLineNameOffset bytes from the beginning of the structure. TAPI21Sample prints the name, but the name might be used for logging purposes in a server-based application. In addition to the name of the device, a great deal of other information is present in lineDevCaps , including the types of calls the line is capable of handling.

TAPI addresses

As is the case in many areas of Win32 programming, unfortunate TAPI naming choices have created no end of grief for developers over the years . What is an address from the TAPI perspective? It is a phone number to you and me. Of course, there is more to the story.

For instance, suppose "+1 (732) 555-2683" is the phone number given as the address to a TAPI function. What would happen if this address were dialed from my home phone, also in the 732 area code? The call would not go through because calls within an area must not include the area code in the number you dial. The solution is to translate the address using the information about the current area and country code so that the number to be dialed is correct, relative to that current area and country code. This translation from the canonical address results in a phone number that can be dialed correctly from the current location. In addition to correcting the phone number to a proper relative phone number, calling cards can be specified and call waiting can be disabled, if there is a cancel call waiting string defined for the current location. This flexibility is much more useful for mobile users. These users might dial from many different locations, and so entering canonical addresses for all contacts is worthwhile. However, even for stationary users, such as server-based applications, entering canonical addresses and doing translation in the applications can be useful in case the area code where the server is located splits , requiring calls that previously dialed using 7 digits to dial 11 digits ”1 plus the area code plus the 7 digits of the phone number.

Making the call with TAPI

The address, or phone number, in Listing 7-2 is built up using the beeper access number and the number to send to the beeper service. Commas are inserted between the numbers to allow dialing to pause between the two numbers and thus allow the beeper service to answer the call. The call is initiated through a call to lineMakeCall :

 LONGWINAPIlineMakeCall(HLINEhLine, LPHCALLlphCall, LPCSTRlpszDestAddress, DWORDdwCountryCode, LPLINECALLPARAMSconstlpCallParams); 

The first parameter is hLine , the handle to the line obtained through a call to lineOpen . The call is tracked after initiated using a call handle, which is returned in the buffer pointed to by the second parameter, lphCall . The address (phone number to you and me) is sent in the lpszDestAddress parameter with dwCountryCode set to the country code, or 0 to use the default for this dialing location.

The final parameter is lpCallParams . This parameter is a pointer to a structure that can be used to specify particulars such as the types of call to support, blocking of caller ID, and the originating address. This parameter can be NULL if no special treatment is required. Additional digits or all dialing for the call can be done using lineDial . If any address is specified in the call to lineMakeCall , the address should be terminated with a semicolon. The parameters are a subset of the parameters passed to lineMakeCall , with the exception of the hCall , which is not a pointer but must be the handle passed back from lineMakeCall .

 LONGWINAPIlineDial(HCALLhCall, LPCSTRlpszDestAddress, DWORDdwCountryCode); 

Listing 7-2 does not use lineDial , but it could if we wanted to split the dialing. The call handle is not valid until a LINE_REPLY message is received from TAPI. TAPI messages are described in the next section.

Waiting for TAPI messages

As I mentioned previously, TAPI21Sample uses an event object to receive asynchronous notifications from TAPI. One important distinction with using an event object supplied by TAPI is that the only operation that can safely be done with the object is waiting for the object, using one of the WaitForSingleObject, WaitForMultipleObjects , or another one of the Win32 waiting functions. SetEvent , ClearEvent , or CloseHandle should not be called using the returned event handle. When the event is signaled, TAPI has a notification waiting, and a call to lineGetMessage gives us the actual notification.

 LONGWINAPIlineGetMessage(HLINEAPPhLineApp, LPLINEMESSAGElpMessage, DWORDdwTimeout); 

The first parameter is the handle returned by lineInitializeEx . The second parameter is a pointer to a TAPI LINEMESSAGE structure, lpMessage . The final parameter is dwTimeout , the amount of time to wait for the message. In Listing 7-2 we set this to 0 to allow the function to return immediately if a message is not waiting since we only call this function when the event handle returned by TAPI is signaled. If no message is waiting, a serious error has occurred because TAPI has just told us that a message was waiting. The LINEMESSAGE structure has the following members :

 typedefstructlinemessage_tag{ DWORDhDevice; DWORDdwMessageID; DWORDdwCallbackInstance; DWORDdwParam1; DWORDdwParam2; DWORDdwParam3; }LINEMESSAGE; 

The hDevice member is either a handle to the line or the call, depending upon the context, as decided by dwMessageID . For instance, one of the messages that can be received is LINE_CALLSTATE. If dwMessageID is LINE_CALLSTATE, hDevice is a handle to the call. On the other hand, if dwMessageID is LINE_REPLY, hDevice has no meaning and should be ignored. The various message types are described in the TAPI documentation.

Listing 7-2 contains a switch statement controlled by the value of dwMessageID :

 switch(lm.dwMessageID) { caseLINE_REPLY: if((long)lm.dwParam2<0) { printf("\nErroroccured...RequestID%d",lm.dwParam1); } break; caseLINE_CALLSTATE: if(LINECALLSTATE_DIALTONE&lm.dwParam1) { printf("\nDialtonedetected"); } if(LINECALLSTATE_DIALING&lm.dwParam1) { printf("\nDialing"); } if(LINECALLSTATE_RINGBACK&lm.dwParam1) { printf("\nRinging..."); } if(LINECALLSTATE_BUSY&lm.dwParam1) { printf("\nBusy..."); } if(LINECALLSTATE_PROCEEDING&lm.dwParam1) { printf("\nProceeding..."); } if(LINECALLSTATE_CONNECTED&lm.dwParam1) { printf("\nConnected..."); } if(LINECALLSTATE_DISCONNECTED&lm.dwParam1) { printf("\nDisconnected..."); lineClose(hLine); } if(LINECALLSTATE_IDLE&lm.dwParam1) { printf("\nIdle..."); lineClose(hLine); } }//...switch(lm.dwMessageID) 

The order or even the presence of any of these specific notifications depends upon the TAPI service provider. In Listing 7-2 the duration of the call should be brief in any event, so setting a timeout of 20 seconds allows the call to proceed, and after 20 seconds with no further TAPI notification, the call and the session are terminated using lineShutdown.

What Else Can TAPI Do?

TAPI can do much more than signal a beeper. TAPI can be set to monitor the line for specific tones using lineMonitorTones , as well as monitor the type of call using lineMonitorMedia . TAPI can be used to create a full service call center, allowing parking and pickup of calls, conferencing, and many other features considered essential for an up-to-date phone system. Using the phone set of functions (not discussed in this book), individual phones can be controlled, allowing lights to blink as needed on the actual phone deskset.

Later in this chapter, we'll see how the ability to beep a network administrator and the ability to send e-mail will work together to create a service application that notifies responsible parties when it encounters a problem.



Inside Server-Based Applications
Inside Server-Based Applications (DV-MPS General)
ISBN: 1572318171
EAN: 2147483647
Year: 1999
Pages: 91

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net