OS Version | Supported TAPI Versions | TSP Versions | Microsoft-Provided TSP |
Windows 3.1 | 1.3 | 1.3 | None |
Windows 95 (Retail) | 1.3, 1.4, (2.0, 2.1)1 | 1.3, 1.4, (2.0)1 | UNIMODEM2 |
Windows NT 4.0 | 1.3, 1.4, 2.0, (2.1)1 | 2.0 | UNIMODEM |
Windows 95 (OSR2) | 1.3, 1.4, (2.0, 2.1)1 | 1.3, 1.4, (2.0)1 | UNIMODEM/V, WAN |
Windows CE 1.0 | 1.5 | unsupported | N/A |
Windows CE 2.0 | 1.5 | 1.5 | UNIMODEM |
Windows 98 | 1.3, 1.4, 2.0, 2.1 | 1.3, 1.4, 2.0 | UNIMODEM/V, WAN |
Windows NT 5.0 | 1.3, 1.4, 2.0, 2.1, 3.0 | 2.0, 3.0, MSP | UNIMODEM 5, H.323, IPConf |
1Supported with the TAPI 2.1 patch. | |||
2UNIMODEM/V is available for download. |
Figure 6 SimpleTAPI
SimpleTAPI.c
// MODULE: SimpleTAPI.c
// PURPOSE: Demonstrates basic TAPI programming model
//
#include <stdio.h>
#include <windows.h>
#include <winbase.h>
#define TAPI_CURRENT_VERSION 0x00020000
#include <tapi.h>
#include "TapiInfo.h"
HLINEAPP g_hLineApp;
// Buffers used for pretty printing stuff...
CHAR strAddress[512];
CHAR strText[512], strText2[512];
// Various flags...
BOOL g_bIncoming; // Waits for Incoming Calls
BOOL g_bTranslate; // Perform Address Translation
BOOL g_bTranslateDialog; // Display Translation Dialog
BOOL g_bForce; // Force specified device
BOOL g_bQuit; // Session terminating.
DWORD g_dwMediaMode; // MediaMode for calls.
BOOL g_bTerminate; // Flag to tell message thread to terminate
DWORD g_dwDevId; // TAPI device used.
// Handles and Messages
HANDLE g_hTAPIEvent; // Created by TAPI
HANDLE g_hEvReply; // Used for Asynch Reply
HLINE g_hLine; // Line handle
HCALL g_hCall; // Call handle
DWORD g_dwTAPIMsg; // TAPI Message
DWORD g_dwNumDevs; // Total number of TAPI devices
DWORD g_dwTAPIVer; // TAPI Version
HANDLE hEventThread; // TAPI Event monitoring thread.
// Stuff defined somewhere else
extern DWORD WINAPI CallProc(LPVOID lpThreadParam);
extern DWORD WINAPI zTapiEventThread (LPVOID lpThreadParam);
extern BOOL ParseCommandLine(int ac, char * av[]);
// Stuff defined here...
BOOL WINAPI zControlHandlerRoutine (DWORD dwCtrlType);
DWORD SelectLine(HLINEAPP hLineApp, DWORD dwTAPIVersion, DWORD dwNumDevs, DWORD dwMediaMode);
void EndSession();
int main (int argc, char * argv[])
{
LINEINITIALIZEEXPARAMS lineInitializeExParams;
LONG lRes;
LINETRANSLATEOUTPUT *lpTranslateOutput;
g_dwTAPIVer = 0x00020000;
g_bIncoming = FALSE;
g_hCall = NULL;
g_bQuit = FALSE;
ParseCommandLine(argc, argv);
SetConsoleCtrlHandler(zControlHandlerRoutine, TRUE);
g_hEvReply = CreateEvent(NULL, FALSE, FALSE, NULL);
// Clear the structure before using...
memset(&lineInitializeExParams, 0, sizeof(LINEINITIALIZEEXPARAMS));
// Populate the options...
lineInitializeExParams.dwTotalSize = sizeof(LINEINITIALIZEEXPARAMS);
lineInitializeExParams.dwOptions = LINEINITIALIZEEXOPTION_USEEVENT;
// Initialize TAPI.
lRes = lineInitializeEx(&g_hLineApp, NULL, NULL,
"CallTest", &g_dwNumDevs, &g_dwTAPIVer, &lineInitializeExParams);
if(lRes != 0) //Oops!
{
FormatLineError(lRes, strText);
printf("TAPI Error: %s on lineInitializeEx.\r\n", strText);
CloseHandle(g_hEvReply);
return -1;
}
// Got the event handle...
g_hTAPIEvent = lineInitializeExParams.Handles.hEvent;
// Start event monitoring...
hEventThread = CreateThread(NULL, 0, zTapiEventThread, NULL, 0, 0);
if(hEventThread == NULL) // Bummer
{
printf("Win32 Error %d on CreateThread (event thread).\r\n",
GetLastError());
g_bTerminate = TRUE;
WaitForSingleObject(hEventThread, INFINITE);
CloseHandle(hEventThread);
CloseHandle(g_hEvReply);
lineShutdown(g_hLineApp);
return -1;
}
if(! g_bForce) // Select the device that supports specified media modes.
g_dwDevId = SelectLine(g_hLineApp, g_dwTAPIVer, g_dwNumDevs,
g_dwMediaMode);
if(g_dwDevId == 0xffffffff)
{
printf("No suitable line found.\r\n");
g_bTerminate = TRUE;
WaitForSingleObject(hEventThread, INFINITE);
CloseHandle(hEventThread);
CloseHandle(g_hEvReply);
lineShutdown(g_hLineApp);
ExitProcess(0);
}
// Open the line...
lRes = lineOpen(g_hLineApp, g_dwDevId, &g_hLine, 0x00020000, 0x00000000, 1,
LINECALLPRIVILEGE_OWNER, g_dwMediaMode, NULL);
if(lRes != 0)
{ // Doh!
FormatLineError(lRes, strText);
printf("TAPI Error: %s on lineOpen.\r\n", strText);
g_bTerminate = TRUE;
WaitForSingleObject(hEventThread, INFINITE);
CloseHandle(hEventThread);
CloseHandle(g_hEvReply);
return -1;
}
// We want to be notified for everything
lRes = lineSetStatusMessages(g_hLine, 0x1ffffff, 0);
if(lRes != 0)
{
FormatLineError(lRes, strText);
printf("TAPI Error: %s on lineOpen.\r\n", strText);
}
if(g_bIncoming)
{
printf("Waiting for incoming call.\r\n");
while (1)
{
// Now we wait for notification...
WaitForSingleObject(g_hEvReply, INFINITE);
if(g_dwTAPIMsg == LINECALLSTATE_OFFERING)
{
printf("Answering.\r\n");
lRes = lineAnswer(g_hCall, NULL, 0);
if(lRes != 0)
{ // Doh!
FormatLineError(lRes, strText);
printf("TAPI Error: %s on lineMakeCall.\r\n", strText);
EndSession();
}
break;
}
}
}
else
{
if(g_bTranslateDialog)
{
lRes = lineTranslateDialog(g_hLineApp, g_dwDevId, g_dwTAPIVer, NULL,
strAddress);
if(lRes != 0)
{
FormatLineError(lRes, strText);
printf("TAPI Error: %s on lineTranslateDialog.\r\n", strText);
}
}
if(g_bTranslate)
{
printf("Performing translation\r\nSource: %s\r\n", strAddress);
lpTranslateOutput = (LINETRANSLATEOUTPUT*)malloc(
sizeof(LINETRANSLATEOUTPUT)+1000);
lpTranslateOutput->dwTotalSize = sizeof(LINETRANSLATEOUTPUT)+1000;
while (1)
{
lRes = lineTranslateAddress(g_hLineApp, g_dwDevId, g_dwTAPIVer,
strAddress, 0, 0, lpTranslateOutput);
if(lRes != 0)
{
FormatLineError(lRes, strText);
printf("TAPI Error: %s on lineTranslateAddress.\r\n",
strText);
}
if(lpTranslateOutput->dwTotalSize <
lpTranslateOutput->dwNeededSize)
lpTranslateOutput = (LINETRANSLATEOUTPUT *)realloc(
lpTranslateOutput, lpTranslateOutput->dwNeededSize);
else break;
}
strcpy(strAddress,
(LPSTR)((DWORD)lpTranslateOutput+
(DWORD)lpTranslateOutput->dwDialableStringOffset));
printf("Results: %s\r\n",
(LPSTR)((DWORD)lpTranslateOutput+
(DWORD)lpTranslateOutput->dwDisplayableStringOffset));
}
printf("Calling %s\r\n", strAddress);
lRes = lineMakeCall(g_hLine, &g_hCall, strAddress, 0, NULL);
if(lRes != 0)
{ // Doh!
FormatLineError(lRes, strText);
printf("TAPI Error: %s on lineMakeCall.\r\n", strText);
}
}
// All done. Wait for the event thread to finish.
WaitForSingleObject(hEventThread, INFINITE);
CloseHandle(hEventThread);
if(!g_bQuit)
{
EndSession();
}
}
BOOL WINAPI zControlHandlerRoutine (DWORD dwCtrlType)
{
// Got an control handler event
switch (dwCtrlType)
{
case CTRL_C_EVENT: // Handle any event that will cause
case CTRL_BREAK_EVENT: // a termination of our program
case CTRL_CLOSE_EVENT:
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT:
{
g_bQuit = TRUE;
EndSession();
break;
}
default:
break;
}
return(FALSE);
} // End of zControlHandlerRoutine
// SelectLine function returns the ID of last line device that supports requested
// MediaMode. If not found, It will return 0xFFFFFFFF
DWORD SelectLine(
HLINEAPP hLineApp, // Valid LineApp Handle
DWORD dwTAPIVersion, // TAPI Version to be used.
DWORD dwNumDevs, // Number of line devices
DWORD dwMediaMode) // MediaMode to look for.
{
DWORD dwResult = 0xFFFFFFFF;
DWORD dwAPIVersion;
LINEEXTENSIONID ExtentionId;
LINEDEVCAPS *lpDevCaps;
DWORD i;
LONG lRes;
BOOL bDone;
for(i = 0 ; i < dwNumDevs ; i++)
{
lRes = lineNegotiateAPIVersion(hLineApp, i, dwTAPIVersion, dwTAPIVersion,
&dwAPIVersion, &ExtentionId);
if(lRes != 0) //Oops!
{
FormatLineError(lRes, strText);
printf("TAPI Error: %s on lineNegotiateAPIVersion.\r\n", strText);
}
else
{
// Allocate a little extra memory...
lpDevCaps = (LINEDEVCAPS *)malloc(sizeof(LINEDEVCAPS)+1000);
// No luck allocating memory...
if(lpDevCaps == NULL) return dwResult;
memset(lpDevCaps, 0, sizeof(LINEDEVCAPS)+1000);
lpDevCaps->dwTotalSize = sizeof(LINEDEVCAPS)+1000;
bDone = FALSE;
do // Go around and keep trying until there's enough memory
{
lRes = lineGetDevCaps(hLineApp, i, dwAPIVersion, 0, lpDevCaps);
if(lRes != 0) //Oops!
{
FormatLineError(lRes, strText);
printf("TAPI Error: %s on lineGetDevCaps.\r\n", strText);
free(lpDevCaps);
break;
}
if(lpDevCaps->dwNeededSize > lpDevCaps->dwTotalSize)
// Reallocate for dwNeededSize
lpDevCaps = (LINEDEVCAPS *)realloc(lpDevCaps,
lpDevCaps->dwNeededSize);
else bDone = TRUE;
}
while (!bDone);
printf("Line %d is %s.\r\n", i,
(LPBYTE)((DWORD)lpDevCaps+(DWORD)lpDevCaps->dwLineNameOffset));
if(lpDevCaps->dwMediaModes & dwMediaMode)
{
dwResult = i;
printf("Line device %d selected.\r\n", i);
}
else printf("Line device %d rejected.\r\n", i);
free(lpDevCaps);
}
}
return dwResult;
}
void EndSession()
{
LINECALLSTATUS CallStatus;
LONG lRes;
if(g_hCall != NULL) // Call might be in progress...
{
memset(&CallStatus, 0, sizeof(LINECALLSTATUS));
lRes = lineGetCallStatus(g_hCall, &CallStatus);
// Technically, lineGetCallStatus returns more info than
// there is in the structure. Since we don't care about it,
// We just go on our merry way
if(lRes == 0) // If it didn't fail, there's at least a call that needs
// to be droped.
{
printf("Call is still in progress. Droping it.\r\n");
lineDrop(g_hCall, NULL, 0);
}
}
printf("Closing line. (Ctrl Handler)\r\n");
lRes = lineClose(g_hLine);
if(lRes != 0)
{
FormatLineError(lRes, strText);
printf("TAPI Error: %s on lineClose.\r\n", strText);
}
g_bTerminate = TRUE; // Tell the event thread that it's time to go...
// Wait for it to comit suicide..
WaitForSingleObject(hEventThread, INFINITE);
CloseHandle(hEventThread);
printf("Shutting down TAPI.\r\n");
lRes = lineShutdown(g_hLineApp);
if(lRes != 0)
{
FormatLineError(lRes, strText);
printf("TAPI Error: %s on lineShutdown.\r\n", strText);
}
ExitProcess(0);
}
CallProc.c
// MODULE: CallProc.c
// PURPOSE: Demonstrates basic TAPI programming model
#include <stdio.h>
#define TAPI_CURRENT_VERSION 0x00020000
#include <tapi.h>
#include "TapiInfo.h"
extern HLINEAPP g_hLineApp;
extern CHAR strAddress[512];
extern CHAR strText[512], strText2[512];
extern BOOL bQuit_A;
extern BOOL g_bTerminate;
extern HANDLE g_hTAPI;
extern HANDLE g_hTAPIEvent;
extern HCALL g_hCall;
extern DWORD g_dwTAPIMsg;
extern HANDLE g_hEvReply; // Used for Asynch Reply
DWORD WINAPI zTapiEventThread (LPVOID lpThreadParam)
{
LONG lRes = 0;
LINECALLINFO *lpCallInfo;
while (TRUE)
{
DWORD dwStatus;
if (g_bTerminate)
{ // Someone told me I should kill myself...
fprintf(stderr, "\nTerminating message thread...\r\n");
return (0);
}
// Get a TAPI event if available, wait for 10ms just enough to give up
// quontum
dwStatus = WaitForSingleObject(g_hTAPIEvent, 10);
if (dwStatus == WAIT_OBJECT_0)
{
LINEMESSAGE lm;
//TAPI's got something to tell us...
if ((lRes = lineGetMessage(g_hLineApp, &lm, 0)) != 0)
{
FormatLineError(lRes, strText);
printf("TAPI Error: %s on lineGetMessage.\r\n", strText);
return(lRes);
}
FormatLineCallback(strText, (DWORD)lm.hDevice, lm.dwMessageID,
lm.dwCallbackInstance, lm.dwParam1, lm.dwParam2,
lm.dwParam3);
printf("TAPI Message: %s.\r\n", strText);
// Process message
switch (lm.dwMessageID)
{
case LINE_REPLY: // Sent after lineMakeCall or lineDrop
{
break;
}
case LINE_CALLSTATE: // Sent after change of call state
{
switch (lm.dwParam1)
{
//Incoming call is offering.
case LINECALLSTATE_OFFERING:
{
// Get the call handle
g_hCall = (HCALL)lm.hDevice;
printf("Signaling Event. \r\n");
g_dwTAPIMsg = LINECALLSTATE_OFFERING;
SetEvent(g_hEvReply);
break;
}
case LINECALLSTATE_IDLE:
{
printf("Droping call.\r\n");
lineDrop(g_hCall, NULL, 0);
g_hCall = NULL;
printf("Terminating event thread.\r\n");
return 0;
break;
}
case LINECALLSTATE_CONNECTED:
{
if(lm.dwCallbackInstance == 1)
{
g_dwTAPIMsg = LINECALLSTATE_CONNECTED;
SetEvent(g_hEvReply);
}
break;
}
case LINECALLSTATE_DISCONNECTED:
{
// We got disconnected, so drop the call
lineDrop((HCALL) lm.hDevice, NULL, 0);
break;
}
default:
break;
}
break;
}
case LINE_CALLINFO: // Call Info is available
{
if(lm.dwParam1 == LINECALLINFOSTATE_CALLID)
{ //Caller ID became available.
lpCallInfo = (LINECALLINFO *)malloc
(sizeof(LINECALLINFO)+1000);
memset(lpCallInfo, 0, sizeof(LINECALLINFO)+1000);
lpCallInfo->dwTotalSize = sizeof(LINECALLINFO)+1000;
while (1)
{
lineGetCallInfo(g_hCall, lpCallInfo);
if (lpCallInfo->dwTotalSize <
lpCallInfo->dwNeededSize)
lpCallInfo = (LINECALLINFO *)realloc(
lpCallInfo, lpCallInfo->dwNeededSize);
else break;
}
printf("Caller is %s : %s\r\n",
(LPSTR)((DWORD)lpCallInfo+(DWORD)lpCallInfo->dwCallerIDOffset),
(LPSTR)((DWORD)lpCallInfo+(DWORD)lpCallInfo->dwCallerIDNameOffset));
}
break;
}
default:
break;
}
}
else if (dwStatus == WAIT_TIMEOUT)
{ // WaitForSingleObject timed out. So go back and wait again!
continue;
}
else
{ // Something else strange happened!!
return(GetLastError());
}
}
return (lRes);
}