Tải bản đầy đủ (.pdf) (56 trang)

Network Programming in .NET With C# and Visual Basic .NET phần 8 doc

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (593.97 KB, 56 trang )

13.4 Physical network tapping 373
Chapter 13
The InterfaceStatistics class, as returned by GetInterfaceStatis-
tics
, is described in Table 13.16 (all properties return int64 unless other-
wise specified).
Mtu
Determines the maximum transmission unit of the
interface. Returns
int64.
Name
Gets a name for the interface. Returns string.
OperationalStatus
Gets the operational status of the interface.
Returns
OperationalStatus.
Type
Determines the interface hardware. Returns
InterfaceType (e.g., modem, ISDN, ADSL,
Ethernet, etc.).
Table 13.16 Significant members of the
InterfaceStatistics
class .
Method or Property Purpose
BytesReceived
Gets the number of bytes received
BytesSent
Gets the number of bytes sent
IncomingPacketsDiscarded
Gets the number of incoming packets
discarded


IncomingPacketsWithErrors
Gets the number of incoming packets
with errors
IncomingUnknownProtocolPackets
Gets the number of incoming
unknown protocol packets
NonUnicastPacketsReceived
Gets the number of non-Unicast
packets received
NonUnicastPacketsSent
Gets the number of non-Unicast
packets sent
OutgoingPacketsDiscarded
Gets the number of outgoing packets
discarded
OutgoingPacketsWithErrors
Gets the number of outgoing packets
with errors
OutputQueueLength
Gets the number of output queue
length
Table 13.15 Significant members of the
NetworkInterface
class (continued).
Method or Property Purpose
374 13.4 Physical network tapping
The IPAddressInformation class, as returned by GetIPAddressInfor-
mation
, is described in Table 13.17.
The IPv4Properties class, as returned by GetIPv4Properties, is

described in Table 13.18. These properties may be alternatively ascertained
on an adapter-by-adapter basis through the
GetAdaptersInfo Windows IP
Helper API function.
Speed
Gets the speed of the interface
UnicastPacketsReceived
Gets the number of Unicast packets
received
UnicastPacketsSent
Gets the number of Unicast packets
sent
Table 13.17 Significant members of the IPAddressInformation class.
Method or Property Purpose
Address
Gets the IP address
DnsEligible
Determines if the address is eligible for DNS
Transient
Determines if the address is transient
Table 13.18 Significant members of the
IPv4Properties
class .
Method or Property Purpose
GetDhcpServerAddresses
Retrieves the local DHCP server
addresses. Returns
IPAddress[].
GetGatewayAddresses
Retrieves the local gateway addresses.

Returns
IPAddress[].
GetWinsServersAddresses
Retrieves the local WINS servers
addresses. Returns
IPAddress[].
AutomaticPrivateAddressingActive
Determines if automatic private
addressing is active. Returns
Boolean.
Table 13.16 Significant members of the
InterfaceStatistics
class (continued).
Method or Property Purpose
13.4 Physical network tapping 375
Chapter 13
The TcpStatistics class, as returned by GetTcpStatistics, is
described in Table 13.19 (all properties return
int64 unless otherwise
stated). This is equivalent to calling the
GetTcpTable Windows IP Helper
API, or running
NETSTAT -p tcp -a from the command line.
AutomaticPrivateAddressingEnabled
Determines if automatic private
addressing is enabled. Returns
Boolean.
DhcpEnabled
Determines if DHCP is enabled.
Returns

Boolean.
RoutingEnabled
Determines if routing is enabled.
Returns
Boolean.
UsesWins
Determines if the computer uses
WINS. Returns
Boolean.
Table 13.19 Significant members of the
TcpStatistics
class .
Method or Property Purpose
ConnectionsAccepted
Determines the number of connections
accepted
ConnectionsInitiated
Determines the number of connections ini-
tiated
CumulativeConnections
Determines the number of cumulative con-
nections
CurrentConnections
Determines the number of current connec-
tions
ErrorsReceived
Determines the number of errors received
FailedConnectionAttempts
Determines the number of failed connection
attempts

MaximumConnections
Determines the maximum number of con-
nections
MaximumTransmissionTimeOut
Determines the maximum transmission
time out
Table 13.18 Significant members of the
IPv4Properties
class (continued).
Method or Property Purpose
376 13.5 Conclusion
The UdpStatistics class, as returned by GetUdpStatistics, is
described in Table 13.20 (all properties return
int64 unless otherwise
stated). This is equivalent to the
GetUdpStatistics Windows IP Helper
API function.
13.5 Conclusion
This chapter has shown three different means to tap nonintrusively into the
data that flows between computers. When local system traffic monitoring is
all that is required, then use of the pure .NET implementation is highly rec-
ommended, but for an enterprisewide implementation, then
PacketX com-
MinimumTransmissionTimeOut
Determines the minimum transmission
time out
ResetConnections
Determines the number of reset connections
SegmentsReceived
Determines the number of segments

