Figure 5   TAPI Support

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);
 }