📄 tapieventprocess.cpp
字号:
#include "StdAfx.h"
#include <tapi3.h>
#include "tapieventprocess.h"
const int Ver_Win2000 = 1;
const int Ver_WinXP = 2;
const int Timer_ID = 1;
const int Max_Rec_Time = 60000;
const DWORD ADDRESSLENGTH = 128;
///////////////////////////////////////////////////////////////////
// CallEventNotification::Event
//
// 1、这是ITCallEventNotification接口中的唯一的方法,当有事件被触发时,
// TAPI 3.0 就会调用这个函数。
// 2、这个函数里仅仅向用户界面线程(在本程序中可以理解为主对话框)发送一个消息,
// 这样做的目的是在 TAPI 的 回调(callback)线程中包含尽量少的处理过程
//
///////////////////////////////////////////////////////////////////
HRESULT
STDMETHODCALLTYPE
CTAPIEventNotification::Event(
TAPI_EVENT TapiEvent,
IDispatch * pEvent
)
{
//
// 调用AddRef,计数器加1.
//
pEvent->AddRef();
//
// 向 UI 线程发送消息
//
PostMessage(
m_hWnd,
WM_PRIVATETAPIEVENT,
(WPARAM) TapiEvent,
(LPARAM) pEvent
);
return S_OK;
}
/*
void CTAPIEventNotification::gg()
{
PostMessage(
m_hWnd,
WM_PRIVATETAPIEVENT,
0,
0
);
}
*/
//////////////////////////////////////////////////////////////////////////////////////////////////////
//
// calss CTapi 实现代码
//
// 说明:
// 1. 本类基于 TAPI3.1, 对 TAPI 的功能进行定制,满足特定需要
// 功能:
// 1. 对给定的电话号码呼叫
// 2. 当有电话呼入时,给对方播放本地事先指定的声音文件
// 3. 接受电话另一方的按键,分别对不同按键作出响应
// 4. 具有来录音功能
// 注意:
// 1. 对 CTapi 类初始化时需要传入当前 UI(用户界面)的句柄,通过这个句柄向 UI 线程发送消息,在 UI
// 线程中对 TAPI EVENT 进行响应。
// 2. 需要 class CTAPIEventNotification 辅助,完成对事件的注册
//////////////////////////////////////////////////////////////////////////////////////////////////////
CTapi::CTapi()
{
m_pTapi = NULL;
m_pAddress = NULL;
m_pCall = NULL;
m_AutoAnswer = false;
m_pPlayFileTerm = NULL;
m_pRecordFileTerm = NULL;
m_Advise = 0;
m_hWnd = 0;
m_dwMessages = 0;
m_Version = 0;
m_WelcomeFileName = "Welcome.wav";
m_RecFileName = "Message";
m_RecFileExt = "avi";
}
CTapi::~CTapi()
{
}
//////////////////////////////////////////////////////////////
// InitializeTapi
//
// TAPI 初始化
///////////////////////////////////////////////////////////////
HRESULT CTapi::InitializeTapi()
{
HRESULT hr;
//
// 初始化 TAPI object
//
hr = CoCreateInstance(
CLSID_TAPI,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITTAPI,
(LPVOID *)&m_pTapi
);
if ( FAILED(hr) )
{
AfxMessageBox("初始化 TAPI 失败!");
return hr;
}
//
// 调用 initialize.
// 本函数必须在其它所有 TAPI 函数被调用之前调用
//
hr = m_pTapi->Initialize();
if ( FAILED(hr) )
{
AfxMessageBox("初始化 TAPI 失败!");
m_pTapi->Release();
m_pTapi = NULL;
return hr;
}
//
// 创建我们自己的事件处理实例并注册之,参见 CTAPIEventNodtification 的定义
//
CTAPIEventNotification *pTAPIEventNotification = new CTAPIEventNotification;
//
//将 CTapi中的窗口句柄传给 CTAPIEventNotification,由 CTAPIEventNotification 向UI进程发送消息
//
pTAPIEventNotification->m_hWnd = m_hWnd;
hr = RegisterTapiEventInterface(pTAPIEventNotification);
//
// 这里已经不再需要 CALLBACK 对象,因为 TAPI 本身会在其内部创建并保留这个对象
//
pTAPIEventNotification->Release();
OSVERSIONINFOEX osvi;
BOOL bOsVersionInfoEx;
//
// 因为 TE_FILETERMINAL 只有在 Windows XP 下才支持,因此这里需要先判断操作系统的版本
// 如果在 Windows XP 以下注册 TE_FILETERMINAL 消息时,这是 TAPI 将无法接受到任何消息!
//
// 获取操作系统版本,使用到结构体 OSVERSIONINFOEX
// 如果调用失败,再尝试用 OSVERSIONINFO
//
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) )
{
// 如果使用 OSVERSIONINFOEX 失败, 尝试 OSVERSIONINFO.
osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) )
return FALSE;
}
if(osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
// 获得操作系统版本,Win2000以下忽略
if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 )
{
// Windows 2000
m_Version = Ver_Win2000;
}
if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 )
{
// Windows XP
m_Version = Ver_WinXP;
}
}
else
{
AfxMessageBox("您的操作系统版本太低,本系统至少要求在 Win2000 环境下");
return E_FAIL;
}
//
// 对我们所希望处理的事件设置事件进行事件过滤设置
//
if(m_Version == Ver_Win2000)
{
hr = m_pTapi->put_EventFilter(TE_CALLNOTIFICATION |
TE_CALLSTATE |
TE_CALLMEDIA |
TE_DIGITEVENT );
}
else if(m_Version == Ver_WinXP)
{
hr = m_pTapi->put_EventFilter(TE_CALLNOTIFICATION |
TE_CALLSTATE |
TE_CALLMEDIA |
TE_DIGITEVENT |
TE_FILETERMINAL); // TE_FILETERMINAL 只有在 WinXP 下才支持
}
if( FAILED(hr) )
{
AfxMessageBox("注册消息失败,系统无法接收到任何消息,但可以对其它号码呼叫");
}
//
// 查找我们需的的 Address (本程序中为 Modem)并在其上开始侦听
//
hr = ListenOnAddresses();
if ( FAILED(hr) )
{
//
//如果失败,释放所之前所审请的资源
//在 ListenOnAddresses 中已有错误信息输出,此处不需要再次输出 Errmsg
//
m_pTapi->Release();
m_pTapi = NULL;
return hr;
}
return S_OK;
}
///////////////////////////////////////////////////////////////
// ShutdownTapi
//
// 功能:关闭 Tapi
///////////////////////////////////////////////////////////////
void CTapi::ShutdownTapi()
{
//
// 如果仍有一个 Call 存在的话,则挂断并释放之
//
DisconnectTheCall();
ReleaseTheCall();
//
// 调用 Sleep 目的是为了留出一段时间让 TAPI 来挂断电话。
// 因为对话框虽然会立即消失,但 TAPI 进程仍有可能未结束
//
Sleep(5000);
//
// 释放主要的对象
//
if (NULL != m_pTapi)
{
m_pTapi->Shutdown();
m_pTapi->Release();
}
}
///////////////////////////////////////////////////////////////////////////
// RegisterTapiEventInterface
//
// 功能:注册 Tapi 事件接口
///////////////////////////////////////////////////////////////////////////
HRESULT CTapi::RegisterTapiEventInterface(CTAPIEventNotification *pTAPIEventNotification)
{
HRESULT hr = S_OK;
IConnectionPointContainer * pCPC;
IConnectionPoint * pCP;
//
// 获得连接点容器
//
hr = m_pTapi->QueryInterface(
IID_IConnectionPointContainer,
(void **)&pCPC
);
if ( FAILED(hr) )
{
return hr;
}
//
// 获得连接点
//
hr = pCPC->FindConnectionPoint(
IID_ITTAPIEventNotification,
&pCP
);
pCPC->Release();
if ( FAILED(hr) )
{
return hr;
}
// m_Advise 用于取消注册之用
hr = pCP->Advise(
pTAPIEventNotification,
&m_Advise
);
pCP->Release();
return hr;
}
//////////////////////////////////////////////////////////////
// AddressSupportsMediaType
//
// 功能:验证给定的 Address 是否支持 给定的 MediaType
// 返回:TRUE——支持;FALSE——不支持
//////////////////////////////////////////////////////////////
BOOL CTapi::AddressSupportsMediaType(
ITAddress * pAddress,
long lMediaType
)
{
VARIANT_BOOL bSupport = VARIANT_FALSE;
ITMediaSupport * pMediaSupport;
if ( SUCCEEDED( pAddress->QueryInterface( IID_ITMediaSupport,
(void **)&pMediaSupport ) ) )
{
//
// 通过 pMediaSupport 验证 pAddress 是否支持 lMediaType
//
pMediaSupport->QueryMediaType(
lMediaType,
&bSupport
);
pMediaSupport->Release();
}
return (bSupport == VARIANT_TRUE);
}
////////////////////////////////////////////////////////////////////////
// ListenOnAddresses
//
// 功能:找到 Modem 所在的 Address,判断是否支持 AudioIn and AudioOut,
// 如果都支持,则调用 ListenOnThisAddress 开始在此 Address 上侦听
////////////////////////////////////////////////////////////////////////
HRESULT CTapi::ListenOnAddresses()
{
HRESULT hr = S_OK;
IEnumAddress * pEnumAddress;
ITAddress * pAddress;
//
// 列举所有的 Address
//
hr = m_pTapi->EnumerateAddresses( &pEnumAddress );
if ( FAILED(hr) )
{
return hr;
}
BOOL bModem; //当前 Address 是否为 Modem 所有
BOOL bState; //当前 Address 状态是否启用
BOOL bListen; //当前 Address 上侦听是否成功
while ( TRUE )
{
//
// 获得下一个 Address
//
bModem = bState = bListen = FALSE;
hr = pEnumAddress->Next( 1, &pAddress, NULL );
if (S_OK != hr)
{
break;
}
BSTR bstrName; //当前 Address 名称
ADDRESS_STATE state; //当前 Address 状态
CString strName; //当前 Address 名称的 ASSIC 表示
//
// 获得当前 Address 名称,并判断是否为 Modem 所有,如果不是,结束本次循环,遍历一下 Address
//
pAddress->get_AddressName(&bstrName);
strName = bstrName;
SysFreeString(bstrName);
strName.MakeLower();
if( strName.Find("modem") == -1 )
continue;
//
//找到了modem
//
bModem = TRUE;
pAddress->get_State( &state);
if( state != AS_INSERVICE)
continue;
//
//上面找到的Modem目前处于启用状态
//
bState = TRUE;
//
// 当前 Address 是否支持声音
if ( AddressSupportsMediaType(pAddress, TAPIMEDIATYPE_AUDIO) )
{
//
// 如果支持声音,开始侦听
//
hr = ListenOnThisAddress( pAddress );
if( SUCCEEDED(hr) )
{
//
// 找到一个 Modem 设备,并且监听成功,退出函数
// 检测当前 Address 能力,是否支持监视所有种类形式的按键
//
ITAddressCapabilities *pAddressCaps = NULL;
long lType = 0;
hr = pAddress->QueryInterface(IID_ITAddressCapabilities, (void**)&pAddressCaps);
if ( SUCCEEDED(hr) )
{
hr = pAddressCaps->get_AddressCapability( AC_MONITORDIGITSUPPORT, &lType );
if( SUCCEEDED(hr) )
{
if( !(lType & LINEDIGITMODE_DTMF) ) //LINEDIGITMODE_DTMFEND
AfxMessageBox("您的 Modem 不支持音频拨号检测功能,无法获得音频拨号的电话机的按键");
else if( !(lType & LINEDIGITMODE_PULSE) )
AfxMessageBox("您的 Modem 不支持脉冲拨号检测功能,无法获得脉冲拨号的电话机的按键");
}
pAddressCaps->Release();
m_pAddress = pAddress;
pAddress->Release();
pEnumAddress->Release();
return S_OK;
}
}
}
pAddress->Release();
}
pEnumAddress->Release();
//
// 以下为对应的错误信息提示
//
if( !bModem )
{
AfxMessageBox("没有找到Modem,请检查您计算机那是否装有Modem");
return E_FAIL;
}
if( !bState )
{
AfxMessageBox("系统监测到您机器内有Modem,但当前没又启用,请手工启用Modem之后在运行本程序");
return E_FAIL;
}
if( !bListen )
{
AfxMessageBox("使用Modem监听失败,可能是您的 Modem 不支持语音,详情请于服务商联系");
return E_FAIL;
}
return S_OK;
}
///////////////////////////////////////////////////////////////////
// ListenOnThisAddress
//
// 功能:在调用这个函数之前,我们已经对所希望处理的事件进行了过滤设置,
// 这里通过调用 RegisterCallNotifications 告诉 TAPI 我们将要在这个
// Address 上触发那些已经设置的事件
//
///////////////////////////////////////////////////////////////////
HRESULT CTapi::ListenOnThisAddress(
ITAddress * pAddress
)
{
long lMediaTypes = TAPIMEDIATYPE_AUDIO;
if ( AddressSupportsMediaType(pAddress, TAPIMEDIATYPE_VIDEO) )
{
lMediaTypes |= TAPIMEDIATYPE_VIDEO;
}
HRESULT hr;
long lRegister;
hr = m_pTapi->RegisterCallNotifications(
pAddress,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -