Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Simplified TAPI and Serial communication through CTapiLine class

0.00/5 (No votes)
27 Dec 2004 1  
Simple tutorial on TAPI 2.x using TAPISample dialog app with CTapiLine class

Sample Image - TAPISample.jpg

Introduction

Microsoft TAPI (Telephony API) in Windows provides telephony functionality for the application, it is a bit confusing for programmers. I have many friends asking me the same question how to connect and communicate through modem. Finally, I came up with this sample tool to do the coaching. I tried to keep this tool source bare minimum to get an easy hold of TAPI programming even though I got to do some juggling. Here, I have used TAPI 2.0. TAPI 3.0 and above is much more simplified and easy to use.

About TAPI

Microsoft TAPI supports a wide variety of devices operating on voice grade lines, ISDN lines, and private branch exchanges. It provides services for placing outgoing calls, accepting incoming calls and managing calls, devices, etc. It simplifies the complexity to program telephony applications. You can ignore the need to know about modem initialization string, AT command and all other strenuous stuffs. TAPI is an abstraction layer built on top of TSPs (Telephony Service Providers). TSPs are the components that provide hardware or service-specific functionality. TSPs are specific to hardware. When an application requests that a telephony device perform an action, TAPI figures out which TSP services that device and makes a call to it. Then TSP will do the rest. Writing TSPs are the next level in telephony development. Rest of this article, leaving the TSPs, I�m explaining only TAPI programming.

TAPI is classified into five parts. Those are LineApp, Line, Call, PhoneApp and Phone. These parts in TAPI are depicted in the picture below:

LineApp: A LineApp is created when lineInitializeEx is called, and destroyed when lineShutdown is called. A process can create multiple LineApps if necessary, but need at least one to access TAPI. Each LineApp represents a distinct TAPI session.

Line: Lines are created by an application's call to lineOpen and are represented by their Line handles. In the end, these Line handles are closed by lineClose. Line devices are owned by the LineApp, and multiple Lines can be owned concurrently by a single LineApp. Lines are most commonly used for call control purposes.

Call: Application creates a call by making a call using lineMakeCall or answering an incoming call using lineAnswer. An application can destroy the call by calling lineDrop. Each Call is identified by a Call handle. Calls are created on a Line, and so are always associated with Line. Though a Call is always owned by a Line, the relationship is not necessarily one-to-one. A single Line can own more than one call. Call waiting and conferencing are other examples.

PhoneApp: A PhoneApp is created when phoneInitializeEx is called, and destroyed when phoneShutdown is called. A process can create multiple PhoneApps if necessary but need at least one to access phone specific TAPIs. Each PhoenApp represents a distinct TAPI session.

Phone: Phone is created by calling phoneOpen TAPI and destroyed by calling phoneClose TAPI. Phone is a logical representation of the terminal equipment. As in the physical telephony world, Phone can be used without calls. For example, you can use a telephone as an interface to a voice mail, SMS, etc.

In this example, totally excluding the PhoneApp and Phone sections, I concentrated on the first three connection-oriented parts. Take a look at the CTapiLine class; I feel it is comprehensive enough to teach call making and answering procedures.

CTapiLine class

This class is not a complete wrapper for TAPI as I said earlier. It posses a minimal set to make or answer a call. All the methods in this class return zero for success and non-zero for failure (other than the HANDLE return).

Open: Open method is the initial method to call. Inside this, it initializes the LineApp and opens the Line. It can open the line in any one of the two modes. First mode opens in the data/fax mode and the other opens in voice mode. To make your PC as an answering machine, you should open in the second mode. The first mode only connects or answers a data call. On successful open, it starts a thread to monitor the line message. Successful connection is notified through one of these messages.

MakeOutgoingCall: This method takes number string as the parameter and dials. It doesn't retrieve or prompt any other sophisticated dialing details. This method should be modified if it needs to dial country code, area code, etc. The simple code in this method is as follows:

LPLINECALLPARAMS lpCallParams;

lpCallParams = (LPLINECALLPARAMS)malloc(sizeof(LINECALLPARAMS)+1024);

memset(lpCallParams,0,sizeof(LINECALLPARAMS)+1024);

lpCallParams->dwTotalSize = sizeof(LINECALLPARAMS)+1024;

// 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;

// Address we are dialing.

lpCallParams->dwDisplayableAddressOffset = sizeof(LINECALLPARAMS);
lpCallParams->dwDisplayableAddressSize = strlen(szAddress);
strcpy((LPSTR)lpCallParams+sizeof(LINECALLPARAMS), szAddress);

