📄 simpletapi.cpp
字号:
//
// SimpleTapi.cpp
//
// Source code from:
//
// Serial Communications: A C++ Developer's Guide, 2nd
// Edition by Mark Nelson, IDG Books, 1999
//
// Please see the book for information on usage.
//
// This file contains the complete implementation of
// the SimpleTapi class. SimpleTapi wraps up a limited
// subset of TAPI 1.4, using the SDK only for its modem
// control and call setup capabilities. This class
// contains several pure virtual functions that are needed
// for notification. Before you can use SimpleTapi, you will
// need to create a derived class that implements those
// functions. A simple example used in the book is
// MySimpleTapi, used in the Chapter 15 example.
//
#include "SimpleTapi.h"
//
// The static m_Trace object is shared by all extant
// SimpleTapi devices. It provices a way to display trace
// messages. Calling the Open() and Close() method of this
// object will open and close a console window that displays
// messages.
//
ConStream SimpleTapi::m_Trace;
//
// The SimpleTapi constructor has to do a couple of obvious
// things, such as clearing the internal handles and setting
// up other variables. One of the things it does that isn't
// quite as obvious is to enumerate a list of modems that
// can be used with this class. That list is stored in the
// m_Devices vector, which is suitable for insertion into
// a drop down list for selection by a user.
//
SimpleTapi::SimpleTapi()
{
//
// No line is active, no call is up
//
m_hLine = 0;
m_hCall = 0;
m_bCallHandleValid = false;
DWORD version = TAPI_CURRENT_VERSION;
//
// Note that Microsoft categorizes this as an obsolete
// function, but for a class like this that is only
// using TAPI for first part call control (setting up
// and tearing down our own calls) it is entirely
// adequate. It registers our app and gets an app handle
// in m_hLineApp, and lets TAPI know that we expect to
// see all notification returned via our static function
// SimpleTapi::CallBack()
//
DWORD device_count;
LONG result = lineInitialize( &m_hLineApp,
GetModuleHandle( NULL ),
Callback,
"TAPI Application",
&device_count );
m_Trace << "SimpleTapi::SimpleTapi() "
<< "lineInitialize() returned "
<< hex << result
<< dec << "\n";
//
// If error return, no devices, no app handle, not going
// to do anything useful.
//
if ( result != 0 ) {
device_count = 0;
m_hLineApp = NULL;
}
//
// We are going to add all data modems to the list of
// useful devices. That list is a vector that contains
// object of type TapiDevices, a little class used here
// to hold a device name and tapi id.
//
// One thing you see here in the call to lineGetDevCaps()
// is a recurrent them in Microsoft SDKs. Instead of
// just telling us how much space we will need to get the
// information, we're expected to make one call just
// to find out how much space is needed, then another
// to get the actual data. I buck the system in this
// case by grossly overestimating the space needed and
// attempting to do it all in one call.
//
m_Devices.resize( device_count );
int j = 0;
for ( int i = 0 ; i < (int) device_count ; i++ ) {
char temp[ 4096 ];
LINEDEVCAPS *dev_caps = (LINEDEVCAPS *) temp;
dev_caps->dwTotalSize = 4096;
result = lineGetDevCaps( m_hLineApp,
i,
version,
0,
dev_caps );
m_Trace << "SimpleTapi::SimpleTapi() "
<< "lineGetDevCaps("
<< i
<< ") returned "
<< hex
<< result
<< dec
<< "\n";
//
// To qualify as a useful device, I have to get
// a successful return from lineGetDevCaps(), the
// device has to be a data modem, and I have to have
// a valid device name, as denoted by
// dwLineNameOffset. If those conditions are met, I
// add the device to the list of devices. Note that
// the name is stored as a text string that may not
// be null terminated. Fortunately there is a string
// constructor that easily acommodates this.
//
if ( result == 0 &&
dev_caps->dwLineNameOffset != 0 &&
( dev_caps->dwMediaModes & LINEMEDIAMODE_DATAMODEM ) )
{
m_Devices[ j ].m_sName
= string( temp + dev_caps->dwLineNameOffset,
dev_caps->dwLineNameSize );
m_Trace << "Device name = "
<< m_Devices[ j ].m_sName
<< "\n";
m_Devices[ j++ ].m_iDeviceNumber = i;
}
}
m_Devices.resize( j );
m_ReplyAction = NOTHING;
m_dwPendingReplyCode = -1;
}
//
// The destructor shuts down TAPI by calling lineShutdown,
// passing the app handle. At that point the TAPI DLL may
// be unloaded and its internal resources given back to the
// system.
//
SimpleTapi::~SimpleTapi()
{
LONG result = -1;
if ( m_hLineApp != NULL )
result = lineShutdown( m_hLineApp );
m_Trace << "lineShutdown() result = "
<< hex
<< result
<< dec
<< "\n";
}
//
// Many things that we ask TAPI to do take long amounts
// of time to accomplish. When we kick off one of these
// asynchronous events, our call to TAPI normally returns
// immediately. Later, as TAPI makes progress on our
// request, it sends notification to us via this callback
// function.
//
// Unfortunately for C++ programmers, callback functions
// in Windows are C functions with no concept of a this
// pointer. This means that the callback function has to
// be a static C++ member function. The good news is that
// TAPI makes provision for the callback function to carry
// around an untyped pointer that the user provides. In our
// case, that pointer is to a SimpleTapi object, giving us
// immediate access to the SimpleTapi object that is actually
// the source or target of the action.
//
void PASCAL SimpleTapi::Callback( DWORD hDevice,
DWORD dwMsg,
DWORD dwCallbackInstance,
DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3 )
{
SimpleTapi *tapi = (SimpleTapi *) dwCallbackInstance;
//
// Like most Windows callbacks, we have to examine the
// message type to decide what to do. SimpleTapi only
// responds to two different message types: LINE_REPLY
// and LINE_CALLSTATE. LINE_REPLY sends responses to
// any of the three asynchronous commands: Make Call,
// Drop Call, and Answer. LINE_CALLSTATE keeps us
// up with the changes in a call state. It is where we
// get the two important events used in the Chapter
// 15 demo program, the connected and disconnected
// events.
//
switch ( dwMsg )
{
case LINE_REPLY :
m_Trace << "LINE_REPLY, request = "
<< hex
<< dwParam1
<< ", result = "
<< dwParam2
<< dec
<< "\n";
//
// If the user of this class made one of the three
// asynchronous function calls, it is presumably
// hanging around and waiting for an answer. We
// supply the answer by calling one of the three
// notification functions. These three notification
// functions aren't defined in the base class, they
// have to be implemented in a derived class that
// is designed to work with a specific application.
//
// Note that in order for this section of code to
// work, we need to only be waiting for one pending
// action at a time. The pending action is stored
// in the m_ReplyAction member of the object at the
// time the asynchronous request is made, using an
// enumerated type defined specifically in this
// class. We don't do anything with the result code
// that TAPI passes in here, we just pass it along
// to whoever is at the other end of the notification
// call.
//
if ( dwParam1 == tapi->m_dwPendingReplyCode ) {
switch ( tapi->m_ReplyAction ) {
case NOTHING:
break;
case HANDLE_MAKE_CALL_RESULT :
tapi->HandleMakeCallResult( dwParam2 );
break;
case HANDLE_DROP_RESULT :
tapi->HandleDropResult( dwParam2 );
break;
case HANDLE_ANSWER_RESULT :
tapi->HandleAnswerResult( dwParam2 );
break;
}
tapi->m_ReplyAction = NOTHING;
}
break;
//
// Things are really only interesting in TAPI while a
// call is in progress. When that is the case, we get
// periodic updates from TAPI telling us how things are
// going. As we progress through these actions, we
// generate notification function calls that allow the
// owner of the TAPI object to do the appropriate things.
// There is a general notification function that always
// gets called, then specific notification routines for
// the all important connected and disconnected events.
//
case LINE_CALLSTATE :
tapi->NotifyCallStateChange( dwParam1 );
//
// if dwParam3 is non-zero for the LINE_CALLSATE
// event, it is being used to inform us of our
// privilege state for the given line. For the type
// of call control we are performing here, we should
// always be the owner of the call.
//
switch ( dwParam3 ) {
case LINECALLPRIVILEGE_OWNER :
m_Trace << "SimpleTapi is now the owner of handle = "
<< hex << hDevice
<< dec << "\n";
break;
}
//
// When the LINE_CALLSTATE message is received,
// dwParam1 contains the new state of the call.
// A few of these states are particularly exciting,
// and lead to notification messages. Most of those
// cases should be pretty obvious. The TAPI docs
// show a few more states than are shown here, and
// a full-featured class might implement more
// notification messages to deal with them. The ones
// used here are adequate for a simple dialing and
// connecting app.
//
switch ( dwParam1 ) {
//
// A connected state can occur after either side
// decides to answer a call. The host app surely
// needs to be notified by this callback routine.
//
case LINECALLSTATE_CONNECTED:
tapi->ConnectedEvent();
break;
case LINECALLSTATE_IDLE:
case LINECALLSTATE_DISCONNECTED:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -