⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tapieventprocess.cpp

📁 具有语音自动应答功能的串口程序
💻 CPP
📖 第 1 页 / 共 3 页
字号:

#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 + -