[Windows Mobile Development and Hacking General] vangelderp:
I did this for a terminal emulator I wrote. I pass a string to these functions which is COM1: for serial, TAPI for modem or RAS for TCPIP RAS.

I have simply copied some of my code here, work through it as if lpszPortName = "TAPI" and you should make sense of it. Once you have the port handle hPort which is returned by the Connected response of the TAPI call back function, then you can read and write as if that were a serial/file handle.

Cheers

Paul

#include <windows.h>

#include <string.h>

#include <tapi.h>

#include "toolbox.h"

//Initilise the port either the TAPI or COMM port as requested by lpszPortName

BOOL PortInitialize (LPTSTR lpszPortName)

{

DWORD dwError;

BOOL RetValue;

HKEY hKey;

DWORD RegKeyDisp, RegKeyType, RegKeySize;

DWORD dwTapiNumDevs;

LONG lTapiReturn;

char PrintString[80];

TCHAR TempString[20];

char CharString[20];

LINEDEVCAPS TapiLineDevCaps;

static DWORD dwLocalAPIVersion;

LINEEXTENSIONID LineExtensionID;

LINECALLPARAMS LineCallParams;

static DWORD ChoosenDevice;

TCHAR DialNumber[30];

#ifdef _WIN32_WCE_EMULATION

if (wcscmp(lpszPortName, TEXT("TAPI")) != 0)

return TRUE;	//If running under emulator then unable to emulate

#endif	//the serial port, so in this 'test' mode just echo.

if (COMMPORTSHUT == TRUE)	//If this is True then we are shutting down the program

return TRUE;	//So don't try to re-open port as it gets stuck in an infinate loop

if (wcscmp(lpszPortName, TEXT("TAPI")) == 0)

{

#ifdef DEBUGVERSION

VTPrint("Entering (TAPI)PortInitilize\r\n" , 0);

#endif

//Retrive the telephone number from the registry

// Fill in the Modem Telephone number

wcscpy(DialNumber, TEXT("T"));

if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,

TEXT("Software\\PVG\\Terminal\\Settings"),

0, NULL, 0,0, NULL, &hKey, &RegKeyDisp) == ERROR_SUCCESS)

{

//Retrive the telephone number

RegKeyType = REG_SZ;

RegKeySize = sizeof(DialNumber);

RegQueryValueEx(hKey, TEXT("Telephone"),

NULL, &RegKeyType,

(PBYTE)DialNumber + sizeof(TCHAR),

&RegKeySize);

RegCloseKey(hKey);

}

//Confirm there is a telephone number

if (*(DialNumber + 1) == 0)

{

VTPrint("No Telephone Number\r\n", 0);

return FALSE;

}

// Use TAPI to open a DATAMODEM communication channel

if (ghLineApp == NULL)

{

#ifdef DEBUGVERSION

VTPrint("Tapi - LineInitialise\r\n",0);

#endif

memset(&TapiLineDevCaps, 0, sizeof(TapiLineDevCaps));

lTapiReturn = lineInitialize(&ghLineApp, hInst, TapiCallBackFunction, NULL, &dwTapiNumDevs);

if (lTapiReturn)

{

wsprintf (TempString,TEXT("%d"), lTapiReturn);

WideCharToMultiByte(CP_ACP, 0, TempString, -1, CharString, 20, NULL, NULL);

VTPrint("TAPI failed to initialise\n\rError Code = ",0);

VTPrint(CharString, 0);

VTPrint("\n\r",0);

TAPIShutdown();

return FALSE;

}

//Loop through devices to find one that can do data at 9600 baud (GSM)

for (ChoosenDevice=0 ; ChoosenDevice < dwTapiNumDevs ; ChoosenDevice++)

{

TapiLineDevCaps.dwTotalSize = sizeof(TapiLineDevCaps);

//Ask for at least TAPI Version 1.4

lTapiReturn = lineNegotiateAPIVersion(ghLineApp, ChoosenDevice, 0x00010004,

0x00010004, &dwLocalAPIVersion, &LineExtensionID);

#ifdef DEBUGVERSION

if (lTapiReturn)

{

wsprintf (TempString,TEXT("%d"), lTapiReturn);

WideCharToMultiByte(CP_ACP, 0, TempString, -1, CharString, 20, NULL, NULL);

VTPrint("TAPI failed to negotiate API Version\n\rError Code = ",0);

VTPrint(CharString, 0);

VTPrint("\n\r",0);

}

#endif

if (!(lTapiReturn))

{

lTapiReturn=lineGetDevCaps(ghLineApp, ChoosenDevice, dwLocalAPIVersion, 0, &TapiLineDevCaps);

#ifdef DEBUGVERSION

if (lTapiReturn)

{

wsprintf (TempString,TEXT("%d"), lTapiReturn);

WideCharToMultiByte(CP_ACP, 0, TempString, -1, CharString, 20, NULL, NULL);

VTPrint("TAPI failed to get device capibility\n\rError Code = ",0);

VTPrint(CharString, 0);

VTPrint("\n\r",0);

}

else

#endif

#ifndef DEBUGVERSION

if (!(lTapiReturn))

#endif

{

if ((TapiLineDevCaps.dwBearerModes & LINEBEARERMODE_VOICE) &&

(TapiLineDevCaps.dwMaxRate >= 9600) &&

(TapiLineDevCaps.dwMediaModes & LINEMEDIAMODE_DATAMODEM))

break;

}

}

}

if (!((TapiLineDevCaps.dwBearerModes & LINEBEARERMODE_VOICE) ||

(TapiLineDevCaps.dwMaxRate >= 9600) ||

(TapiLineDevCaps.dwMediaModes & LINEMEDIAMODE_DATAMODEM)))

{

VTPrint("Unable to find a modem device\r\n", 0);

TAPIShutdown();

lineShutdown(ghLineApp);

ghLineApp = NULL;

return FALSE;

}

//Now we have found a device capable of a dial up modem

strcpy(PrintString, "TAPI Initilised (");

wsprintf (TempString,TEXT("%d"), ChoosenDevice);

WideCharToMultiByte(CP_ACP, 0, TempString, -1, CharString, 20, NULL, NULL);

strcat(PrintString, CharString);

strcat(PrintString, "/");

wsprintf (TempString,TEXT("%d"), dwTapiNumDevs);

WideCharToMultiByte(CP_ACP, 0, TempString, -1, CharString, 3, NULL, NULL);

strcat(PrintString, CharString);

strcat(PrintString, ")\n\r");

VTPrint(PrintString, 0);

}

//Open the TAPI line device

if (ghLine == NULL)

{

#ifdef DEBUGVERSION

VTPrint("TAPI - Obtaining line handle\r\n", 0);

#endif

lTapiReturn = lineOpen(ghLineApp, ChoosenDevice, &ghLine, dwLocalAPIVersion, 0 , 0,

LINECALLPRIVILEGE_NONE, LINEMEDIAMODE_DATAMODEM, 0);

if (lTapiReturn)

{

wsprintf (TempString,TEXT("%d"), lTapiReturn);

WideCharToMultiByte(CP_ACP, 0, TempString, -1, CharString, 20, NULL, NULL);

VTPrint("TAPI failed to Open Line\n\rError Code = ",0);

VTPrint(CharString, 0);

VTPrint("\n\r",0);

TAPIShutdown();

return FALSE;

}

//Request specific notification messages

lTapiReturn = lineSetStatusMessages(ghLine, LINEDEVSTATE_RINGING | LINEDEVSTATE_CONNECTED |

LINEDEVSTATE_DISCONNECTED | LINEDEVSTATE_OUTOFSERVICE |

LINEDEVSTATE_MAINTENANCE | LINEDEVSTATE_CLOSE |

LINEDEVSTATE_REINIT, LINEADDRESSSTATE_OTHER);

if (lTapiReturn)

{

wsprintf (TempString,TEXT("%d"), lTapiReturn);

WideCharToMultiByte(CP_ACP, 0, TempString, -1, CharString, 20, NULL, NULL);

VTPrint("TAPI failed to Set Status Messages\n\rError Code = ",0);

VTPrint(CharString, 0);

VTPrint("\n\r",0);

TAPIShutdown();

return FALSE;

}

//Configure line device for a data modem

memset(&LineCallParams, 0, sizeof(LineCallParams));

LineCallParams.dwTotalSize = sizeof(LineCallParams);

LineCallParams.dwBearerMode = LINEBEARERMODE_VOICE;

LineCallParams.dwMediaMode = LINEMEDIAMODE_DATAMODEM;

//Expect the line to start out idle (we don't want to break into a current call)

LineCallParams.dwCallParamFlags = LINECALLPARAMFLAGS_IDLE;

//If multiple addresses on the line, use the first address

LineCallParams.dwAddressMode = LINEADDRESSMODE_ADDRESSID;

LineCallParams.dwAddressID = 0;

}

//Make the call

if (ghCall == NULL)

{

#ifdef DEBUGVERSION

VTPrint("TAPI Making the call\r\n", 0);

#endif

lTapiReturn = lineMakeCall(ghLine, &ghCall, DialNumber, 0, &LineCallParams);

if (lTapiReturn < 0)

{

wsprintf (TempString,TEXT("%d"), lTapiReturn);

WideCharToMultiByte(CP_ACP, 0, TempString, -1, CharString, 20, NULL, NULL);

VTPrint("TAPI failed to Make the Call\n\rError Code = ",0);

VTPrint(CharString, 0);

VTPrint("\n\r",0);

TAPIShutdown();

ghCall = NULL;

return FALSE;

}

}

VTPrint("Call Initiated\r\n",0);

return TRUE;

}

if (wcscmp(lpszPortName, TEXT("RAS")) != 0)

{

{

// Open the serial port.

hPort = CreateFile (lpszPortName, // Pointer to the name of the port

GENERIC_READ | GENERIC_WRITE,

// Access (read-write) mode

0, // Share mode

NULL, // Pointer to the security attribute

OPEN_EXISTING,// How to open the serial port

0, // Port attributes

NULL); // Handle to port with attribute

// to copy

// If it fails to open the port, return FALSE.

if ( hPort == INVALID_HANDLE_VALUE )

{

// Could not open the port.

COMMPORTSHUT=TRUE;

MessageBox (hMainWnd, TEXT("Unable to open the port"),

TEXT("Error"), MB_OK);

dwError = GetLastError ();

return FALSE;

}

}

RetValue=InitiliseCommHandle();

return RetValue;

}

return TRUE;

}

//Initilise the communication handle, this occurs once a communication channel has been

//opened, either direct to a serial port or via TAPI

BOOL InitiliseCommHandle(void)

{

DWORD dwError,

dwThreadID;

DCB PortDCB;

COMMTIMEOUTS CommTimeouts;

#ifdef DEBUGVERSION

char SendString[40];

sprintf(SendString, "Handle = %d\r\n", hPort);

VTPrint(SendString,0);

#endif

// Get the default port setting information.

PortDCB.DCBlength = sizeof (DCB);

GetCommState (hPort, &PortDCB);

// Change the DCB structure settings.

if (wcscmp(COMMPORTNAME, TEXT("TAPI")) == 0)

{

//TAPI (modem) Settings

PortDCB.BaudRate = 19200;	// Current baud

PortDCB.ByteSize = 8; // Number of bits/byte, 4-8

PortDCB.Parity = NOPARITY; // Parity odd,even,mark,space

}

else

{

//Standard Serial Port Settings

PortDCB.BaudRate = 1200; // Current baud

PortDCB.ByteSize = 7; // Number of bits/byte, 4-8

PortDCB.Parity = EVENPARITY; // Parity odd,even,mark,space

}

PortDCB.fBinary = TRUE; // Binary mode; no EOF check

PortDCB.fParity = TRUE; // Enable parity checking

PortDCB.fOutxCtsFlow = FALSE; // CTS output flow control

PortDCB.fRtsControl = RTS_CONTROL_HANDSHAKE;

PortDCB.fOutxDsrFlow = FALSE; // No DSR output flow control

PortDCB.fDtrControl = DTR_CONTROL_ENABLE; //DTR output ON

PortDCB.fDsrSensitivity = FALSE; // DSR sensitivity

PortDCB.fTXContinueOnXoff = TRUE; // XOFF continues Tx

PortDCB.fOutX = FALSE; // No XON/XOFF out flow control

PortDCB.fInX = FALSE; // No XON/XOFF in flow control

PortDCB.fErrorChar = FALSE; // Disable error replacement

PortDCB.fNull = FALSE; // Disable null stripping

PortDCB.fAbortOnError = FALSE; // Do not abort reads/writes on

// error

PortDCB.StopBits = ONESTOPBIT; // 0,1,2 = 1, 1.5, 2

// Configure the port according to the specifications of the DCB

// structure.

if (!SetCommState (hPort, &PortDCB))

{

MessageBox (hMainWnd, TEXT("Unable to configure the port"),

TEXT("Error"), MB_OK);

dwError = GetLastError ();

return FALSE;

}

// Retrieve the time-out parameters for all read and write operations

// on the port.

GetCommTimeouts (hPort, &CommTimeouts);

// Change the COMMTIMEOUTS structure settings.

CommTimeouts.ReadIntervalTimeout = MAXDWORD;

CommTimeouts.ReadTotalTimeoutMultiplier = 0;

CommTimeouts.ReadTotalTimeoutConstant = 0;

CommTimeouts.WriteTotalTimeoutMultiplier = 20;

CommTimeouts.WriteTotalTimeoutConstant = 1000;

// Set the time-out parameters for all read and write operations

// on the port.

if (!SetCommTimeouts (hPort, &CommTimeouts))

{

// Could set the timeouts.

MessageBox (hMainWnd, TEXT("Unable to set the port time-out parameters"),

TEXT("Error"), MB_OK);

dwError = GetLastError ();

return FALSE;

}

if (wcscmp(COMMPORTNAME, TEXT("TAPI")) != 0)

{

// Direct the port to perform extended functions SETDTR and SETRTS

// SETDTR: Sends the DTR (data-terminal-ready) signal.

// SETRTS: Sends the RTS (request-to-send) signal.

// EscapeCommFunction (hPort, SETDTR);

// EscapeCommFunction (hPort, SETRTS);

//Use the Swap Comms routine to set the serial port to the last open state

//This is a little messy to set the port first of all and then change some

//of the settings here

SwapComms(CurrentController);

}

// Create a read thread for reading data from the communication port.

if (hReadThread = CreateThread (NULL, 0, PortReadThread, 0, 0,

&dwThreadID))

{

CloseHandle (hReadThread);

}

else

{

// Could not create the read thread.

MessageBox (hMainWnd, TEXT("Unable to create the read thread"),

TEXT("Error"), MB_OK);

dwError = GetLastError ();

return FALSE;

}

return TRUE;

}

