?? 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接口中的唯一的方法,當有事件被觸發(fā)時,
// TAPI 3.0 就會調(diào)用這個函數(shù)。
// 2、這個函數(shù)里僅僅向用戶界面線程(在本程序中可以理解為主對話框)發(fā)送一個消息,
// 這樣做的目的是在 TAPI 的 回調(diào)(callback)線程中包含盡量少的處理過程
//
///////////////////////////////////////////////////////////////////
HRESULT
STDMETHODCALLTYPE
CTAPIEventNotification::Event(
TAPI_EVENT TapiEvent,
IDispatch * pEvent
)
{
//
// 調(diào)用AddRef,計數(shù)器加1.
//
pEvent->AddRef();
//
// 向 UI 線程發(fā)送消息
//
PostMessage(
m_hWnd,
WM_PRIVATETAPIEVENT,
(WPARAM) TapiEvent,
(LPARAM) pEvent
);
return S_OK;
}
/*
void CTAPIEventNotification::gg()
{
PostMessage(
m_hWnd,
WM_PRIVATETAPIEVENT,
0,
0
);
}
*/
//////////////////////////////////////////////////////////////////////////////////////////////////////
//
// calss CTapi 實現(xiàn)代碼
//
// 說明:
// 1. 本類基于 TAPI3.1, 對 TAPI 的功能進行定制,滿足特定需要
// 功能:
// 1. 對給定的電話號碼呼叫
// 2. 當有電話呼入時,給對方播放本地事先指定的聲音文件
// 3. 接受電話另一方的按鍵,分別對不同按鍵作出響應
// 4. 具有來錄音功能
// 注意:
// 1. 對 CTapi 類初始化時需要傳入當前 UI(用戶界面)的句柄,通過這個句柄向 UI 線程發(fā)送消息,在 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;
}
//
// 調(diào)用 initialize.
// 本函數(shù)必須在其它所有 TAPI 函數(shù)被調(diào)用之前調(diào)用
//
hr = m_pTapi->Initialize();
if ( FAILED(hr) )
{
AfxMessageBox("初始化 TAPI 失敗!");
m_pTapi->Release();
m_pTapi = NULL;
return hr;
}
//
// 創(chuàng)建我們自己的事件處理實例并注冊之,參見 CTAPIEventNodtification 的定義
//
CTAPIEventNotification *pTAPIEventNotification = new CTAPIEventNotification;
//
//將 CTapi中的窗口句柄傳給 CTAPIEventNotification,由 CTAPIEventNotification 向UI進程發(fā)送消息
//
pTAPIEventNotification->m_hWnd = m_hWnd;
hr = RegisterTapiEventInterface(pTAPIEventNotification);
//
// 這里已經(jīng)不再需要 CALLBACK 對象,因為 TAPI 本身會在其內(nèi)部創(chuàng)建并保留這個對象
//
pTAPIEventNotification->Release();
OSVERSIONINFOEX osvi;
BOOL bOsVersionInfoEx;
//
// 因為 TE_FILETERMINAL 只有在 Windows XP 下才支持,因此這里需要先判斷操作系統(tǒng)的版本
// 如果在 Windows XP 以下注冊 TE_FILETERMINAL 消息時,這是 TAPI 將無法接受到任何消息!
//
// 獲取操作系統(tǒng)版本,使用到結構體 OSVERSIONINFOEX
// 如果調(diào)用失敗,再嘗試用 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)
{
// 獲得操作系統(tǒng)版本,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("您的操作系統(tǒng)版本太低,本系統(tǒng)至少要求在 Win2000 環(huán)境下");
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("注冊消息失敗,系統(tǒng)無法接收到任何消息,但可以對其它號碼呼叫");
}
//
// 查找我們需的的 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();
//
// 調(diào)用 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,
// 如果都支持,則調(diào)用 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 狀態(tài)是否啟用
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 狀態(tài)
CString strName; //當前 Address 名稱的 ASSIC 表示
//
// 獲得當前 Address 名稱,并判斷是否為 Modem 所有,如果不是,結束本次循環(huán),遍歷一下 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目前處于啟用狀態(tài)
//
bState = TRUE;
//
// 當前 Address 是否支持聲音
if ( AddressSupportsMediaType(pAddress, TAPIMEDIATYPE_AUDIO) )
{
//
// 如果支持聲音,開始偵聽
//
hr = ListenOnThisAddress( pAddress );
if( SUCCEEDED(hr) )
{
//
// 找到一個 Modem 設備,并且監(jiān)聽成功,退出函數(shù)
// 檢測當前 Address 能力,是否支持監(jiān)視所有種類形式的按鍵
//
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("系統(tǒng)監(jiān)測到您機器內(nèi)有Modem,但當前沒又啟用,請手工啟用Modem之后在運行本程序");
return E_FAIL;
}
if( !bListen )
{
AfxMessageBox("使用Modem監(jiān)聽失敗,可能是您的 Modem 不支持語音,詳情請于服務商聯(lián)系");
return E_FAIL;
}
return S_OK;
}
///////////////////////////////////////////////////////////////////
// ListenOnThisAddress
//
// 功能:在調(diào)用這個函數(shù)之前,我們已經(jīng)對所希望處理的事件進行了過濾設置,
// 這里通過調(diào)用 RegisterCallNotifications 告訴 TAPI 我們將要在這個
// Address 上觸發(fā)那些已經(jīng)設置的事件
//
///////////////////////////////////////////////////////////////////
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 + -