received
SegmentsResent
Determines the number of segments resent
SegmentsSent
Determines the number of segments sent
SegmentsSentWithReset
Determines the number of segments sent
with reset
Table 13.20 Significant members of the
UdpStatistics
class.
Method or Property Purpose
DatagramsReceived
Determines the number of datagrams
received
DatagramsSent
Determines the number of datagrams sent
IncomingDatagramsDiscarded
Determines the number of incoming data-
grams discarded
IncomingDatagramsWithErrors
Determines the number of incoming data-
grams with errors
UdpListeners
Determines the number of active UDP lis-
teners
Table 13.19 Significant members of the
TcpStatistics
class (continued).
Method or Property Purpose

13.5 Conclusion 377
Chapter 13
bined with WinPCap is possibly the best option. Where financial constraints
prevent the use of a third-party commercial component, then
rvPacket will
probably point you in the right direction.
It would be impossible to document the format of every protocol that
could exist on a network, so only IP and TCP have been described in this
chapter. Interested readers are advised to consult the relevant RFC for infor-
mation on any specific protocol.
The next chapter deals with a form of telecommunication that has been
with us since the 1880s (i.e., the ubiquitous phone call); however, the chap-
ter is taken from a Computer Telephony Integration (CTI) developer’s per-
spective. Prepare to be introduced to the telephony API.
This page intentionally left blank

379

14

Adding Digital Telephony

14.1 Introduction

If you call any large cinema looking for times for films, you will undoubt-
edly be forwarded to an automated system that tells you when each film is
on. This system is made possible by digital telephony.
Computer Telephony Integration, or CTI, systems routinely cost
$10,000 and upward for enterprise-scale systems. The high cost is largely a
result of the misconceived idea that any telephony system requires loads of

specialized hardware and, thus, is out of reach for the humble developer. In
fact, you can put a simple system together using no more than a cheap
modem.
Any company that employs staff to answer phone calls can save money
by implementing a CTI system. Such a system can be used to route calls to
different departments automatically or to match a caller with customer ID
and associated purchase history.
This chapter is mainly devoted to one rather large code example built up
in three sections. The first section explains how to pick up and drop a call.
The following section explains how to detect key presses on the remote
handset, and the chapter concludes with a demonstration of how to play
back audio to the caller.

Note:

You will need a voice modem and phone line to test the following
examples. Access to a second phone (such as a mobile phone) is beneficial.

Calls made from any phone line may incur charges if the line is opened.

380

14.2

Basic telephony

14.2 Basic telephony

This chapter is focused on using the telephony API, but it is possible to
control a modem by issuing COM port commands. These will provide the

ability to dial telephone numbers and control the physical connection to
the phone line.
Even if your modem is internal or connected via USB, it will always be
mapped to a COM port. To discover the number of this COM port, you
can look at Start

→→
→→

Control Panel

→→
→→

phone and Modem options

→→
→→

Modems.
Under the Attached To tab will be the number of the COM port to which
the modem is attached.
Any command that is sent to this COM port will be interpreted by the
modem. A list of common AT commands shown in Table 14.1.
The responses the modem will send back shown in Table 14.2.

Table 14.1

AT commands.


AT Command Purpose

ATDT<

phone
number

><enter>

Dials the specified phone number using touch-tone dialing.
A comma in the number represents a pause, a W waits for a
second dial tone, and an @ waits for a five-second silence.

ATPT<

phone
number

><enter>

Dials the specified number using pulse dialing.

AT S0=<

number

>

Picks up the line after the specified number of rings.


+++

Drop line.

Table 14.2

Modem responses .

Response Meaning

O K

The command has executed without errors.

CONNECT

A connection to the remote phone has been made.

RING

An incoming call is detected.

NO CARRIER

No carrier signal has been detected (in GSM modems, this
can mean that there is no network).

ERROR

The command is not understood.


14.2

Basic telephony 381
Chapter 14

To implement a simple phone dialer in .NET, open Visual Studio
.NET and start a new Windows forms project. Right-click on the toolbox
and click Customize Toolbox (or Add/Remove Items in Visual Studio
.NET 2003). Click on the COM Controls tab, and then add the Microsoft
Communications control (

MSCOMM.OCX

). Drag this onto the form, and set
the