//TAPI sends status messages to this function.

void CALLBACK TapiCallBackFunction(DWORD dwdevice, DWORD dwMsg, DWORD dwCallbackInstance,

DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)

{

DWORD lTapiReturn, dwStructSize;

VARSTRING *pvs, *pvsOld;

#ifdef DEBUGVERSION

char SendString[40];

#endif

pvs = NULL;

dwStructSize = sizeof (VARSTRING);

switch(dwMsg)

{

case LINE_CALLSTATE:

switch(dwParam1)

{

case LINECALLSTATE_IDLE:

if (hPort != INVALID_HANDLE_VALUE)

{

VTPrint("Line Idle\n\r",0);

//Change MENU back to COMM mode

ControllerMenu(TRUE);

//Shutdown TAPI

hPort = INVALID_HANDLE_VALUE;

lineDeallocateCall(ghCall);

ghCall = NULL;

ghLine = NULL;

}

break;

case LINECALLSTATE_CONNECTED:

//If a handle is already present then don't re-open

//This needs doing as multiple connected events can occur on connection

if (hPort != INVALID_HANDLE_VALUE)

break;

//Spin round loop till the structure is big enough

do

{	pvsOld = pvs;

if (NULL == (pvs = realloc (pvs, dwStructSize)))

break;

pvs->dwTotalSize = dwStructSize;

if (lTapiReturn = lineGetID (NULL, 0, ghCall, LINECALLSELECT_CALL,

pvs, TEXT("comm/datamodem")))

break;

}

while ((dwStructSize = pvs->dwNeededSize) > pvs->dwTotalSize) ;

if (pvs == NULL)

{

if (pvsOld)

free (pvsOld);

VTPrint("Failed to allocate port handle memory\n\r", 0);

TAPIShutdown();

break;

}

if (lTapiReturn)

{

free(pvs);

VTPrint("Failed to obtain port handle\n\r", 0);

TAPIShutdown();

break;

}

hPort = * (HANDLE*) ((char*) pvs + pvs->dwStringOffset);

free(pvs);

//Change Menu to Modem Mode

ControllerMenu(FALSE);

VTPrint("Connected\n\r", 0);

InitiliseCommHandle();

break;

case LINECALLSTATE_DIALING:

//VTPrint("Dialling\n\r", 0);

break;

case LINECALLSTATE_PROCEEDING:

//VTPrint("Waiting for an answer\n\r", 0);

break;

case LINECALLSTATE_DISCONNECTED:

ControllerMenu(TRUE);

//Change Menu to COMM mode

switch (dwParam2)

{

case LINEDISCONNECTMODE_UNREACHABLE:

VTPrint("Unreachable\r\n",0);

hPort = INVALID_HANDLE_VALUE;

break;

case LINEDISCONNECTMODE_BUSY:

VTPrint("Line Busy\n\r", 0);

break;

}

TAPIShutdown();

}

break;

default:

break;

}

}

[/code]

BOOL TAPIShutdown()

//Do the TAPI Shutdown Process

{

static bShuttingDown = FALSE;

//If we are not initilised then Shutdown unnecessary

if (ghLineApp == NULL)

return TRUE;

//Prevent Shutdown re-entrancy problems

if (bShuttingDown)

return TRUE;

bShuttingDown=TRUE;

if (ghCall != NULL)

lineDrop(ghCall, NULL, 0);

bShuttingDown = FALSE;

return TRUE;

}