If you are compiling one of the many TAPI sample applications or any other TAPI code that was written before TAPI 2.0 was released and you are running on Windows 95 then you may see many of your TAPI function calls return LINEERR_OPERATIONUNAVAIL or other similar errors. The reason for this is twofold: Many TAPI functions and structures have changed slightly with TAPI 2.0, and most development tools currently default to assuming TAPI 2.0 when compiling applications.
Try adding the following line of code BEFORE you include tapi.h anywhere in your source code:
#define TAPI_CURRENT_VERSION 0x00010004
This will make the compiler assume that TAPI 1.4 is being used (the default for Windows 95) instead of TAPI 2.0 or later. Unfortunately Microsoft did not document the need for this #define when TAPI 2.0 was released. The only place that this is documented is in a comment at the top of newer versions of the tapi.h header file.
2. How can I get the name of the COM port my modem is connected to?
TAPI applications really should not need to know about COM ports, especially since there is a great deal of TAPI hardware available that doesn't make use of COM ports at all. If all you intend to do is display information in your application then you should rely on device information in the LINEADDRESSCAPS and LINEDEVCAPS structures as well as functions such as lineConfigDialog and not try to make use of COM port information yourself.
There can be times when you may have a valid reason to get the name of the COM port a modem is connected to. Unfortunately, the TAPI specification for versions 1.4 and earlier did not include any means of determining this information. As of TAPI 2.0, the device ID "comm/datamodem/portname" is listed as one of the standard recognized device ID's. So if you are using TAPI 2.0 or later you can call lineGetID with this device ID, and if the service provider you are using makes use of a COM port it should return you the string name of the port (i.e. "COM1").
Although the "comm/datamodem/portname" device ID was introduced with TAPI 2.0, it is possible for some TAPI 1.x TSP's to support it. Device ID strings are simply passed through TAPI to the TSP being used, and if the TSP recognizes the string it will return the appropriate information. So companies that have developed TAPI 1.x TSP's after the TAPI 2.0 specifications were released, it's possible they may support this device ID as well. Keep in mind, however, that there is no requirement that a TSP support this, or any other device ID. So when you call lineGetID with this device ID you may get a LINEERR_OPERATIONUNAVAIL error which would indicate the TSP doesn't recognize the device ID.
IF you know that your application is using the Unimodem or Unimodem/V service provider then there is another way you can obtain the name of the COM port, no matter what version of TAPI you are using. This method is not supported by Microsoft, and future versions of Unimodem may behave differently, so use this method at your own risk. The Unimodem and Unimodem/V service providers store a partial registry key in the dwDeviceSpecific fields of the LINEDEVCAPS structure. If you look in the registry under this key you can locate the name of the COM port and other information. For example, if you examine the LINEDEVCAPS for a modem you would find a string similar to this:
SYSTEM\CurrentControlSet\Control\Class\{4D36E96D-E325-11CE-BFC1-08002BE10318}\0000
If you look in the registry under this key under the HKEY_LOCAL_MACHINE key then you will find a string value named "AttachedTo". The value of this string is the identifier of the COM port that the modem is connected to.
3. Why doesn't my application detect when the caller hangs up?
There are a few different reasons why this may be happening, and it depends in part on the kind of telephone line that your application is running on. When a telephone call is disconnected (the term many people use is "line drop") a special signal is sent over the telephone line. With standard analog telephone lines that are found in most residential homes, small businesses, etc. a line drop is signaled by a momentary drop in the electrical current on the telephone line (called "loop current drop"). PBX's typically do not generate a loop current drop when a call is disconnected. Many PBX's use a fast sounding busy signal, proprietary digital signaling, or what is known as in-band signaling (a combination of DTMF tones) to signal when a call has been disconnected.
Many older modems were not designed to detect the loop current drop that signals a disconnect, but would instead rely on the assumption that when the modems carrier (the CD light on most modems) was stopped then the two modems were done communicating with each other and the call was at an end. Telephone answering machines, most standard modems, and other devices are designed to detect the loop current drop that signals when a call has been disconnected.
Unfortunately, the problem with not detecting when a caller hangs up can often be traced to the TSP that is being used. The Unimodem and Unimodem/V TSP's do not recognize when modems disconnect due to loop current drops, even if the modem is capable of detecting it. When placing a data call with Unimodem or Unimodem/V the TSP relies on the same loss of carrier that all modems have historically relied on. When placing a voice call with Unimodem, the TSP relies on human intervention to tell it when a call is complete. If you place a voice call with Unimodem you will see a dialog box asking you to click on a "hangup" button when the call is complete. If you place a call with Unimodem/V and are using a supported voice modem then you won't see the "hangup" dialog box, however your application still may not be able to detect when a caller hangs up. Unimodem/V can only detect a line drop when it hears a fast busy signal on the phone line. Unfortunately many residential telephone offices do not generate fast busy signals on their lines when a caller disconnects. The most common environment where you will find a fast busy signal is in smaller PBX's or "key systems". If Unimodem/V is hooked to a PBX that generates a fast busy signal when a call is disconnected then it will send your application a LINECALLSTATE_DISCONNECTED message as expected.
This sort of behavior is bound to change based on the hardware and TSP being used. The only way to know for certain whether or not a LINECALLSTATE_DISCONNECTED is properly generated when using a specific phone line, TSP, and hardware, is to actually test it. This is an excellent example of when a utility like the TAPI Browser would come in handy.
For more technical information on loop current vist http://www.sandman.com/loopcur.html
4. Why does my application return LINECALLSTATE_CONNECTED when the call hasn't been answered yet?
Trying to place a voice call and detecting if and when a caller answers the phone is known as "call progress detection", and it is not a very easy thing to do. The telephony hardware has to be able to classify different signals such as dial tones, busy signals, ringing, operator intercepts (those "boop-boop-boop the number you have dialed is out of service" kinds of messages), as well as other things that a human caller takes for granted. Historically, voice modems were designed to answer voice calls as if it was a fancy answering machine, and not to place calls. Some voice modems these days do have call progress detection capabilities, but the quality varies widely from modem to modem. Many voice modems have little or no call progress detection capabilities.
Once again, the TSP being used has some part in the picture. The Unimodem TSP has no call progress detection at all, which is why it displays a dialog box asking the caller to click on a "connect" and "hangup" button as a call proceeds. The Unimodem/V TSP has very little call progress detection built into it. When placing a voice call with Unimodem/V it will always blindly generate a LINECALLSTATE_CONNECTED message within a second or two of successfully dialing the requested number. If a busy signal is detected at some point after the LINECALLSTATE_CONNECTED message is sent then the TSP will send a LINECALLSTATE_DISCONNECTED message with the dwParam2 parameter set to LINEDISCONNECTMODE_BUSY.
When you use different TSP's and hardware you will see different behavior. TSP's and voice processing boards from companies such as Dialogic and Rhetorex have excellent call progress detection capabilities and will accurately generate LINECALLSTATE_CONNECTED, LINECALLSTATE_BUSY, etc. TSP's for large PBX's are likely to generate even more information when a call is placed since the PBX has full control over the telephone lines. Again, the only way to know for sure what kinds of messages will be seen and when is to test the TSP and hardware with a utility like the TAPI Browser.
5. How can I determine the media mode of a call?
Exactly how (and if) you can determine the media mode (voice, fax, data, etc.) of a call will depend almost entirely upon the TSP and hardware you are using. Different TSP's will update media mode information at different times and in different ways, so you need to know exactly what kind of TSP's your application will be working with.
TSP's for environments like ISDN telephone lines, PBX's, etc. might accurately indicate the media mode at any time during the process of a call since this kind of information can be transmitted separately from the actual call itself. In these cases, you may be able to call lineGetCallInfo in order to obtain the media mode as soon as you receive a LINE_APPNEWCALL or a LINECALLSTATE_OFFERING message. You may also receive a LINECALLINFOSTATE_MEDIAMODE message shortly after answering the call which would indicate that you should then call lineGetCallInfo to get the media mode.
When using standard analog telephone lines there is no way of accurately knowing the media mode without actually answering the call and performing some sort of media detection. This should always be done at the TSP level, so the application shouldn't need to worry about the details of how it is done. The actual process of determining the media mode of a call can be fairly complex, even when only dealing with the three most common type of media: voice, fax, and data. When a modem makes a call to establish a data connection it remains silent until the called modem answers the phone and sends a signal that initiates the negotiation of the connection. So when an application answers a data call it hears only silence on the line. A fax machine that places a call, on the other hand, will generate a very specific tone, which makes it the easiest type of call to classify. The tone a fax machine generates when attempting to send a fax is a 1/2 second 1100Hz tone every 3 seconds. Accurately detecting the presence of a voice on the line can be a little tricky, especially if there is a bad telephone connection, if it's a long distance call routed through older telephone equipment, etc. The typical way that systems attempt to classify calls on analog phone lines is to try to detect either a voice or the fax tone for a period of a few seconds. If neither is detected then the modem initiates a data negotiation to try to determine if a datamodem is calling.
If you are using the Unimodem/V TSP with a supported voice modem then it can usually handle detecting voice vs. fax calls fairly reliably. It can also be configured to manually classify the type of call via the Operator Agent utility that comes with it. See question II-3 for information on the Operator Agent.
If you are using the Unimodem TSP then you will have to find some other way to determine the media mode. The Unimodem TSP is unable to classify the media of any calls.
When using any other TSP you will need to determine how it handles media detection on your own. Read any information on the TSP that you may have and use utilities like the TAPI Browser to test the behavior of the TSP.
6. Why does lineInitialize return LINEERR_UNINITIALIZED when I call it?
TAPI distinguishes between different applications via the hInstance parameter specified in the lineInitialize or lineInitializeEx call. When using TAPI 1.x under Windows 3.x or Windows 95 this parameter must not be NULL. The TAPI documentation erronously states that lineInitialize and lineInitializeEx will return LINEERR_INVALPARAM. In reality, the error LINEERR_UNINITIALIZED will be returned in this case.
With TAPI 2.0 or later you can use NULL as the hInstance parameter in calls to lineInitializeEx. In this case TAPI will use the module handle of the application.
7. How can I send and receive faxes with TAPI?
Faxing is one area of TAPI that is seriously lacking. Over the past few years a number of TAPI developers have asked Microsoft to define a standard for faxing via TAPI. Despite all the prodding and some half-hearted attempts, Microsoft has still not dealt with this outstanding issue. What this means for the TAPI application developer is that they must decide on some proprietary method of implementing fax support, which effectively defeats the entire purpose of TAPI.
As an example of the issue regarding faxing, suppose you wrote a TAPI voice application. Thanks to the standard method in which voice is supported under TAPI, this application could run on voice modems, PBX's that support voice in TAPI, high end voice processing hardware such as Dialogic, Rhetorex, or any other platform that supports the LINEMEDIAMODE_AUTOMATEDVOICE media mode. Suppose you wanted to add the ability to send or receive faxes to this voice application. Many of the TSP's that support LINEMEDIAMODE_AUTOMATEDVOICE also support LINEMEDIAMODE_G3FAX, so you would assume that you could send and receive faxes on these platforms. However, because there is no standard fax API, you would need to write your fax support using custom API's, so when you support faxing on one hardware platform you exclude yourself from other platforms. This is the problem faced in developing fax applications under TAPI.
The most commonly used means of supporting faxing under TAPI is through standard faxmodems, simply because faxmodems are inexpensive and widely available. If you decide to support faxing with standard faxmodems then there are essentially four different approaches that can be taken:
If you intend to support faxing using any other hardware then you will need to use whatever fax API that hardware may support.
8. How do I get information out of a structure using these dwXXXOffset and dwXXXSize fields?
TAPI makes extensive use of variably sized structures (VSS) in order to return information to applications. When you look at the definition of a TAPI structure you can immediately tell that it is a VSS if the first three fields of the structure are:
Not only will a VSS contain the above three fields, but it will undoubtedly also contain a series of fields with names in the format of dwXXXSize and dwXXXOffset. Because this data is variable in size, the dwXXX data is actually appended to the TAPI structure. The Offset field is the number of bytes from the beginning of the structure where the data can be found, and the Size field is the size of the actual data in bytes. Click here to see some sorce code that demonstrates how to extract the CallerID information from a VSS.
It is not uncommon for people to suddenly find themselves faced with the following dialog box when they try to place a voice call using TAPI:

This dialog box is the result of trying to place a voice call using the Unimodem TSP or trying to place a voice call using the Unimodem/V TSP when the modem either does not support voice or is not configured properly. So you will only see this dialog box when using the Unimodem TSP's and only when using LINEMEDIAMODE_INTERACTIVEVOICE IF the modem and/or TSP does not directly support voice calls. If you are using Unimodem/V and have a properly configured voice modem then you should not see this dialog box. If you are seeing this dialog box then you probably want to use LINEMEDIAMODE_DATAMODEM, need to install Unimodem/V, or do not have your modem properly configured.
10. What does the negative number returned by a TAPI function mean?
In TAPI some functions (e.g. lineMakeCall) are asynchronous. When you call the function, it returns immediately. The return code can be a positive number, indicating which LINE_REPLY message will be the reply for this function. It can also be a negative number, an error code. In reality, the number is not negative since the type is DWORD, which is not signed.
There are several ways of decoding the error message. First, look at it in unsigned Hex. The number should be something like 0x8000004B. You can then look in TAPI.H for this value, which in this case turns out to be LINEERR_RESOURCEUNAVAIL. Looking the error code up your help files should help you on why you may have gotten the error message.
You can also run the macro TAPIERROR_FORMATMESSAGE to turn the return value into a standard windows error code. Then call FormatMessage to return a test string of the error message.