comport

property to the COM port number to which your modem is
connected. Add a button to the form, named

btnPhone

, click it, and add
this code:

C#

private void btnPhone_Click(object sender, System.EventArgs

e)
{
axMSComm1.PortOpen=true;
axMSComm1.Output="ATDT00353877519575\r\n";
}

VB.NET

Private Sub btnPhone_Click(ByVal sender As Object, _
ByVal e As System.EventArgs)
axMSComm1.PortOpen=True
axMSComm1.Output="ATDT00353877519575" + vbcrlf
End Sub

Note:

Running the code listed above may incur phone charges. It is advis-
able to change the phone number listed (00353877519575) to some other,

less expensive number.
Only one program can control each COM port at a time. This code will
fail if you are using the modem at the time. Several settings are associated
with a COM port; in this case, however, the default parameters (9600

NO DIAL TONE

There is no dial tone on the phone line.

BUSY


The remote end is too busy to take the call.

NO ANSWER

The remote end did not take the call.

Table 14.2

Modem responses (continued).

Response Meaning

382

14.3

Listening for incoming phone calls

baud, no parity, 8 data bits, 1 stop bit—or “9600,n,8,1”) are suitable for
communication with a modem. When the modem begins communication
at full speed, it will use a baud rate of 56 Kbps. This can be set using the

settings

property of the Microsoft communications control.

14.3 Listening for incoming phone calls

You can only do a certain number of things with a modem by sending com-
mands back and forth via a COM port. In order to develop serious applica-

tions, you have to use the Telephony Application Programming Interface
(TAPI). The TAPI libraries were designed with C++ in mind, not .NET, so
there is a steep learning curve. It is worthwhile to evaluate the various com-
mercial components available before tinkering with low-level TAPI code. A
few interesting Web sites, such as

www.shrinkwrapvb.com and www.pron-
exus.com,

contain a wealth of information on TAPI.
The overall architecture of TAPI is modeled on a collection of phone
lines that are connected to the computer. Not all of these phone lines are
physical connections. Some of them are software representations of phone
lines used for various internal processes. Each phone line may be opened or
closed, which is analogous to a phone being on or off hook. An open phone
line does not necessarily incur charges, unless a call is active.
When a phone line is open (off hook), it generates callbacks detailing
any event that has happened on the line, such as an incoming call. A call-
back is simply a function that is called asynchronously by an underlying
process.
When an incoming call is detected, the callback will contain a handle that
can be passed to a function that accepts the call. At this point, call charges are
applied to the line by the phone operator. Once the call is open, the modem
behaves like a rudimentary audio device, which can play and receive basic
audio. The line can still generate callbacks, such as a line dropping or the
detection of the remote user pressing digits on the phone’s keypad.
When the call is dropped, the line remains open, but the modem can no
longer function as an audio device. Phone charges will no longer be applied
when the call is dropped. Callbacks will be generated until the line is closed.


Note:

Warning: If a line is not closed before the application exits, the com-

puter may need to be restarted before the line can be reopened.

14.3

Listening for incoming phone calls 383
Chapter 14

Without further ado, here is the first example of TAPI. This sample
application will enable you to open and close a phone line, as well as detect
and accept incoming calls.
Open a new project in Visual Studio .NET. Name the form

frmTapi

,
and add to it three buttons:

btnStart

,

btnStop

, and

btnAccept


. You should
also include a textbox named

tbStatus

with

multiline

set to

true

.
Add a module named

TAPI

, and add the following code. In C#, you add
a class file instead of a module. Note that in C#, the class namespace is
assumed to be

tapi1_cs

, so substitute this for the name of your project.

C#

using System;