lRet = lineMakeCall(m_hLine, &m_hCall, szAddress, 0, lpCallParams);

GetIncomingCall: This is a blocking method. Until a call comes, it blocks. After a call, it tries to answer and if it is successful, it returns zero. Success does not mean connected. The code, which answers, is...

// Now we wait for notification.

switch(WaitForSingleObject(m_hEventFromThread, INFINITE))
{
case WAIT_OBJECT_0:
    if(m_dwLineMsg == LINECALLSTATE_OFFERING)
    {        // answer the call

        lRet = lineAnswer(m_hCall, NULL, 0);
        lRet = (lRet>0)?0:lRet;

        if(lRet)
            Close();
        return lRet;
    }
    break;
case WAIT_TIMEOUT:
    return ERROR_TIMEOUT;
};

Message monitoring thread will indicate through an event when a call comes. Then this method answers by calling lineAnswer TAPI.

GetHandle: This returns the HANDLE of the requested class only after successful connection. This handle is very similar to what you would get by opening the COM port using the CreateFile API. By default, the handle is opened for overlapped I/O operation. After use, retrieved process should close this handle. Check the code below, it retrieves the HANDLE for serial communication:

VARSTRING *pvarStrDevID = (VARSTRING *)malloc(sizeof(VARSTRING)+255);

memset(pvarStrDevID,0,sizeof(VARSTRING)+255);
pvarStrDevID->dwTotalSize = sizeof(VARSTRING)+255;

long lRet = lineGetID(m_hLine,0,m_hCall,
        LINECALLSELECT_LINE,pvarStrDevID,"comm/datamodem");
if(lRet)
{
    *lError = lRet;
    return NULL;
}

*lError = 0;

return *((LPHANDLE)((char *)pvarStrDevID + pvarStrDevID->dwStringOffset));

lineGetID returns VARSTRING, which is a variable structure. On successful return, end of this structure will contain 4 byte HANDLE corresponding to the class requested. Next to this HANDLE, a null terminated modem string name (from the driver) will appear, which can be ignored. Here in this tool, specified hard-coded class name is "comm/datamodem".

Close: Close closes the entire operation. Inside, it closes the Line and shuts down the LineApp. Also terminates the message monitoring thread.

Class Usage

The following picture shows the typical TAPI use, using CTAPILine class methods.

One of the complications in TAPI programming is, most of the TAPIs use variable structure. I.e., when you code for successful operation, you should always check for LINEERR_STRUCTURETOOSMALL error code from the TAPI and reallocate the structure constantly to fit. To simplify, I haven't coded this class in that way. Here, I scrupulously allocated all of the variable structure with huge memory, it prevents the failure in ordinary execution.

Tool Demo

TAPISample is the perfect sample tool which uses the CTapiLine class. TAPISample tool's picture on top was captured when making a dialup connection to server in Internet. Those who have dialup Internet access can replicate this. To start, click 'Open' and type the number to dial. There is no feature in this tool to indicate the connected status. You can find the connected sign by the modem sound. Once connected, modem stops squeaking. Now get the handle by clicking "Get Handle" and start the reading by clicking "Read". Now you can see some bytes coming on "Data from stream" edit box. When you click "Write", the text you typed on "Data to write" will be sent.

Here in this tool, serial communication handle is retrieved. It is Overlapped serial communication handle. This is used on the "Serial communication" section on the dialog, same like a handle created by the CreateFile with Overlapped flag.

More on TAPI

Here, I have demonstrated only serial communication using TAPI. You can use this to record and playback Wave files on the phone line only if you have voice capable modem. Then you can use your PC as an answering machine.

To record or playback first, you must open with LINEMEDIAMODE_AUTOMATEDVOICE media mode (first mode in Open) and with LINECALLPRIVILEGE_OWNER privilege. Then request the handles for "wave/in" and "wave/out" and use these handles in Wave API waveInStart and waveOutWrite to record and playback.

Conclusion

I have performed very little test on this class and the tool, since I don't have the luxury of resources like extra PCs with modem, ISDN modem, data/fax/voice modem, etc. So I guarantee you this class has some serious bugs lurking. If you find any such thing, please update me. Please appreciate if you find this useful and don't curse me if you find it difficult. Definitely, this is a bit obsolete tutorial after TAPI 3.0 and later release. If you are a beginner, I bet it will provide you a good start. Thanks.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here