using System.Runtime.InteropServices;
namespace tapi1_cs
{
public class TAPI
{
public static int hCall;
public static int hTAPI;
public static int lNumLines;
public static int hLine;
public static linedevcaps lpLineDevCaps;
public static frmTAPI userInterface;
public const int TAPIVERSION = 0x10004;
public const short LINECALLPRIVILEGE_OWNER = 0x4;
public const short LINECALLPRIVILEGE_MONITOR = 0x2;
public const short LINEMEDIAMODE_AUTOMATEDVOICE = 0x8;
public const int LINE_LINEDEVSTATE = 8;
public const int LINE_CALLSTATE = 2;
public const int LINECALLSTATE_OFFERING = 0x2;
public const int LINECALLSTATE_ACCEPTED = 0x4;
public const int LINECALLSTATE_DISCONNECTED = 0x4000;
public struct linedialparams
{
int dwDialPause;
int dwDialSpeed;

384

14.3

Listening for incoming phone calls


int dwDigitDuration;
int dwWaitForDialtone;
}
public struct lineextensionid
{
int dwExtensionID0;
int dwExtensionID1;
int dwExtensionID2;
int dwExtensionID3;
}
public struct linedevcaps
{
public int dwTotalSize;
public int dwNeededSize;
public int dwUsedSize;
public int dwProviderInfoSize;
public int dwProviderInfoOffset;
public int dwSwitchInfoSize;
public int dwSwitchInfoOffset;
public int dwPermanentLineID;
public int dwLineNameSize;
public int dwLineNameOffset;
public int dwStringFormat;
public int dwAddressModes;
public int dwNumAddresses;
public int dwBearerModes;
public int dwMaxRate;
public int dwMediaModes;
public int dwGenerateToneModes;

public int dwGenerateToneMaxNumFreq;
public int dwGenerateDigitModes;
public int dwMonitorToneMaxNumFreq;
public int dwMonitorToneMaxNumEntries;
public int dwMonitorDigitModes;
public int dwGatherDigitsMinTimeout;
public int dwGatherDigitsMaxTimeout;
public int dwMedCtlDigitMaxListSize;
public int dwMedCtlMediaMaxListSize;
public int dwMedCtlToneMaxListSize;
public int dwMedCtlCallStateMaxListSize;

14.3

Listening for incoming phone calls 385
Chapter 14

public int dwDevCapFlags;
public int dwMaxNumActiveCalls;
public int dwAnswerMode;
public int dwRingModes;
public int dwLineStates;
public int dwUUIAcceptSize;
public int dwUUIAnswerSize;
public int dwUUIMakeCallSize;
public int dwUUIDropSize;
public int dwUUISendUserUserInfoSize;
public int dwUUICallInfoSize;
public linedialparams MinDialParams;
public linedialparams MaxDialParams;

public linedialparams DefaultDialParams;
public int dwNumTerminals;
public int dwTerminalCapsSize;
public int dwTerminalCapsOffset;
public int dwTerminalTextEntrySize;
public int dwTerminalTextSize;
public int dwTerminalTextOffset;
public int dwDevSpecificSize;
public int dwDevSpecificOffset;
public int dwLineFeatures; // TAPI v1.4
public string bBytes;
}
[DllImport("Tapi32.dll",SetLastError=true)]
public static extern int lineAnswer (int hCall, ref string
lpsUserUserInfo, int dwSize);

[DllImport("Tapi32.dll",SetLastError=true)]
public static extern int lineInitialize (ref int hTAPI,int
hInst, LineCallBackDelegate fnPtr ,
ref int szAppName, ref int dwNumLines);
[DllImport("Tapi32.dll",SetLastError=true)]
public static extern int lineNegotiateAPIVersion(int hTAPI,
int dwDeviceID, int dwAPILowVersion,
int dwAPIHighVersion,
ref int lpdwAPIVersion,
ref lineextensionid lpExtensionID);

386

14.3


Listening for incoming phone calls

[DllImport("Tapi32.dll",SetLastError=true)]
public static extern int lineOpen (int hLineApp, int
dwDeviceID, ref int lphLine, int dwAPIVersion,
int dwExtVersion, ref int dwCallbackInstance,
int dwPrivileges, int dwMediaModes,
ref int lpCallParams);
[DllImport("Tapi32.dll",SetLastError=true)]
public static extern int lineGetDevCaps (int hLineApp, int
dwDeviceID, int dwAPIVersion, int dwExtVersion,
ref linedevcaps lpLineDevCaps);
[DllImport("Tapi32.dll",SetLastError=true)]
public static extern int lineSetStatusMessages (int hLine,
int dwLineStates, int dwAddressStates);
[DllImport("Tapi32.dll",SetLastError=true)]
public static extern int lineDrop (int hCall, string
lpsUserUserInfo, int dwSize);
[DllImport("Tapi32.dll",SetLastError=true)]
public static extern int lineShutdown(int hLineApp);
}
}

VB.NET

Option Strict Off
Option Explicit On
Module VB_TAPI
Public LastTAPIEvent As Object

Public aditionalTAPIEventInfo As Object
Public hCall As Integer
Public hTAPI As Integer
Public lNumLines As Integer
Public hLine As Integer
Public lpLineDevCaps As linedevcaps
Public userInterface As frmTAPI
Public Const TAPIVERSION As Integer = &H10004
Public Const LINECALLPRIVILEGE_OWNER As Short = &H4S
Public Const LINECALLPRIVILEGE_MONITOR As Short = &H2S

14.3

Listening for incoming phone calls 387
Chapter 14

Public Const LINEMEDIAMODE_AUTOMATEDVOICE As Short = &H8S
Public Const LINE_LINEDEVSTATE = 8
Public Const LINE_CALLSTATE = 2
Public Const LINECALLSTATE_OFFERING = &H2
Public Const LINECALLSTATE_ACCEPTED = &H4
Public Const LINECALLSTATE_DISCONNECTED = &H4000
Structure linedialparams
Dim dwDialPause As Integer
Dim dwDialSpeed As Integer
Dim dwDigitDuration As Integer
Dim dwWaitForDialtone As Integer
End Structure
Structure lineextensionid
Dim dwExtensionID0 As Integer

Dim dwExtensionID1 As Integer
Dim dwExtensionID2 As Integer
Dim dwExtensionID3 As Integer
End Structure
Structure linedevcaps
Dim dwTotalSize As Integer
Dim dwNeededSize As Integer
Dim dwUsedSize As Integer
Dim dwProviderInfoSize As Integer
Dim dwProviderInfoOffset As Integer
Dim dwSwitchInfoSize As Integer
Dim dwSwitchInfoOffset As Integer
Dim dwPermanentLineID As Integer
Dim dwLineNameSize As Integer
Dim dwLineNameOffset As Integer
Dim dwStringFormat As Integer
Dim dwAddressModes As Integer
Dim dwNumAddresses As Integer
Dim dwBearerModes As Integer
Dim dwMaxRate As Integer
Dim dwMediaModes As Integer
Dim dwGenerateToneModes As Integer
Dim dwGenerateToneMaxNumFreq As Integer
Dim dwGenerateDigitModes As Integer

388

14.3

Listening for incoming phone calls


Dim dwMonitorToneMaxNumFreq As Integer
Dim dwMonitorToneMaxNumEntries As Integer
Dim dwMonitorDigitModes As Integer
Dim dwGatherDigitsMinTimeout As Integer
Dim dwGatherDigitsMaxTimeout As Integer
Dim dwMedCtlDigitMaxListSize As Integer
Dim dwMedCtlMediaMaxListSize As Integer
Dim dwMedCtlToneMaxListSize As Integer
Dim dwMedCtlCallStateMaxListSize As Integer
Dim dwDevCapFlags As Integer
Dim dwMaxNumActiveCalls As Integer
Dim dwAnswerMode As Integer
Dim dwRingModes As Integer
Dim dwLineStates As Integer
Dim dwUUIAcceptSize As Integer
Dim dwUUIAnswerSize As Integer
Dim dwUUIMakeCallSize As Integer
Dim dwUUIDropSize As Integer
Dim dwUUISendUserUserInfoSize As Integer
Dim dwUUICallInfoSize As Integer
Dim MinDialParams As linedialparams
Dim MaxDialParams As linedialparams
Dim DefaultDialParams As linedialparams
Dim dwNumTerminals As Integer
Dim dwTerminalCapsSize As Integer
Dim dwTerminalCapsOffset As Integer
Dim dwTerminalTextEntrySize As Integer
Dim dwTerminalTextSize As Integer
Dim dwTerminalTextOffset As Integer

Dim dwDevSpecificSize As Integer
Dim dwDevSpecificOffset As Integer
Dim dwLineFeatures As Integer ' TAPI v1.4
Dim bBytes As String
End Structure
Public Declare Function lineAnswer Lib "Tapi32" _
(ByVal hCall As Integer, ByRef lpsUserUserInfo _
As String, ByVal dwSize As Integer) As Integer
Public Declare Function lineInitialize Lib "Tapi32" _
(ByRef hTAPI As Integer, ByVal hInst As Integer, _

14.3

Listening for incoming phone calls 389
Chapter 14

ByVal fnPtr As LineCallBackDelegate, ByRef _
szAppName As Integer, ByRef dwNumLines As _
Integer) As Integer
Public Declare Function lineNegotiateAPIVersion Lib _
"Tapi32" (ByVal hTAPI As Integer, ByVal _
dwDeviceID As Integer, ByVal dwAPILowVersion _
As Integer, ByVal dwAPIHighVersion As Integer, _
ByRef lpdwAPIVersion As Integer, ByRef _
lpExtensionID As lineextensionid) _
As Integer
Public Declare Function lineOpen Lib "Tapi32" _
(ByVal hLineApp As Integer, ByVal dwDeviceID _
As Integer, ByRef lphLine As Integer, ByVal _
dwAPIVersion As Integer, ByVal dwExtVersion _

As Integer, ByRef dwCallbackInstance _
As Integer, ByVal dwPrivileges As Integer, _
ByVal dwMediaModes As Integer, ByRef _
lpCallParams As Integer) As Integer
Public Declare Function lineGetDevCaps Lib "Tapi32" _
(ByVal hLineApp As Integer, ByVal dwDeviceID _
As Integer, ByVal dwAPIVersion As Integer, _
ByVal dwExtVersion As Integer, ByRef _
lpLineDevCaps As linedevcaps) As Integer
Public Declare Function lineSetStatusMessages Lib _
"Tapi32" (ByVal hLine As Integer, ByVal _
dwLineStates As Integer, ByVal _
dwAddressStates As Integer) As Integer
Public Declare Function lineDrop Lib "Tapi32" _
(ByVal hCall As Integer, ByVal lpsUserUserInfo _
As String, ByVal dwSize As _
Integer) As Integer
Public Declare Function lineShutdown Lib "Tapi32" _
(ByVal hLineApp As Integer) As Integer
End Module

390

14.3

Listening for incoming phone calls

The code for the module may look daunting because these function def-
initions are ported directly from the


TAPI.H

C++ code from the Windows
platform SDK. It is not important to understand every parameter sent to
these API calls, but for the moment, Table 14.3 gives an overview of all the
API calls involved.
The core element of every TAPI application is the callback function

Lin-
eCallBack

. This is used to detect changes in the phone line, such as incom-
ing calls, dropped calls, or key presses on the remote telephone keypad.
Add the following code to the TAPI module:

Note:

The purpose of the

LineCallBackDelegate

delegate is to ensure that
the underlying telephony processes have something to call back to even
after the program closes. This prevents Windows from crashing if your

application does not shut down cleanly.

Table 14.3

Telephony API functions.


API Function Purpose

lineAnswer

Picks up the phone when an incoming call is
detected. This may incur phone charges.

lineInitialize

Indicates the name of the callback function to
TAPI, and retrieves the number of modems (vir-
tual and physical) installed on the system.

lineNegotiateAPIVersion

Determines whether a modem can support a speci-
fied version of TAPI (i.e., 1.4 in this case).

lineOpen

Indicates to TAPI that the callback should now
start receiving events for a specified modem.

lineGetDevCaps

Retrieves a host of technical information about a
specified modem (see the

lineDevCaps


structure
listed above).

lineSetStatusMessages

Indicates which, if any, events should be passed to
the callback.

lineDrop

Shuts down a modem temporarily, dropping any
active call.

lineShutdown

Shuts down a modem permanently, cleaning up
any resources.

14.3

Listening for incoming phone calls 391
Chapter 14
C#
public delegate int LineCallBackDelegate(int dwDevice, int
dwMessage, int dwInstance, int dwParam1, int dwParam2,
int dwParam3);
public static int LineCallBack(int dwDevice, int dwMessage,
int dwInstance, int dwParam1, int dwParam2, int dwParam3)
{

string msgEvent="";
msgEvent = Convert.ToString(dwMessage);
switch (dwMessage)
{
case LINE_CALLSTATE:
switch(dwParam1)
{
case LINECALLSTATE_OFFERING:
msgEvent = "Incomming call";
hCall = dwDevice;
break;
case LINECALLSTATE_ACCEPTED:
msgEvent = "Call accepted";
break;
case LINECALLSTATE_DISCONNECTED:
msgEvent = "Call disconnected";
break;
}
break;
case LINE_LINEDEVSTATE:
msgEvent = "Ringing";
break;
}
userInterface.showMessage("Event: " + msgEvent + " Data:"
+ dwParam1 + "\r\n");
return 1;
}
VB.NET
Delegate Function LineCallBackDelegate(ByVal dwDevice _
As Integer, ByVal dwMessage As Integer, ByVal _

dwInstance As Integer, ByVal dwParam1 As _
Integer, ByVal dwParam2 As Integer, ByVal dwParam3 _
392 14.3 Listening for incoming phone calls
As Integer) As Integer
Public Function LineCallBack(ByVal dwDevice As _
Integer, ByVal dwMessage As Integer, ByVal dwInstance _
As Integer, ByVal dwParam1 As Integer, ByVal dwParam2 _
As Integer, ByVal dwParam3 As Integer) As Integer
Dim msgEvent As String
msgEvent = CStr(dwMessage)
Select Case dwMessage
Case LINE_CALLSTATE
Select Case dwParam1
Case LINECALLSTATE_OFFERING
msgEvent = "Incomming call"
hCall = dwDevice
Case LINECALLSTATE_ACCEPTED
msgEvent = "Call accepted"
Case LINECALLSTATE_DISCONNECTED
msgEvent = "Call disconnected"
End Select
Case LINE_LINEDEVSTATE
msgEvent = "Ringing"
Case Else
msgEvent = dwMessage.ToString()
End Select
userInterface.tbStatus.Text += "Event: " & _
msgEvent & " Data:" & dwParam1 & vbCrLf
End Function
To explain the above code briefly: Once a line has been opened, every

event on that line will cause TAPI to make a call to this function. The
parameter
dwMessage indicates broadly what has happened on the line, and
dwParam1 defines the event more concisely.
The most important message type is
LINE_CALLSTATE. This indicates sig-
nificant state changes on the line. To determine the exact nature of the event, it
is necessary to drill-down and look at
dwParam1. When this parameter is set to
LINECALLSTATE_OFFERING (0x2), a call has just been detected, and the handle
to that call has been passed in
dwDevice. This handle can be later passed to
lineAnswer to pick up the phone. Other events such as
LINECALLSTATE_ACCEPTED (0x4) and LINECALLSTATE_DISCONNECTED (0x4000)
determine when a call becomes active and when the call is terminated.
14.3 Listening for incoming phone calls 393
Chapter 14
In some cases, the event can be assumed by looking at the dwMessage
parameter only. A LINE_LINEDEVSTATE (0x8) event is most likely to be the
ringing sound from an incoming call, but it could also be that the phone line
is out of service, indicated by a
dwParam1 of LINEDEVSTATE_OUTOFSERVICE
(0x80), or that the phone line is under maintenance, indicated by
LINEDEVSTATE_MAINTENANCE (0x100). Because this type of occurrence is rare,
and a computer program can hardly resolve the problem, the event can be
ignored.
At this point, the user interface should have already been prepared with
three buttons named
btnStart, btnStop, and btnAccept on the form. A
large textbox named

tbStatus is required. The multiline property should
be set to
true.
Click the Start button and enter the following code:
C#
private void btnStart_Click(object sender, System.EventArgs
e)
{
startModem();
}
VB.NET
Private Sub btnStart_Click(ByVal eventSender As _
System.Object, ByVal eventArgs As System.EventArgs) _
Handles btnStart.Click
startModem()
End Sub
Click the Stop button and enter the following code:
C#
private void btnStop_Click(object sender, System.EventArgs e)
{
stopModem();
}
VB.NET
Private Sub btnStop_Click(ByVal eventSender As _
System.Object, ByVal eventArgs As System.EventArgs) _
Handles btnStop.Click
394 14.3 Listening for incoming phone calls
stopModem()
End Sub
Click the Accept button and enter the following code:

C#
private void btnAcceptCall_Click(object sender,
System.EventArgs e)
{
acceptCall();
}
VB.NET
Private Sub btnAccept_Click(ByVal eventSender As _
System.Object, ByVal eventArgs As System.EventArgs) _
Handles btnAccept.Click
acceptCall()
End Sub
C# developers will also require the following function:
C#
public void showMessage(string message)
{
tbStatus.Text += message;
}
The reason for the extra function is that in VB.NET the TAPI module
exposes functions and types contained within it globally. In C#, a class is
used to hold the functions and types; therefore, any calls to these functions
must be through a reference to the class. Because the functions are static,
the only programmatic difference is the
TAPI prefix; however, the class
needs to have a reference to the form so that it can display text on the screen
when the TAPI callback occurs.
A computer may have more than one modem attached and will almost
certainly have a few virtual modems, which are used for various other inter-
nal purposes. Voice modems are much more useful when it comes to tele-
phony applications, but a data modem can still pick up and drop calls, even

if it cannot communicate with a human user once the line is active. This
limited functionality may be all that is required, however, if, for instance,
14.3 Listening for incoming phone calls 395
Chapter 14
the computer needs to do only one task in response to an incoming phone
call, such as connecting to the Internet or rebooting.
This code is designed to open the first line it can find that is capable of
detecting incoming calls. A more advanced system would select a voice
modem over a data modem by selecting a modem with the lowest accept-
able
lMediaMode. A voice modem can work with a media mode set to
LINEMEDIAMODE_INTERACTIVEVOICE (4 hex), whereas a data modem will
generally only use
LINEMEDIAMODE_DATAMODEM (10 hex). Hybrid modems do
exist, so the code below will scan all media modes from 1 to 100.
C#
public void startModem()
{
int nError=0;
TAPI.lineextensionid lpExtensionID = new
TAPI.lineextensionid();
int lUnused=0;
int lLineID=0;
int lNegVer=0;
long lPrivilege=0;
long lMediaMode=0;
IntPtr HInstance=(IntPtr)0;

lPrivilege = TAPI.LINECALLPRIVILEGE_OWNER +
TAPI.LINECALLPRIVILEGE_MONITOR;

lMediaMode = 4;

Module thisModule;
thisModule =
Assembly.GetExecutingAssembly().GetModules()[0];
HInstance = Marshal.GetHINSTANCE(thisModule);
TAPI.LineCallBackDelegate callback = new
TAPI.LineCallBackDelegate(TAPI.LineCallBack);
int Unused = 0;
nError = TAPI.lineInitialize(ref TAPI.hTAPI,
HInstance.ToInt32(),
callback, ref Unused, ref TAPI.lNumLines);

for (lLineID = 0;lLineID<TAPI.lNumLines;lLineID++)
{
nError = TAPI.lineNegotiateAPIVersion(TAPI.hTAPI,
396 14.3 Listening for incoming phone calls
lLineID,
TAPI.TAPIVERSION,TAPI.TAPIVERSION,
ref lNegVer, ref lpExtensionID);
do
{
nError = TAPI.lineOpen(TAPI.hTAPI, lLineID,
ref TAPI.hLine,
lNegVer, lUnused, ref lUnused,
(int)lPrivilege, (int)lMediaMode, ref lUnused);
lMediaMode ++;
} while (nError < 0 && lMediaMode < 100);
if (nError == 0) break;
}

TAPI.lpLineDevCaps.dwTotalSize =
Marshal.SizeOf(TAPI.lpLineDevCaps);
TAPI.lpLineDevCaps.bBytes = new
StringBuilder().Append(' ',2000).ToString();
TAPI.lineGetDevCaps(TAPI.hTAPI, lLineID, lNegVer, lUnused,
ref TAPI.lpLineDevCaps);
TAPI.lineSetStatusMessages(TAPI.hLine,
TAPI.lpLineDevCaps.dwLineStates, 0);
}
VB.NET
Public Sub startModem()
Dim nError As Integer
Dim lpExtensionID As lineextensionid
Dim lUnused As Integer
Dim lLineID As Integer
Dim i As Short
Dim lNegVer As Integer
Dim lPrivilege As Long
Dim lMediaMode As Long
lPrivilege = LINECALLPRIVILEGE_OWNER + _
LINECALLPRIVILEGE_MONITOR
lMediaMode = 4
nError = lineInitialize(hTAPI, _
Microsoft.VisualBasic.Compatibility.VB6.GetHInstance.ToInt32, _
AddressOf LineCallBack, 0, lNumLines)
14.3 Listening for incoming phone calls 397
Chapter 14
For lLineID = 0 To lNumLines
nError = lineNegotiateAPIVersion(hTAPI, _
lLineID,TAPIVERSION,TAPIVERSION, _

lNegVer, lpExtensionID)
Do
nError = lineOpen(hTAPI, lLineID, hLine, lNegVer, _
lUnused, lUnused, lPrivilege, lMediaMode, 0)
lMediaMode = lMediaMode + 1
Loop Until nError >= 0 Or lMediaMode = 100
If nError = 0 Then Exit For
Next
lpLineDevCaps.dwTotalSize = Len(lpLineDevCaps)
lpLineDevCaps.bBytes = Space(2000)
lineGetDevCaps(hTAPI, lLineID, lNegVer, lUnused, _
lpLineDevCaps)
lineSetStatusMessages(hLine, lpLineDevCaps.dwLineStates, 0)
End Sub
It is important to shut down the line after use because no other program
can use the modem until the line has been closed. If you close your program
before the line is closed, there may be problems reopening the line, and you
may have to restart your computer.
C#
public void stopModem()
{
int nError;
nError = TAPI.lineShutdown(TAPI.hTAPI);
}
VB.NET
Public Sub stopModem()
Dim nError As Integer
nError = lineShutdown(hTAPI)
End Sub
Whenever an incoming call is detected, the callback function will set a

public variable named
hCall to a reference number (a handle) that TAPI
recognizes. When this handle is passed to
lineAnswer, the phone line is

×