You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5843 lines
167 KiB
5843 lines
167 KiB
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (c) 1997 Active Voice Corporation. All Rights Reserved.
|
|
//
|
|
// Active Agent(r) and Unified Communications(tm) are trademarks of Active Voice Corporation.
|
|
//
|
|
// Other brand and product names used herein are trademarks of their respective owners.
|
|
//
|
|
// The entire program and user interface including the structure, sequence, selection,
|
|
// and arrangement of the dialog, the exclusively "yes" and "no" choices represented
|
|
// by "1" and "2," and each dialog message are protected by copyrights registered in
|
|
// the United States and by international treaties.
|
|
//
|
|
// Protected by one or more of the following United States patents: 5,070,526, 5,488,650,
|
|
// 5,434,906, 5,581,604, 5,533,102, 5,568,540, 5,625,676, 5,651,054.
|
|
//
|
|
// Active Voice Corporation
|
|
// Seattle, Washington
|
|
// USA
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// AVTapi.cpp : Implementation of CAVTapi
|
|
#include "stdafx.h"
|
|
#include "TapiDialer.h"
|
|
#include "AVTapi.h"
|
|
#include "AVTapiCall.h"
|
|
|
|
#include "ConfExp.h"
|
|
#include "ConfRoom.h"
|
|
#include "ConfDetails.h"
|
|
|
|
#include "ThreadAns.h"
|
|
#include "ThreadDial.h"
|
|
#include "ThreadDS.h"
|
|
|
|
#include "DlgCall.h"
|
|
#include "DlgJoin.h"
|
|
#ifdef _BAKEOFF
|
|
#include "DlgAddr.h"
|
|
#endif
|
|
|
|
#define VECT_CLS CAVTapi
|
|
#define VECT_IID IID_IAVTapiNotification
|
|
#define VECT_IPTR IAVTapiNotification
|
|
|
|
#define CLOSE_CONF(_P_, _CRIT_) \
|
|
_CRIT_.Lock(); \
|
|
if ( _P_ ) \
|
|
{ \
|
|
_P_->Release(); \
|
|
_P_ = NULL; \
|
|
} \
|
|
_CRIT_.Unlock();
|
|
|
|
#define BAIL_ON_DATACALL \
|
|
{ \
|
|
AVCallType nType = AV_VOICE_CALL; \
|
|
IAVTapiCall *pAVCall = FindAVTapiCall( lCallID ); \
|
|
if ( pAVCall ) \
|
|
{ \
|
|
pAVCall->get_nCallType(&nType); \
|
|
pAVCall->Release(); \
|
|
} \
|
|
if ( nType == AV_DATA_CALL ) return S_OK; \
|
|
}
|
|
|
|
#define BAIL_ON_CONFCALL \
|
|
{ \
|
|
AVCallType nType = AV_VOICE_CALL; \
|
|
DWORD dwAddressType = dwAddressType = 0; \
|
|
IAVTapiCall *pAVCall = FindAVTapiCall( lCallID ); \
|
|
if ( pAVCall ) \
|
|
{ \
|
|
pAVCall->get_nCallType(&nType); \
|
|
pAVCall->get_dwAddressType(&dwAddressType); \
|
|
pAVCall->Release(); \
|
|
} \
|
|
if ( (nType != AV_DATA_CALL) && (dwAddressType == LINEADDRESSTYPE_SDP) ) return S_OK; \
|
|
}
|
|
|
|
#define BAIL_ON_DATA_OR_CONFCALL \
|
|
{ \
|
|
AVCallType nType = AV_VOICE_CALL; \
|
|
DWORD dwAddressType = dwAddressType = 0; \
|
|
IAVTapiCall *pAVCall = FindAVTapiCall( lCallID ); \
|
|
if ( pAVCall ) \
|
|
{ \
|
|
pAVCall->get_nCallType(&nType); \
|
|
pAVCall->get_dwAddressType(&dwAddressType); \
|
|
pAVCall->Release(); \
|
|
} \
|
|
if ( (nType == AV_DATA_CALL) || (dwAddressType == LINEADDRESSTYPE_SDP) ) return S_OK; \
|
|
}
|
|
|
|
|
|
int CAVTapi::arAddressTypes[] = { LINEADDRESSTYPE_SDP,
|
|
LINEADDRESSTYPE_EMAILNAME,
|
|
LINEADDRESSTYPE_IPADDRESS,
|
|
LINEADDRESSTYPE_DOMAINNAME,
|
|
LINEADDRESSTYPE_PHONENUMBER };
|
|
|
|
#define NUM_CB_TERMINALS 3
|
|
#define AUDIO_CAPTURE 0
|
|
#define AUDIO_RENDER 1
|
|
#define VIDEO_CAPTURE 2
|
|
#define VIDEO_RENDER 3
|
|
|
|
#define MAX_CALLWINDOWS 4
|
|
|
|
#define _USE_DEFAULTSERVER
|
|
|
|
#define USB_NULLVOLUME (-1)
|
|
|
|
|
|
UINT AddressTypeToRegKey( DWORD dwAddressType, bool bPermanent )
|
|
{
|
|
if ( (dwAddressType & LINEADDRESSTYPE_SDP) != 0 )
|
|
return (bPermanent) ? IDN_REG_REDIAL_ADDRESS_CONF : IDN_REG_REDIAL_ADDRESS_CONF_ADDR;
|
|
else if ( (dwAddressType & LINEADDRESSTYPE_PHONENUMBER) != 0 )
|
|
return (bPermanent) ? IDN_REG_REDIAL_ADDRESS_POTS : IDN_REG_REDIAL_ADDRESS_POTS_ADDR;
|
|
|
|
return (bPermanent) ? IDN_REG_REDIAL_ADDRESS_INTERNET : IDN_REG_REDIAL_ADDRESS_INTERNET_ADDR;
|
|
}
|
|
|
|
DialerMediaType AddressToMediaType( long dwAddressType )
|
|
{
|
|
if ( (dwAddressType & LINEADDRESSTYPE_SDP) != 0 )
|
|
return DIALER_MEDIATYPE_CONFERENCE;
|
|
if ( (dwAddressType & LINEADDRESSTYPE_PHONENUMBER) != 0 )
|
|
return DIALER_MEDIATYPE_POTS;
|
|
|
|
return DIALER_MEDIATYPE_INTERNET;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CAVTapi
|
|
|
|
CAVTapi::CAVTapi()
|
|
{
|
|
m_pITTapi = NULL;
|
|
|
|
m_pIConfExplorer = NULL;
|
|
m_pIConfRoom = NULL;
|
|
m_pITapiNotification = NULL;
|
|
|
|
m_lShowCallDialog = 0;
|
|
m_lRefreshDS = 0;
|
|
|
|
m_bstrDefaultServer = NULL;
|
|
m_bAutoCloseCalls = false;
|
|
|
|
m_pUSBPhone = NULL;
|
|
m_pDlgCall = NULL;
|
|
m_bUSBOpened = FALSE;
|
|
m_bstrUSBCaptureTerm = NULL;
|
|
m_bstrUSBRenderTerm = NULL;
|
|
|
|
m_hEventDialerReg = NULL;
|
|
|
|
// Audio echo cancellation
|
|
m_bAEC = FALSE;
|
|
|
|
m_nUSBInVolume = USB_NULLVOLUME;
|
|
m_nUSBOutVolume = USB_NULLVOLUME;
|
|
}
|
|
|
|
void CAVTapi::FinalRelease()
|
|
{
|
|
ATLTRACE(_T(".enter.CAVTapi::FinalRelease().\n"));
|
|
SysFreeString( m_bstrDefaultServer );
|
|
|
|
ATLTRACE(_T(".exit.CAVTapi::FinalRelease().\n"));
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::Init(BSTR *pbstrOperation, BSTR *pbstrDetails, long *phr)
|
|
{
|
|
_ASSERT( pbstrOperation && pbstrDetails && phr );
|
|
ATLTRACE(_T(".enter.CAVTapi::Init().\n"));
|
|
|
|
HCURSOR hCurOld = SetCursor( LoadCursor(NULL, IDC_APPSTARTING) );
|
|
|
|
//
|
|
// Open the event object to detect when the
|
|
// Dialer was registered as client for the events
|
|
//
|
|
|
|
m_hEventDialerReg = CreateEvent( NULL,
|
|
TRUE,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
// Create the conference room object
|
|
m_critConfRoom.Lock();
|
|
if ( !m_pIConfRoom )
|
|
{
|
|
// No conference explorer object, create a new one
|
|
m_pIConfRoom = new CComObject<CConfRoom>;
|
|
if ( m_pIConfRoom )
|
|
m_pIConfRoom->AddRef();
|
|
}
|
|
m_critConfRoom.Unlock();
|
|
|
|
|
|
// Load registry settings
|
|
LoadRegistry();
|
|
|
|
CErrorInfo er;
|
|
er.set_Operation( IDS_ER_INIT_TAPI );
|
|
|
|
// Startup threads
|
|
er.set_Details( IDS_ER_CREATE_THREAD );
|
|
if ( !_Module.StartupThreads() )
|
|
{
|
|
SetCursor( hCurOld );
|
|
return er.set_hr(E_UNEXPECTED);
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// Create and initialize TAPI if not already done
|
|
if ( !m_pITTapi )
|
|
{
|
|
er.set_Details( IDS_ER_CREATE_TAPI_OBJECT );
|
|
hr = er.set_hr(CoCreateInstance( CLSID_TAPI,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_ITTAPI,
|
|
(void **) &m_pITTapi ));
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
er.set_Details( IDS_ER_INITIALIZE_TAPI );
|
|
if ( SUCCEEDED(hr = er.set_hr(m_pITTapi->Initialize())) )
|
|
{
|
|
// Register ourselves with the _Module object
|
|
_Module.SetAVTapi( this );
|
|
|
|
// Set the Event filter to only give us only the events we're interested in
|
|
m_pITTapi->put_EventFilter(TE_CALLNOTIFICATION | \
|
|
TE_CALLSTATE | \
|
|
TE_CALLMEDIA | \
|
|
TE_CALLINFOCHANGE | \
|
|
TE_REQUEST | \
|
|
TE_PRIVATE | \
|
|
TE_ADDRESS | \
|
|
TE_PHONEEVENT | \
|
|
TE_TAPIOBJECT);
|
|
|
|
// Listen for incoming calls
|
|
er.set_Details( IDS_ER_CREATE_TAPI_NOTIFICATION_OBJECT );
|
|
|
|
// $CRIT - enter
|
|
ITapiNotification *pNotify = new CComObject<CTapiNotification>;
|
|
if ( pNotify )
|
|
{
|
|
Lock();
|
|
m_pITapiNotification = pNotify;
|
|
m_pITapiNotification->AddRef();
|
|
Unlock();
|
|
|
|
hr = pNotify->Init( m_pITTapi, (long *) &er );
|
|
|
|
// Register for assisted telephony
|
|
m_pITTapi->RegisterRequestRecipient( 0, LINEREQUESTMODE_MAKECALL, TRUE);
|
|
|
|
// Publish user in ILS servers
|
|
RegisterUser( true, NULL );
|
|
}
|
|
else
|
|
{
|
|
// Couldn't create object
|
|
hr = er.set_hr( E_OUTOFMEMORY );
|
|
}
|
|
|
|
//
|
|
// Detect USB Phone
|
|
//
|
|
|
|
USBFindPhone( &m_pUSBPhone );
|
|
|
|
//
|
|
// Detect the audio echo cancellation setting
|
|
//
|
|
|
|
m_bAEC = AECGetRegistryValue();
|
|
}
|
|
|
|
// Failure!
|
|
if ( FAILED(hr) )
|
|
{
|
|
ATLTRACE(_T(".error.CAVTapi::Init() -- failed to initialize TAPI(0x%08lx).\n"), hr );
|
|
_Module.SetAVTapi( NULL );
|
|
RELEASE( m_pITTapi );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
_Module.ShutdownThreads();
|
|
|
|
// Extract error code information
|
|
er.Commit();
|
|
*pbstrOperation = SysAllocString( er.m_bstrOperation );
|
|
*pbstrDetails = SysAllocString( er.m_bstrDetails );
|
|
*phr = er.m_hr;
|
|
// Don't want to call the ErrorNotify callback
|
|
er.set_hr( S_OK );
|
|
}
|
|
|
|
RefreshDS();
|
|
|
|
ATLTRACE(_T(".exit.CAVTapi::Init(0x%08lx).\n"), hr);
|
|
SetCursor( hCurOld );
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::Term()
|
|
{
|
|
ATLTRACE(_T(".enter.CAVTapi::Term().\n"));
|
|
|
|
HCURSOR hCurOld = SetCursor( LoadCursor(NULL, IDC_APPSTARTING) );
|
|
HRESULT hr = S_OK;
|
|
|
|
SaveRegistry();
|
|
|
|
//
|
|
// Dialer registration event
|
|
//
|
|
if( m_hEventDialerReg)
|
|
{
|
|
CloseHandle( m_hEventDialerReg );
|
|
}
|
|
|
|
|
|
//Unregister the user
|
|
//FIXUP: The shutdown threads just waits for 5 seconds for thread to finish and then just exits.
|
|
//This will cause the app to hang. We really should KillThread the threads that are not
|
|
//returning.
|
|
//RegisterUser( false, NULL );
|
|
|
|
// Hide conference windows
|
|
CLOSE_CONF(m_pIConfExplorer, m_critConfExplorer );
|
|
CLOSE_CONF(m_pIConfRoom, m_critConfRoom );
|
|
|
|
|
|
RELEASE_CRITLIST(m_lstAVTapiCalls, m_critLstAVTapiCalls);
|
|
|
|
Lock();
|
|
if ( m_pITapiNotification ) m_pITapiNotification->Shutdown();
|
|
RELEASE( m_pITapiNotification );
|
|
Unlock();
|
|
|
|
//
|
|
// Release ITPhone, if exist one
|
|
//
|
|
m_critUSBPhone.Lock();
|
|
if( m_pUSBPhone )
|
|
{
|
|
m_pUSBPhone->Release();
|
|
m_pUSBPhone = NULL;
|
|
}
|
|
|
|
if( m_bstrUSBCaptureTerm )
|
|
{
|
|
SysFreeString( m_bstrUSBCaptureTerm );
|
|
m_bstrUSBCaptureTerm = NULL;
|
|
}
|
|
|
|
if( m_bstrUSBRenderTerm )
|
|
{
|
|
SysFreeString( m_bstrUSBRenderTerm );
|
|
m_bstrUSBRenderTerm = NULL;
|
|
}
|
|
|
|
m_critUSBPhone.Unlock();
|
|
|
|
// Shutdown threads
|
|
_Module.ShutdownThreads();
|
|
|
|
if ( m_pITTapi )
|
|
{
|
|
ATLTRACE(_T(".1.CAVTapi::Term() -- shutting down Telephony Services.\n"));
|
|
m_pITTapi->RegisterRequestRecipient( 0, LINEREQUESTMODE_MAKECALL, FALSE );
|
|
hr = m_pITTapi->Shutdown();
|
|
RELEASE( m_pITTapi );
|
|
}
|
|
|
|
// Unregister ourselves with the _Module object
|
|
_Module.SetAVTapi( NULL );
|
|
|
|
ATLTRACE(_T(".exit.CAVTapi::Term(0x%08lx).\n"), hr);
|
|
SetCursor( hCurOld );
|
|
return hr;
|
|
}
|
|
|
|
void CAVTapi::LoadRegistry()
|
|
{
|
|
USES_CONVERSION;
|
|
CRegKey regKey;
|
|
DWORD dwTemp;
|
|
TCHAR szText[255], szType[255], szServer[MAX_PATH + 100];
|
|
|
|
// Cached ILS server
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_DIALER_KEY, szText, ARRAYSIZE(szText) );
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_CONFSERV_DEFAULTSERVER, szType, ARRAYSIZE(szType) );
|
|
if ( regKey.Open(HKEY_CURRENT_USER, szText, KEY_READ) == ERROR_SUCCESS )
|
|
{
|
|
dwTemp = ARRAYSIZE(szServer);
|
|
regKey.QueryValue( szServer, szType, &dwTemp );
|
|
regKey.Close();
|
|
BSTR bstrTemp = NULL;
|
|
bstrTemp = SysAllocString( T2COLE(szServer) );
|
|
put_bstrDefaultServer( bstrTemp );
|
|
SysFreeString( bstrTemp );
|
|
}
|
|
|
|
// Automatically close call widows
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_AUTOCLOSECALLS, szType, ARRAYSIZE(szType) );
|
|
if ( regKey.Open(HKEY_CURRENT_USER, szText, KEY_READ) == ERROR_SUCCESS )
|
|
{
|
|
dwTemp = m_bAutoCloseCalls;
|
|
regKey.QueryValue( dwTemp, szType );
|
|
regKey.Close();
|
|
put_bAutoCloseCalls( (VARIANT_BOOL) (dwTemp != 0) );
|
|
}
|
|
|
|
// # of conference room windows
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_KEY, szText, ARRAYSIZE(szText) );
|
|
LoadString( _Module.GetResourceInstance(), AddressTypeToRegKey(LINEADDRESSTYPE_SDP, true), szType, ARRAYSIZE(szType) );
|
|
_tcscat( szText, _T("\\") );
|
|
_tcscat( szText, szType );
|
|
regKey.Open( HKEY_CURRENT_USER, szText, KEY_READ );
|
|
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_TERMINAL_MAX_VIDEO, szText, ARRAYSIZE(szText) );
|
|
IConfRoom *pConfRoom;
|
|
if ( SUCCEEDED(get_ConfRoom(&pConfRoom)) )
|
|
{
|
|
short nMax;
|
|
pConfRoom->get_nMaxTerms( &nMax );
|
|
|
|
dwTemp = nMax;
|
|
regKey.QueryValue( dwTemp, szText );
|
|
nMax = (short) min( MAX_VIDEO, max(1, dwTemp) );
|
|
|
|
pConfRoom->put_nMaxTerms( nMax );
|
|
pConfRoom->Release();
|
|
}
|
|
}
|
|
|
|
void CAVTapi::SaveRegistry()
|
|
{
|
|
USES_CONVERSION;
|
|
CRegKey regKey;
|
|
TCHAR szKey[255], szType[255];
|
|
|
|
BSTR bstrServer = NULL;
|
|
get_bstrDefaultServer( &bstrServer );
|
|
|
|
// Cached ILS server
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_DIALER_KEY, szKey, ARRAYSIZE(szKey) );
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_CONFSERV_DEFAULTSERVER, szType, ARRAYSIZE(szType) );
|
|
if ( regKey.Open(HKEY_CURRENT_USER, szKey, KEY_WRITE) == ERROR_SUCCESS )
|
|
{
|
|
if ( bstrServer )
|
|
regKey.SetValue( OLE2CT(bstrServer), szType );
|
|
else
|
|
regKey.DeleteValue( szType );
|
|
|
|
regKey.Close();
|
|
}
|
|
SysFreeString( bstrServer );
|
|
|
|
// Automatically close call widows
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_AUTOCLOSECALLS, szType, ARRAYSIZE(szType) );
|
|
if ( regKey.Open(HKEY_CURRENT_USER, szKey, KEY_WRITE) == ERROR_SUCCESS )
|
|
{
|
|
VARIANT_BOOL bAutoClose;
|
|
get_bAutoCloseCalls( &bAutoClose );
|
|
regKey.SetValue( bAutoClose, szType );
|
|
regKey.Close();
|
|
}
|
|
}
|
|
|
|
|
|
STDMETHODIMP CAVTapi::get_hWndParent(HWND * pVal)
|
|
{
|
|
*pVal = _Module.GetParentWnd();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::put_hWndParent(HWND newVal)
|
|
{
|
|
if ( !::IsWindow(newVal) ) return E_INVALIDARG;
|
|
|
|
_Module.SetParentWnd(newVal );
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::CreateCall(AVCreateCall *pInfo)
|
|
{
|
|
USES_CONVERSION;
|
|
ATLTRACE(_T(".enter.CAVTapi::CreateCall().\n"));
|
|
_ASSERT( pInfo );
|
|
|
|
// Make sure we only show once
|
|
if ( pInfo->bShowDialog && !AtomicSeizeToken(m_lShowCallDialog) ) return S_OK;
|
|
|
|
HRESULT hr = S_OK;
|
|
int nRet;
|
|
CComBSTR l_bstrDisplayableAddress( pInfo->lpszDisplayableAddress );
|
|
|
|
if ( pInfo->bShowDialog )
|
|
{
|
|
|
|
// Create dialog and initialize data memebers
|
|
CDlgPlaceCall dlg;
|
|
SysReAllocString( &dlg.m_bstrAddress, pInfo->bstrAddress );
|
|
dlg.m_dwAddressType = pInfo->lAddressType;
|
|
|
|
//
|
|
// Store the pointer to the dialog for USBEvents
|
|
//
|
|
m_pDlgCall = &dlg;
|
|
|
|
nRet = dlg.DoModal( _Module.GetParentWnd() );
|
|
|
|
//
|
|
// Release the pojnter to the dialog
|
|
//
|
|
m_pDlgCall = NULL;
|
|
|
|
AtomicReleaseToken( m_lShowCallDialog );
|
|
pInfo->lRet = (long) nRet;
|
|
ATLTRACE(_T(".1.CAVTapi::CreateCall() - dialog returned %ld.\n"), nRet );
|
|
|
|
// Retrieve dialog information
|
|
SysReAllocString( &pInfo->bstrName, dlg.m_bstrName );
|
|
SysReAllocString( &pInfo->bstrAddress, dlg.m_bstrAddress );
|
|
pInfo->lAddressType = dlg.m_dwAddressType;
|
|
pInfo->bAddToSpeeddial = dlg.m_bAddToSpeeddial;
|
|
|
|
if ( nRet != IDOK )
|
|
return hr;
|
|
}
|
|
else if ( (pInfo->lAddressType & LINEADDRESSTYPE_SDP) != NULL )
|
|
{
|
|
// Make sure there isn't a conference already in session
|
|
CErrorInfo er( IDS_ER_CALL_ENTERCONFROOM, IDS_ER_CONFERENCE_ROOM_LIMIT_EXCEEDED );
|
|
IConfRoom *pConfRoom;
|
|
if ( SUCCEEDED(get_ConfRoom(&pConfRoom)) )
|
|
{
|
|
if ( pConfRoom->IsConfRoomInUse() == S_OK )
|
|
er.set_hr( E_ABORT );
|
|
|
|
pConfRoom->Release();
|
|
}
|
|
|
|
if ( FAILED(er.m_hr) )
|
|
return er.m_hr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
// If the user specifies a conference, we try to match it using the information entered
|
|
// If the match is solid (one hit) we automatically call, otherwise we throw up a dialog
|
|
// showing all the conferences that appear to match.
|
|
//
|
|
if ( !pInfo->lpszDisplayableAddress && ((pInfo->lAddressType & LINEADDRESSTYPE_SDP) != NULL) )
|
|
{
|
|
bool bReturn = true;
|
|
IConfExplorer *pConfExplorer;
|
|
if ( SUCCEEDED(hr = get_ConfExplorer(&pConfExplorer)) )
|
|
{
|
|
CONFDETAILSLIST lstConfs;
|
|
|
|
// Enumerate all the conferences that match the criteria entered
|
|
IConfExplorerTreeView *pTreeView;
|
|
if ( SUCCEEDED(pConfExplorer->get_TreeView(&pTreeView)) )
|
|
{
|
|
// first time through request only scheduled conferences
|
|
pTreeView->BuildJoinConfListText( (long *) &lstConfs, pInfo->bstrAddress );
|
|
pTreeView->Release();
|
|
}
|
|
|
|
// Do we have a definitive match?
|
|
if ( lstConfs.size() == 0 )
|
|
{
|
|
_Module.DoMessageBox(IDS_MSG_NO_CONFS_MATCHED, MB_ICONINFORMATION, false );
|
|
}
|
|
else if ( (lstConfs.size() == 1) && lstConfs.front()->m_bstrAddress && (SysStringLen(lstConfs.front()->m_bstrAddress) > 0) )
|
|
{
|
|
// Setup to join this specific conference
|
|
if ( pInfo->bstrName )
|
|
{
|
|
SysFreeString( pInfo->bstrName );
|
|
pInfo->bstrName = NULL;
|
|
}
|
|
|
|
SysReAllocString( &pInfo->bstrAddress, lstConfs.front()->m_bstrAddress );
|
|
SysReAllocString( &l_bstrDisplayableAddress, lstConfs.front()->m_bstrName );
|
|
bReturn = false;
|
|
}
|
|
else
|
|
{
|
|
// Multiple hits, resolve via conference dialog
|
|
CDlgJoinConference dlgJoin;
|
|
SysReAllocString( &dlgJoin.m_bstrSearchText, pInfo->bstrAddress );
|
|
|
|
if ( ((nRet = dlgJoin.DoModal(_Module.GetParentWnd())) == IDOK) && dlgJoin.m_confDetails.m_bstrAddress && (SysStringLen(dlgJoin.m_confDetails.m_bstrAddress) > 0) )
|
|
hr = pConfExplorer->Join( (long *) &dlgJoin.m_confDetails );
|
|
|
|
// Store dialog return value
|
|
pInfo->lRet = (long) nRet;
|
|
}
|
|
|
|
// Clean up
|
|
DELETE_LIST( lstConfs );
|
|
pConfExplorer->Release();
|
|
}
|
|
|
|
if ( bReturn ) return hr;
|
|
}
|
|
|
|
CErrorInfo er;
|
|
er.set_Operation( IDS_ER_PLACECALL );
|
|
er.set_Details( IDS_ER_GET_ADDRESS );
|
|
|
|
ITAddress *pITAddress;
|
|
if ( SUCCEEDED(hr = er.set_hr(GetAddress(pInfo->lAddressType, true, &pITAddress))) )
|
|
{
|
|
// Setup dialing info to pass to dialing thread
|
|
er.set_Details( IDS_ER_CREATE_THREAD );
|
|
CThreadDialingInfo *pThreadInfo = new CThreadDialingInfo;
|
|
if ( pThreadInfo )
|
|
{
|
|
HRESULT hrDialog = S_OK;
|
|
|
|
// Resolve the address
|
|
if ( (pInfo->lAddressType & LINEADDRESSTYPE_SDP) == NULL )
|
|
{
|
|
CComPtr<IAVGeneralNotification> pAVGen;
|
|
if ( SUCCEEDED(_Module.get_AVGenNot(&pAVGen)) )
|
|
{
|
|
BSTR bstrResolvedName = NULL;
|
|
BSTR bstrResolvedAddress = NULL;
|
|
|
|
// Resolve
|
|
hrDialog = pAVGen->fire_ResolveAddressEx( pInfo->bstrAddress,
|
|
_Module.GuessAddressType( OLE2CT(pInfo->bstrAddress) ),
|
|
AddressToMediaType(pInfo->lAddressType),
|
|
DIALER_LOCATIONTYPE_UNKNOWN,
|
|
&bstrResolvedName,
|
|
&bstrResolvedAddress,
|
|
&pThreadInfo->m_bstrUser1,
|
|
&pThreadInfo->m_bstrUser2 );
|
|
|
|
if ( SUCCEEDED(hrDialog) )
|
|
pThreadInfo->m_bResolved = true;
|
|
}
|
|
}
|
|
|
|
if ( hrDialog != S_FALSE )
|
|
{
|
|
// Store information in dialing structure
|
|
pThreadInfo->set_ITAddress( pITAddress );
|
|
if ( pInfo->bstrName ) pThreadInfo->m_bstrName = SysAllocString( pInfo->bstrName );
|
|
pThreadInfo->m_bstrAddress = SysAllocString( pInfo->bstrAddress );
|
|
pThreadInfo->m_bstrOriginalAddress = SysAllocString( (l_bstrDisplayableAddress == NULL) ? pInfo->bstrAddress : l_bstrDisplayableAddress );
|
|
pThreadInfo->m_dwAddressType = pInfo->lAddressType;
|
|
pThreadInfo->TranslateAddress();
|
|
|
|
// Want to return the displayable address, rather than the dialable address
|
|
SysReAllocString( &pInfo->bstrAddress, pThreadInfo->m_bstrOriginalAddress );
|
|
|
|
// Dialing takes place on separate thread
|
|
DWORD dwID;
|
|
HANDLE hThread = CreateThread( NULL, 0, ThreadDialingProc, (void *) pThreadInfo, NULL, &dwID );
|
|
if ( !hThread )
|
|
{
|
|
hr = er.set_hr( E_UNEXPECTED );
|
|
ATLTRACE(_T(".error.CAVTapi::CreateCall() -- failed to creat the dialing thread.\n") );
|
|
delete pThreadInfo;
|
|
}
|
|
else
|
|
{
|
|
CloseHandle( hThread );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = er.set_hr( E_OUTOFMEMORY );
|
|
}
|
|
|
|
pITAddress->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::JoinConference(long *pnRet, BOOL bShowDialog, long *pConfDetails )
|
|
{
|
|
CDlgJoinConference dlg;
|
|
HRESULT hr = S_OK;
|
|
int nRet = IDOK;
|
|
|
|
// Gather information from user
|
|
if ( bShowDialog )
|
|
{
|
|
nRet = dlg.DoModal( _Module.GetParentWnd() );
|
|
if ( pnRet ) *pnRet = nRet;
|
|
}
|
|
else
|
|
{
|
|
_ASSERT( pConfDetails ); // if you're not showing the dialog, you better have something to dial
|
|
dlg.m_confDetails = *((CConfDetails *) pConfDetails);
|
|
}
|
|
|
|
// Join selected conference
|
|
// Join the conference if we have a valid conference name
|
|
if ( (nRet == IDOK) && dlg.m_confDetails.m_bstrAddress && (SysStringLen(dlg.m_confDetails.m_bstrAddress) > 0) )
|
|
{
|
|
m_critConfExplorer.Lock();
|
|
if ( m_pIConfExplorer )
|
|
hr = m_pIConfExplorer->Join( (long *) &dlg.m_confDetails );
|
|
m_critConfExplorer.Unlock();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CAVTapi::GetAddress( DWORD dwAddressType, bool bErrorMsg, ITAddress **ppITAddress )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD dwPermID = 0;
|
|
DWORD dwAddrID = 0;
|
|
|
|
// Retrieve address information stored in the registry
|
|
TCHAR szText[255];
|
|
CRegKey regKey;
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_KEY, szText, ARRAYSIZE(szText) );
|
|
if ( regKey.Open(HKEY_CURRENT_USER, szText, KEY_READ) == ERROR_SUCCESS )
|
|
{
|
|
LoadString( _Module.GetResourceInstance(), AddressTypeToRegKey(dwAddressType, true), szText, ARRAYSIZE(szText) );
|
|
regKey.QueryValue( dwPermID, szText );
|
|
|
|
LoadString( _Module.GetResourceInstance(), AddressTypeToRegKey(dwAddressType, false), szText, ARRAYSIZE(szText) );
|
|
regKey.QueryValue( dwAddrID, szText );
|
|
|
|
|
|
// Has the user specified a particular address?
|
|
if ( dwPermID )
|
|
{
|
|
// Open the specified address
|
|
if ( FAILED(hr = GetDefaultAddress(dwAddressType, dwPermID, dwAddrID, ppITAddress)) )
|
|
{
|
|
// Notify user that we could not retrieve the specified address
|
|
if ( !bErrorMsg || _Module.DoMessageBox(IDS_MSG_PLACECALL_GETADDRESS, MB_YESNO | MB_ICONQUESTION, false) == IDYES )
|
|
dwPermID = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !dwPermID ) hr = GetDefaultAddress( dwAddressType, 0, 0, ppITAddress );
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CAVTapi::GetDefaultAddress( DWORD dwAddressType, DWORD dwPermID, DWORD dwAddrID, ITAddress **ppITAddress )
|
|
{
|
|
// Is TAPI running?
|
|
_ASSERT(m_pITTapi);
|
|
if ( !m_pITTapi ) return E_PENDING;
|
|
|
|
// Loop through addresses, looking for one that supports interactive voice
|
|
HRESULT hr;
|
|
IEnumAddress *pEnumAddresses;
|
|
if ( FAILED(hr = m_pITTapi->EnumerateAddresses(&pEnumAddresses)) ) return hr;
|
|
bool bFoundAddress = false;
|
|
|
|
while ( !bFoundAddress )
|
|
{
|
|
if ( (hr = pEnumAddresses->Next(1, ppITAddress, NULL)) != S_OK ) break;
|
|
|
|
// Address must support audio in and out
|
|
ITMediaSupport *pITMediaSupport;
|
|
if ( SUCCEEDED(hr = (*ppITAddress)->QueryInterface(IID_ITMediaSupport, (void **) &pITMediaSupport)) )
|
|
{
|
|
VARIANT_BOOL bSupport;
|
|
if ( SUCCEEDED(pITMediaSupport->QueryMediaType(TAPIMEDIATYPE_AUDIO, &bSupport)) && bSupport )
|
|
{
|
|
// Look for an address that supports the requested address type
|
|
ITAddressCapabilities *pCaps;
|
|
if ( SUCCEEDED((*ppITAddress)->QueryInterface(IID_ITAddressCapabilities, (void **) &pCaps)) )
|
|
{
|
|
long lAddrTypes = 0;
|
|
pCaps->get_AddressCapability( AC_ADDRESSTYPES, &lAddrTypes );
|
|
|
|
// Is this the address type we're looking for?
|
|
if ( (lAddrTypes & dwAddressType) != 0 )
|
|
bFoundAddress = TRUE;
|
|
|
|
pCaps->Release();
|
|
}
|
|
}
|
|
pITMediaSupport->Release();
|
|
}
|
|
|
|
// Is a particular address specified?
|
|
if ( dwPermID )
|
|
{
|
|
long lPermID = 0, lAddrID = 0;
|
|
// We need this to identify an address
|
|
|
|
ITAddressCapabilities *pCaps;
|
|
if ( SUCCEEDED((*ppITAddress)->QueryInterface(IID_ITAddressCapabilities, (void **) &pCaps)) )
|
|
{
|
|
pCaps->get_AddressCapability( AC_PERMANENTDEVICEID, &lPermID );
|
|
pCaps->get_AddressCapability( AC_ADDRESSID, &lAddrID );
|
|
pCaps->Release();
|
|
}
|
|
|
|
if ( ((DWORD) lPermID != dwPermID) || ((DWORD) lAddrID != dwAddrID) ) bFoundAddress = false;
|
|
}
|
|
|
|
// If we didn't find an address, move on to the next one
|
|
if ( !bFoundAddress )
|
|
RELEASE(*ppITAddress);
|
|
}
|
|
pEnumAddresses->Release();
|
|
|
|
if ( SUCCEEDED(hr) && !bFoundAddress ) hr = E_ABORT;
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CAVTapi::CreateTerminalArray( ITAddress *pITAddress, IAVTapiCall *pAVCall, ITCallInfo *pITCallInfo )
|
|
{
|
|
int i;
|
|
HRESULT hr;
|
|
|
|
// Has the user specified particular terminals for the address?
|
|
USES_CONVERSION;
|
|
BSTR bstrTerm[3] = { NULL, NULL, NULL };
|
|
DWORD dwAddressType;
|
|
pAVCall->get_dwAddressType( &dwAddressType );
|
|
|
|
if ( IsPreferredAddress(pITAddress, dwAddressType) )
|
|
{
|
|
// Build the registry key where the terminal information is stored
|
|
TCHAR szText[255], szKey[255];
|
|
CRegKey regKey;
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_KEY, szText, ARRAYSIZE(szText) );
|
|
LoadString( _Module.GetResourceInstance(), AddressTypeToRegKey(dwAddressType, true), szKey, ARRAYSIZE(szKey) );
|
|
_tcscat( szText, _T("\\") );
|
|
_tcscat( szText, szKey );
|
|
|
|
// Try to open the key
|
|
if ( (regKey.Open(HKEY_CURRENT_USER, szText, KEY_READ) == ERROR_SUCCESS) ||
|
|
(dwAddressType == LINEADDRESSTYPE_SDP))
|
|
{
|
|
UINT nIDS_Key[] = { IDN_REG_REDIAL_TERMINAL_AUDIO_CAPTURE, IDN_REG_REDIAL_TERMINAL_AUDIO_RENDER, IDN_REG_REDIAL_TERMINAL_VIDEO_CAPTURE };
|
|
for ( i = 0; i < ARRAYSIZE(nIDS_Key); i++ )
|
|
{
|
|
// Retrieve terminal specified for a particular device
|
|
DWORD dwCount = ARRAYSIZE(szKey);
|
|
|
|
//
|
|
// Don't select terminals on audio streams
|
|
// if Phone was already selected
|
|
//
|
|
HRESULT hrReserved = E_FAIL;
|
|
hrReserved = USBReserveStreamForPhone(
|
|
nIDS_Key[i],
|
|
&bstrTerm[i]
|
|
);
|
|
|
|
if( FAILED(hrReserved) )
|
|
{
|
|
// The Phone wasn't selected on this stream
|
|
// we'll use the old mechanism to get from registry
|
|
// the terminal for this stream
|
|
LoadString( _Module.GetResourceInstance(), nIDS_Key[i], szText, ARRAYSIZE(szText) );
|
|
if ( (regKey.QueryValue(szKey, szText, &dwCount) == ERROR_SUCCESS) && (dwCount > 0) )
|
|
bstrTerm[i] = SysAllocString( T2COLE(szKey) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Did we get a good set of terminals?
|
|
hr = CreateTerminals( pITAddress, dwAddressType, pAVCall, pITCallInfo, bstrTerm );
|
|
|
|
// Clean up
|
|
for ( i = 0; i < ARRAYSIZE(bstrTerm); i++ )
|
|
SysFreeString( bstrTerm[i] );
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CAVTapi::CreateTerminals( ITAddress *pITAddress, DWORD dwAddressType, IAVTapiCall *pAVCall, ITCallInfo *pITCallInfo, BSTR *pbstrTerm )
|
|
{
|
|
#define LOOP_AUDIOIN 0
|
|
#define LOOP_AUDIOOUT 1
|
|
#define LOOP_VIDEOIN 2
|
|
|
|
// Must have a valid address object
|
|
_ASSERT( pITAddress );
|
|
|
|
ATLTRACE(_T(".enter.CAVTapi::CreateTerminals().\n"));
|
|
|
|
USES_CONVERSION;
|
|
HRESULT hr;
|
|
bool bAllocPreview = false;
|
|
|
|
ITStreamControl *pStreamControl;
|
|
hr = pITCallInfo->QueryInterface( IID_ITStreamControl, (void **) &pStreamControl );
|
|
if ( FAILED(hr) )
|
|
return (hr == E_NOINTERFACE) ? S_OK : hr;
|
|
|
|
int nInd = 0;
|
|
TCHAR szTemp[100];
|
|
LoadString( _Module.GetResourceInstance(), IDS_NONE_DEVICE, szTemp, ARRAYSIZE(szTemp) );
|
|
BSTR bstrNone = SysAllocString( T2COLE(szTemp) );
|
|
if ( !bstrNone ) return E_OUTOFMEMORY;
|
|
|
|
// What media types does the address support
|
|
long lSupportedMediaModes = 0;
|
|
ITMediaSupport *pITMediaSupport;
|
|
if ( SUCCEEDED(pITAddress->QueryInterface(IID_ITMediaSupport, (void **) &pITMediaSupport)) )
|
|
{
|
|
pITMediaSupport->get_MediaTypes( &lSupportedMediaModes );
|
|
pITMediaSupport->Release();
|
|
}
|
|
|
|
ITTerminalSupport *pITTerminalSupport;
|
|
if ( SUCCEEDED(hr = pITAddress->QueryInterface(IID_ITTerminalSupport, (void **) &pITTerminalSupport)) )
|
|
{
|
|
for ( int i = 0; i < 3; i++ )
|
|
{
|
|
CErrorInfo er;
|
|
er.set_Operation( IDS_ER_CREATE_TERMINALS );
|
|
|
|
// Which terminal are we doing?
|
|
long lMediaMode;
|
|
TERMINAL_DIRECTION nDir;
|
|
UINT nIDSDetails;
|
|
|
|
switch ( i )
|
|
{
|
|
case LOOP_AUDIOIN:
|
|
nIDSDetails = IDS_ER_CREATE_AUDIO_CAPTURE;
|
|
lMediaMode = TAPIMEDIATYPE_AUDIO;
|
|
nDir = TD_CAPTURE;
|
|
break;
|
|
|
|
case LOOP_AUDIOOUT:
|
|
nIDSDetails = IDS_ER_CREATE_AUDIO_RENDER;
|
|
lMediaMode = TAPIMEDIATYPE_AUDIO;
|
|
nDir = TD_RENDER;
|
|
break;
|
|
|
|
case LOOP_VIDEOIN:
|
|
nIDSDetails = IDS_ER_CREATE_VIDEO_CAPTURE;
|
|
lMediaMode = TAPIMEDIATYPE_VIDEO;
|
|
nDir = TD_CAPTURE;
|
|
break;
|
|
}
|
|
|
|
// Skip if they don't support the media mode
|
|
if ( (lMediaMode & lSupportedMediaModes) == 0 )
|
|
continue;
|
|
|
|
// Check and make sure that terminals exist for this driver:
|
|
bool bSkipTerminal = true;
|
|
IEnumTerminal *pEnumTerminal;
|
|
if ( pITTerminalSupport->EnumerateStaticTerminals(&pEnumTerminal) == S_OK )
|
|
{
|
|
// What type of terminal do we have? (audio in, audio out, video in, etc.)
|
|
ITTerminal *pITTerminal;
|
|
while ( bSkipTerminal && (pEnumTerminal->Next(1, &pITTerminal, NULL) == S_OK) )
|
|
{
|
|
TERMINAL_DIRECTION nTD;
|
|
long nTerminalType;
|
|
|
|
// Render or Capture?
|
|
if ( SUCCEEDED(pITTerminal->get_Direction(&nTD)) && SUCCEEDED(pITTerminal->get_MediaType(&nTerminalType)) )
|
|
{
|
|
if ( (nTerminalType == lMediaMode) && (nTD == nDir) )
|
|
bSkipTerminal = false;
|
|
}
|
|
|
|
// Clean up
|
|
pITTerminal->Release();
|
|
}
|
|
pEnumTerminal->Release();
|
|
}
|
|
|
|
if ( bSkipTerminal )
|
|
continue;
|
|
|
|
// Set Audio Echo Cancellation
|
|
if( (lMediaMode == TAPIMEDIATYPE_AUDIO) &&
|
|
( nDir == TD_CAPTURE) &&
|
|
( m_bAEC == TRUE) )
|
|
{
|
|
HRESULT hrAEC = AECSetOnStream( pStreamControl, m_bAEC);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
// Allocate the terminal
|
|
//
|
|
|
|
ITTerminal *pTempTerminal = NULL;
|
|
|
|
if ( pbstrTerm[i] )
|
|
{
|
|
// Ignore NONE terminals
|
|
if ( !wcscmp(bstrNone, pbstrTerm[i]) )
|
|
continue;
|
|
|
|
er.set_Details( nIDSDetails );
|
|
hr = er.set_hr( GetTerminal(pITTerminalSupport, lMediaMode, nDir, pbstrTerm[i], &pTempTerminal) );
|
|
ATLTRACE(_T("CAVTapi::CreateTerminals(%d) -- hr=0x%08lx creating terminal %s.\n"), nInd, hr, OLE2CT(pbstrTerm[i]) );
|
|
}
|
|
|
|
// If the user hasn't specified a particular terminal, use the default one
|
|
if ( !pTempTerminal )
|
|
{
|
|
hr = er.set_hr( pITTerminalSupport->GetDefaultStaticTerminal(lMediaMode, nDir, &pTempTerminal) );
|
|
ATLTRACE(_T("CAVTapi::CreateTerminals(%d) -- hr=0x%08lx create default Audio=%d, Render=%d.\n"), nInd, hr, (bool) (lMediaMode == TAPIMEDIATYPE_AUDIO), nDir );
|
|
}
|
|
|
|
if ( hr != S_OK ) break; // failed to get the terminal
|
|
|
|
// Select the terminal onto the stream
|
|
if ( SUCCEEDED(hr = SelectTerminalOnStream(pStreamControl, lMediaMode, nDir, pTempTerminal, pAVCall)) )
|
|
{
|
|
if ( (lMediaMode == TAPIMEDIATYPE_VIDEO) && (nDir == TD_CAPTURE) )
|
|
bAllocPreview = true;
|
|
|
|
nInd++;
|
|
}
|
|
|
|
pTempTerminal->Release();
|
|
}
|
|
|
|
// Did we get the requested terminals?
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
CErrorInfo er;
|
|
er.set_Operation( IDS_ER_CREATE_TERMINALS );
|
|
|
|
HRESULT hrTemp = S_OK;
|
|
LPOLESTR psz = NULL;
|
|
BSTR bstrTerminalClass = NULL;
|
|
STRING_FROM_IID(CLSID_VideoWindowTerm, bstrTerminalClass);
|
|
|
|
// Do we need to allocate a preview window?
|
|
if ( bAllocPreview )
|
|
{
|
|
ITTerminal *pPreviewTerminal = NULL;
|
|
if ( ((hrTemp = er.set_hr(pITTerminalSupport->CreateTerminal(bstrTerminalClass, TAPIMEDIATYPE_VIDEO, TD_RENDER, &pPreviewTerminal))) == S_OK) && pPreviewTerminal )
|
|
{
|
|
SelectTerminalOnStream( pStreamControl, TAPIMEDIATYPE_VIDEO, TD_CAPTURE, pPreviewTerminal, pAVCall );
|
|
pPreviewTerminal->Release();
|
|
}
|
|
}
|
|
|
|
// Try for video in (recieve video) -- this is a 'dynamic' terminal
|
|
if ( ((lSupportedMediaModes & TAPIMEDIATYPE_VIDEO) != 0) && (CanCreateVideoWindows(dwAddressType) == S_OK) )
|
|
{
|
|
// For conferences we want to create more terminals.
|
|
short nNumCreate = 1;
|
|
if ( (dwAddressType & LINEADDRESSTYPE_SDP) != NULL )
|
|
{
|
|
IConfRoom *pConfRoom;
|
|
if ( SUCCEEDED(get_ConfRoom(&pConfRoom)) )
|
|
{
|
|
pConfRoom->get_nMaxTerms(&nNumCreate);
|
|
pConfRoom->Release();
|
|
}
|
|
}
|
|
|
|
er.set_Details( IDS_ER_CREATE_VIDEO_RENDER );
|
|
|
|
// Create the requested number of terminals
|
|
while ( nNumCreate-- )
|
|
{
|
|
ITTerminal *pVidTerminal = NULL;
|
|
if ( ((hrTemp = er.set_hr(pITTerminalSupport->CreateTerminal(bstrTerminalClass, TAPIMEDIATYPE_VIDEO, TD_RENDER, &pVidTerminal))) == S_OK) && pVidTerminal )
|
|
{
|
|
SelectTerminalOnStream( pStreamControl, TAPIMEDIATYPE_VIDEO, TD_RENDER, pVidTerminal, pAVCall );
|
|
pVidTerminal->Release();
|
|
nInd++;
|
|
}
|
|
}
|
|
|
|
ATLTRACE(_T("CAVTapi::CreateTerminals(%d) -- hr=0x%08lx creating Video Render.\n"), nInd, hr );
|
|
}
|
|
SysFreeString( bstrTerminalClass );
|
|
|
|
/*
|
|
// Was not able to create any terminals!
|
|
if ( nInd == 0 )
|
|
hr = E_ABORT;
|
|
*/
|
|
}
|
|
|
|
pITTerminalSupport->Release();
|
|
}
|
|
|
|
SysFreeString( bstrNone );
|
|
pStreamControl->Release();
|
|
|
|
ATLTRACE(_T(".exit.CAVTapi::CreateTerminals().\n"));
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CAVTapi::GetTerminal( ITTerminalSupport *pITTerminalSupport, long nReqType, TERMINAL_DIRECTION nReqTD, BSTR bstrReqName, ITTerminal **ppITTerminal )
|
|
{
|
|
IEnumTerminal *pEnumTerminal;
|
|
HRESULT hr = pITTerminalSupport->EnumerateStaticTerminals( &pEnumTerminal );
|
|
if ( hr != S_OK ) return hr;
|
|
|
|
bool bFoundTerminal = false;
|
|
|
|
// Look for a terminal with the specified characteristics
|
|
while ( pEnumTerminal->Next(1, ppITTerminal, NULL) == S_OK )
|
|
{
|
|
// Is it going the right direction? Render / Capture
|
|
TERMINAL_DIRECTION nTD;
|
|
if ( SUCCEEDED((*ppITTerminal)->get_Direction(&nTD)) && (nTD == nReqTD) )
|
|
{
|
|
// Is in the right type? Audio / Video
|
|
long nType;
|
|
if ( SUCCEEDED(hr = (*ppITTerminal)->get_MediaType(&nType)) && (nType == nReqType) )
|
|
{
|
|
// Does it have the right name?
|
|
BSTR bstrName = NULL;
|
|
if ( SUCCEEDED(hr = (*ppITTerminal)->get_Name(&bstrName)) && (wcscmp(bstrName, bstrReqName) == 0) )
|
|
bFoundTerminal = true;
|
|
|
|
SysFreeString( bstrName );
|
|
}
|
|
}
|
|
|
|
// Exit condition
|
|
if ( bFoundTerminal ) break;
|
|
|
|
// Reset pointer
|
|
(*ppITTerminal)->Release();
|
|
*ppITTerminal = NULL;
|
|
}
|
|
|
|
pEnumTerminal->Release();
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::get_ConfExplorer(IConfExplorer **ppVal)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
m_critConfExplorer.Lock();
|
|
|
|
if ( !m_pIConfExplorer )
|
|
{
|
|
// No conference explorer object, create a new one
|
|
m_pIConfExplorer = new CComObject<CConfExplorer>;
|
|
if ( m_pIConfExplorer )
|
|
m_pIConfExplorer->AddRef();
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
// AddRef before returning
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
*ppVal = m_pIConfExplorer;
|
|
(*ppVal)->AddRef();
|
|
}
|
|
|
|
m_critConfExplorer.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::get_ConfRoom(IConfRoom **ppVal)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
m_critConfRoom.Lock();
|
|
if ( m_pIConfRoom )
|
|
hr = m_pIConfRoom->QueryInterface(IID_IConfRoom, (void **) ppVal );
|
|
m_critConfRoom.Unlock();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
CallManagerMedia CAVTapi::ResolveMediaType( long lAddressType )
|
|
{
|
|
switch ( lAddressType )
|
|
{
|
|
case LINEADDRESSTYPE_SDP: return CM_MEDIA_MCCONF;
|
|
case LINEADDRESSTYPE_PHONENUMBER: return CM_MEDIA_POTS;
|
|
}
|
|
|
|
return CM_MEDIA_INTERNET;
|
|
}
|
|
|
|
IAVTapiCall* CAVTapi::FindAVTapiCall( long lCallID )
|
|
{
|
|
IAVTapiCall *pRet = NULL;
|
|
|
|
// $CRIT_ENTER
|
|
m_critLstAVTapiCalls.Lock();
|
|
AVTAPICALLLIST::iterator i, iEnd = m_lstAVTapiCalls.end();
|
|
for ( i = m_lstAVTapiCalls.begin(); i != iEnd; i++ )
|
|
{
|
|
long lNewCallID;
|
|
if ( SUCCEEDED((*i)->get_lCallID(&lNewCallID)) && (lCallID == lNewCallID) )
|
|
{
|
|
(*i)->AddRef();
|
|
pRet = *i;
|
|
break;
|
|
}
|
|
}
|
|
m_critLstAVTapiCalls.Unlock();
|
|
// $CRIT_EXIT
|
|
|
|
return pRet;
|
|
}
|
|
|
|
IAVTapiCall* CAVTapi::FindAVTapiCall( ITBasicCallControl *pControl )
|
|
{
|
|
ATLTRACE(_T(".enter.CAVTapi::FindAVTapiCall().\n"));
|
|
_ASSERT( pControl );
|
|
IAVTapiCall *pRet = NULL;
|
|
|
|
// $CRIT_ENTER
|
|
m_critLstAVTapiCalls.Lock();
|
|
AVTAPICALLLIST::iterator i, iEnd = m_lstAVTapiCalls.end();
|
|
for ( i = m_lstAVTapiCalls.begin(); i != iEnd; i++ )
|
|
{
|
|
ITBasicCallControl *pIndControl;
|
|
if ( SUCCEEDED((*i)->get_ITBasicCallControl(&pIndControl)) && pIndControl)
|
|
{
|
|
// found match?
|
|
pIndControl->Release();
|
|
if ( pIndControl == pControl )
|
|
{
|
|
(*i)->AddRef();
|
|
pRet = *i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
m_critLstAVTapiCalls.Unlock();
|
|
// $CRIT_EXIT
|
|
|
|
return pRet;
|
|
}
|
|
|
|
IAVTapiCall* CAVTapi::AddAVTapiCall( ITBasicCallControl *pControl, long lCallID )
|
|
{
|
|
CAVTapiCall *pNewCall = new CComObject<CAVTapiCall>;
|
|
if ( pNewCall )
|
|
{
|
|
pNewCall->AddRef();
|
|
pNewCall->put_lCallID( lCallID );
|
|
pNewCall->put_callState( CS_IDLE );
|
|
|
|
if ( pControl )
|
|
pNewCall->put_ITBasicCallControl( pControl );
|
|
|
|
// Add to list
|
|
// $CRIT_ENTER
|
|
m_critLstAVTapiCalls.Lock();
|
|
m_lstAVTapiCalls.push_back( pNewCall );
|
|
m_critLstAVTapiCalls.Unlock();
|
|
// $CRIT_EXIT
|
|
|
|
ATLTRACE(_T(".1.CAVTapi::AddAVTapiCall() -- added %ld.\n"), lCallID );
|
|
|
|
// Second AddRef() is for the 'get' type operation
|
|
pNewCall->AddRef();
|
|
return pNewCall;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
bool CAVTapi::RemoveAVTapiCall( ITBasicCallControl *pDeleteControl )
|
|
{
|
|
IAVTapiCall *pAVCall = NULL;
|
|
|
|
// $CRIT_ENTER
|
|
m_critLstAVTapiCalls.Lock();
|
|
AVTAPICALLLIST::iterator i, iEnd = m_lstAVTapiCalls.end();
|
|
for ( i = m_lstAVTapiCalls.begin(); i != iEnd; i++ )
|
|
{
|
|
ITBasicCallControl *pControl;
|
|
if ( SUCCEEDED((*i)->get_ITBasicCallControl(&pControl)) )
|
|
{
|
|
if ( pControl == pDeleteControl )
|
|
{
|
|
// found match?
|
|
ATLTRACE(_T("CAVTapi::RemoveAVTapiCall(%p).\n"), pControl );
|
|
pAVCall = *i;
|
|
m_lstAVTapiCalls.erase( i );
|
|
pControl->Release();
|
|
break;
|
|
}
|
|
pControl->Release();
|
|
}
|
|
}
|
|
m_critLstAVTapiCalls.Unlock();
|
|
// $CRIT_EXIT
|
|
|
|
// Destroy call object
|
|
if ( pAVCall ) pAVCall->Release();
|
|
|
|
return bool (pAVCall != NULL);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// ActionSelected()
|
|
//
|
|
// This method is use by the client EXE to signal an action taken on a particular call
|
|
// identified by lCallID. The action is defined by the CallManagerActions enum. If
|
|
// lCallID is -1 the function will use the GetFirstActiveCall() method to retrieve a
|
|
// call to use.
|
|
//
|
|
//
|
|
STDMETHODIMP CAVTapi::ActionSelected(long lCallID, CallManagerActions cma)
|
|
{
|
|
#undef FETCH_STRING
|
|
#define FETCH_STRING(_CMS_, _IDS_) \
|
|
{ \
|
|
LoadString( _Module.GetResourceInstance(), _IDS_, szText, ARRAYSIZE(szText) ); \
|
|
SysReAllocString( &bstrText, T2COLE(szText) ); \
|
|
fire_SetCallState_CMS(lCallID, _CMS_, bstrText); \
|
|
}
|
|
|
|
ATLTRACE(_T(".enter.CAVTapi::ActionSelected(id=%ld, action=%d).\n"), lCallID, cma );
|
|
IAVTapiCall *pAVCall = NULL;
|
|
if ( lCallID == -1 )
|
|
GetFirstCall( &pAVCall ); // If -1 use any call we can find
|
|
else
|
|
pAVCall = FindAVTapiCall( lCallID );
|
|
|
|
if ( !pAVCall )
|
|
{
|
|
// We don't have this call anymore, close it
|
|
if ( (lCallID != -1) && (cma == CM_ACTIONS_CLOSE) ) fire_CloseCallControl( lCallID );
|
|
return S_OK;
|
|
}
|
|
|
|
USES_CONVERSION;
|
|
HRESULT hr;
|
|
BSTR bstrText = NULL;
|
|
TCHAR szText[255];
|
|
|
|
ITCallInfo *pInfo = NULL;
|
|
ITBasicCallControl *pControl = NULL;
|
|
CALL_STATE curState = CS_IDLE;
|
|
|
|
if ( SUCCEEDED(hr = pAVCall->get_ITBasicCallControl(&pControl)) )
|
|
{
|
|
if ( SUCCEEDED(pControl->QueryInterface(IID_ITCallInfo, (void **) &pInfo)) )
|
|
pInfo->get_CallState( &curState );
|
|
}
|
|
|
|
switch( cma )
|
|
{
|
|
// Place call on hold or take off hold
|
|
case CM_ACTIONS_TAKECALL:
|
|
if ( pControl && pInfo )
|
|
{
|
|
if ( curState == CS_HOLD )
|
|
{
|
|
FETCH_STRING( CM_STATES_CURRENT, IDS_PLACECALL_UNHOLDING );
|
|
hr = pControl->Hold( false );
|
|
}
|
|
else
|
|
{
|
|
hr = AnswerAction( pInfo, pControl, pAVCall, FALSE );
|
|
}
|
|
}
|
|
break;
|
|
|
|
// Hang up the call; if OFFERING then this means reject the call
|
|
case CM_ACTIONS_REJECTCALL:
|
|
case CM_ACTIONS_DISCONNECT:
|
|
FETCH_STRING(CM_STATES_CURRENT, IDS_PLACECALL_DISCONNECTING)
|
|
fire_ClearCurrentActions( lCallID );
|
|
fire_AddCurrentAction( lCallID, CM_ACTIONS_CLOSE, NULL );
|
|
|
|
hr = pAVCall->PostMessage( 0, CAVTapiCall::TI_DISCONNECT );
|
|
break;
|
|
|
|
// Place call on hold
|
|
case CM_ACTIONS_HOLD:
|
|
if ( pControl )
|
|
hr = pControl->Hold( (bool) (curState != CS_HOLD) );
|
|
break;
|
|
|
|
case CM_ACTIONS_ENTERCONFROOM:
|
|
{
|
|
IConfRoom *pConfRoom;
|
|
if ( SUCCEEDED(get_ConfRoom(&pConfRoom)) )
|
|
{
|
|
pConfRoom->Release();
|
|
}
|
|
}
|
|
break;
|
|
|
|
// Close call control dialog
|
|
case CM_ACTIONS_CLOSE:
|
|
if ( SUCCEEDED(hr = fire_CloseCallControl(lCallID)) )
|
|
RemoveAVTapiCall( pControl );
|
|
break;
|
|
|
|
#ifdef _BAKEOFF
|
|
case CM_ACTIONS_TRANSFER:
|
|
if ( pControl )
|
|
{
|
|
CDlgGetAddress dlg;
|
|
if ( dlg.DoModal(GetActiveWindow()) == IDOK )
|
|
{
|
|
CErrorInfo er( IDS_ER_CALL_TRANSFER, 0 );
|
|
hr = er.set_hr( pControl->BlindTransfer(dlg.m_bstrAddress) );
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
if ( SUCCEEDED(hr = pControl->Disconnect(DC_NORMAL)) )
|
|
hr = fire_CloseCallControl( lCallID );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CM_ACTIONS_CALLBACK:
|
|
if ( pControl )
|
|
{
|
|
CErrorInfo er( IDS_ER_SWAPHOLD, IDS_ER_SWAPHOLD_FIND_CANDIDATE );
|
|
IAVTapiCall *pAVCandidate;
|
|
if ( SUCCEEDED(hr = er.set_hr(GetSwapHoldCallCandidate(pAVCall, &pAVCandidate))) )
|
|
{
|
|
er.set_Details( IDS_ER_SWAPHOLD_EXECUTE );
|
|
ITBasicCallControl *pControlSwap;
|
|
if ( SUCCEEDED(pAVCandidate->get_ITBasicCallControl(&pControlSwap)) )
|
|
{
|
|
hr = er.set_hr( pControl->SwapHold(pControlSwap) );
|
|
pControlSwap->Release();
|
|
}
|
|
pAVCandidate->Release();
|
|
}
|
|
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
RELEASE( pInfo );
|
|
RELEASE( pControl );
|
|
|
|
// Clean up
|
|
pAVCall->Release();
|
|
SysFreeString( bstrText );
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::DigitPress(long lCallID, PhonePadKey nKey)
|
|
{
|
|
USES_CONVERSION;
|
|
HRESULT hr = S_OK;
|
|
|
|
// Convert the digit to the appropriate string.
|
|
BSTR bstrDigit = NULL;
|
|
switch ( nKey )
|
|
{
|
|
case PP_DTMF_STAR: bstrDigit = SysAllocString(L"*"); break;
|
|
case PP_DTMF_POUND: bstrDigit = SysAllocString(L"#"); break;
|
|
case PP_DTMF_0: bstrDigit = SysAllocString(L"0"); break;
|
|
|
|
default:
|
|
{
|
|
bstrDigit = SysAllocString(L"1");
|
|
|
|
//
|
|
// We have to verify the string allocation before we use it
|
|
//
|
|
if( IsBadStringPtr( bstrDigit, (UINT)-1) )
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
bstrDigit[0] += nKey;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We have to verify the string allocation before we use it
|
|
// We didn't verify for PP_DTMF_STAR, PP_DTMF_POUND and PP_DTMF_0
|
|
//
|
|
if( IsBadStringPtr( bstrDigit, (UINT)-1) )
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
ATLTRACE(_T(".enter.CAVTapi::DigitPress(id=%ld, digit=%d).\n"), lCallID, nKey );
|
|
|
|
// Do for all connected calls
|
|
AVTAPICALLLIST lstCalls;
|
|
GetAllCallsAtState( &lstCalls, CS_CONNECTED );
|
|
|
|
while ( !lstCalls.empty() )
|
|
{
|
|
IAVTapiCall *pAVCall = lstCalls.front();
|
|
lstCalls.pop_front();
|
|
|
|
ITBasicCallControl *pControl;
|
|
if ( SUCCEEDED(hr = pAVCall->get_ITBasicCallControl(&pControl)) )
|
|
{
|
|
// Generate the digits on the MediaControl
|
|
ITLegacyCallMediaControl *pMediaControl;
|
|
if ( SUCCEEDED(hr = pControl->QueryInterface(IID_ITLegacyCallMediaControl, (void **) &pMediaControl)) )
|
|
{
|
|
hr = pMediaControl->GenerateDigits( bstrDigit, 2 );
|
|
pMediaControl->Release();
|
|
}
|
|
pControl->Release();
|
|
}
|
|
|
|
// Release ref to call
|
|
pAVCall->Release();
|
|
}
|
|
|
|
// Clean up
|
|
SysFreeString( bstrDigit );
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CAVTapi::GetFirstCall( IAVTapiCall **ppAVCall )
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
// Grab first call on list
|
|
// $CRIT_ENTER
|
|
m_critLstAVTapiCalls.Lock();
|
|
if ( !m_lstAVTapiCalls.empty() )
|
|
{
|
|
*ppAVCall = m_lstAVTapiCalls.front();
|
|
(*ppAVCall)->AddRef();
|
|
hr = S_OK;
|
|
}
|
|
m_critLstAVTapiCalls.Unlock();
|
|
// $CRIT_EXIT
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
// CAVTapi::ShowMediaPreview( long lCallID, hWndParent, bVisible )
|
|
//
|
|
// Pass in -1 for the lCallID to search all calls!
|
|
// Pass in 0 to notify that the preview window is hidden
|
|
//
|
|
STDMETHODIMP CAVTapi::ShowMediaPreview(long lCallID, HWND hWndParent, BOOL bVisible)
|
|
{
|
|
ATLTRACE(_T(".enter.CAVTapi::ShowMediaPreview(%ld, %d).\n"), lCallID, bVisible );
|
|
HRESULT hr = E_NOINTERFACE;
|
|
IAVTapiCall *pAVCall = NULL;
|
|
IVideoWindow *pVideoPreview = NULL;
|
|
|
|
// Does the call selected support video?
|
|
if ( lCallID > 0 )
|
|
{
|
|
/// Selected a particular call
|
|
pAVCall = FindAVTapiCall( lCallID );
|
|
if ( pAVCall )
|
|
hr = pAVCall->get_IVideoWindowPreview( (IDispatch **) &pVideoPreview );
|
|
}
|
|
|
|
// ------------------------- Did we find a valid call
|
|
if ( SUCCEEDED(hr) && pVideoPreview )
|
|
SetVideoWindowProperties( pVideoPreview, hWndParent, bVisible );
|
|
|
|
RELEASE(pVideoPreview);
|
|
RELEASE(pAVCall);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::ShowMedia(long lCallID, HWND hWndParent, BOOL bVisible)
|
|
{
|
|
ATLTRACE(_T(".enter.CAVTapi::ShowMedia(%ld, %d).\n"), lCallID, bVisible );
|
|
HRESULT hr = E_NOINTERFACE;
|
|
if ( lCallID > 0 )
|
|
{
|
|
IAVTapiCall *pAVCall = FindAVTapiCall( lCallID );
|
|
if ( pAVCall )
|
|
{
|
|
if ( !bVisible || (pAVCall->IsRcvVideoStreaming() == S_OK) )
|
|
{
|
|
IVideoWindow *pVideoWindow;
|
|
if ( SUCCEEDED(hr = pAVCall->get_IVideoWindow(0, (IDispatch **) &pVideoWindow)) )
|
|
{
|
|
SetVideoWindowProperties( pVideoWindow, hWndParent, bVisible );
|
|
pVideoWindow->Release();
|
|
}
|
|
}
|
|
|
|
pAVCall->Release();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::get_dwCallCaps(long lCallID, DWORD * pVal)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
IAVTapiCall *pAVCall = FindAVTapiCall( lCallID );
|
|
if ( pAVCall )
|
|
{
|
|
hr = pAVCall->get_dwCaps( pVal );
|
|
pAVCall->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Event notification methods
|
|
|
|
STDMETHODIMP CAVTapi::CreateNewCall(ITAddress * pITAddress, IAVTapiCall * * ppAVCall)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
_ASSERT( pITAddress );
|
|
|
|
// Create and add call to call list
|
|
if ( (*ppAVCall = AddAVTapiCall(NULL, 0)) != NULL )
|
|
hr = S_OK;
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::fire_NewCall( ITAddress *pITAddress, DWORD dwAddressType, long lCallID, IDispatch *pDisp, AVCallType nType, IAVTapiCall **ppAVCallRet )
|
|
{
|
|
#ifdef _DEBUG
|
|
if ( nType == AV_DATA_CALL )
|
|
ATLTRACE(_T(".enter.CAVTapi::fire_NewCall() data call.\n"));
|
|
else
|
|
ATLTRACE(_T(".enter.CAVTapi::fire_NewCall() voice call.\n"));
|
|
#endif
|
|
_ASSERT( pITAddress );
|
|
|
|
// First attempt to get an ITBasicCallControl interface for the call
|
|
HRESULT hr = E_FAIL;
|
|
|
|
ITBasicCallControl *pITControl = NULL;
|
|
if ( !pDisp || SUCCEEDED(hr = pDisp->QueryInterface(IID_ITBasicCallControl, (void **) &pITControl)) )
|
|
{
|
|
// Make sure that we haven't already created a dialog for this call
|
|
IAVTapiCall *pAVCall = (pDisp) ? FindAVTapiCall( pITControl ) : NULL;
|
|
if ( !pAVCall )
|
|
{
|
|
// Create and add call to call list
|
|
long lAddressType = LINEADDRESSTYPE_IPADDRESS;
|
|
BSTR bstrAddressName = NULL;
|
|
pITAddress->get_AddressName( &bstrAddressName );
|
|
|
|
if ( lCallID || SUCCEEDED(hr = fire_NewCallWindow(&lCallID, ResolveMediaType(dwAddressType), bstrAddressName, nType)) )
|
|
{
|
|
pAVCall = AddAVTapiCall( pITControl, lCallID );
|
|
|
|
// If we fail to add to list, make sure we close the dialog
|
|
if ( !pAVCall )
|
|
{
|
|
fire_CloseCallControl( lCallID );
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
// Set the appropriate address type for the call
|
|
pAVCall->put_nCallType( nType );
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
SysFreeString( bstrAddressName );
|
|
}
|
|
|
|
// Return the call or release it
|
|
if ( ppAVCallRet )
|
|
*ppAVCallRet = pAVCall;
|
|
else
|
|
RELEASE( pAVCall );
|
|
|
|
if ( pITControl ) pITControl->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::fire_NewCallWindow(long *plCallID, CallManagerMedia cmm, BSTR bstrAddressName, AVCallType nType)
|
|
{
|
|
ATLTRACE(_T(".enter.CAVTapi::fire_NewCallWindow().\n") );
|
|
|
|
// Clean out existing call control windows if necessary
|
|
CloseExtraneousCallWindows();
|
|
|
|
// Do we have a different call type?
|
|
switch ( nType )
|
|
{
|
|
case AV_DATA_CALL:
|
|
if ( cmm == CM_MEDIA_INTERNET )
|
|
cmm = CM_MEDIA_INTERNETDATA;
|
|
break;
|
|
}
|
|
|
|
FIRE_VECTOR( NewCall(plCallID, cmm, bstrAddressName));
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::fire_SetCallerID(long lCallID, BSTR bstrCallerID)
|
|
{
|
|
ATLTRACE(_T(".enter.CAVTapi::fire_SetCallerID(%ld).\n"), lCallID );
|
|
BAIL_ON_DATA_OR_CONFCALL;
|
|
FIRE_VECTOR( SetCallerID(lCallID, bstrCallerID));
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::fire_ClearCurrentActions(long lCallID)
|
|
{
|
|
ATLTRACE(_T(".enter.CAVTapi::fire_ClearCurrentActions(%ld).\n"), lCallID );
|
|
BAIL_ON_DATA_OR_CONFCALL;
|
|
FIRE_VECTOR( ClearCurrentActions(lCallID));
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::fire_AddCurrentAction(long lCallID, CallManagerActions cma, BSTR bstrText)
|
|
{
|
|
ATLTRACE(_T(".enter.CAVTapi::fire_AddCurrentAction(%ld,cma=%d).\n"), lCallID, cma );
|
|
BAIL_ON_DATA_OR_CONFCALL;
|
|
FIRE_VECTOR( AddCurrentAction(lCallID, cma, bstrText));
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::fire_SetCallState(long lCallID, ITCallStateEvent *pEvent, IAVTapiCall *pAVCall )
|
|
{
|
|
ATLTRACE(_T(".enter.CAVTapi::fire_SetCallState(ID=%ld, pEvent=%p, pAVCall=%p).\n"), lCallID, pEvent, pAVCall );
|
|
#define IF_ADD_ACTION(_LC_, _CMA_) \
|
|
if ( (lCaps & (_LC_)) != 0 ) \
|
|
fire_AddCurrentAction( lCallID, (_CMA_), NULL );
|
|
|
|
#undef FETCH_STRING
|
|
#define FETCH_STRING(_CMS_, _IDS_) \
|
|
cms = _CMS_; \
|
|
LoadString( _Module.GetResourceInstance(), _IDS_, szText, ARRAYSIZE(szText) ); \
|
|
SysReAllocString( &bstrText, T2COLE(szText) );
|
|
|
|
USES_CONVERSION;
|
|
CallManagerStates cms = CM_STATES_UNKNOWN;
|
|
BSTR bstrText = NULL;
|
|
TCHAR szText[255];
|
|
|
|
if ( pEvent )
|
|
{
|
|
CALL_STATE callState;
|
|
CALL_STATE_EVENT_CAUSE nCec = CEC_NONE;
|
|
|
|
pEvent->get_State( &callState );
|
|
pEvent->get_Cause( &nCec );
|
|
|
|
// Setup incoming call dialog with appropriate information
|
|
CALL_STATE csPrev = CS_IDLE;
|
|
pAVCall->get_callState( &csPrev );
|
|
|
|
// Only update if state has changed
|
|
if ( csPrev != callState )
|
|
{
|
|
AVCallType nCallType;
|
|
pAVCall->get_nCallType( &nCallType );
|
|
|
|
// Update call's state
|
|
pAVCall->put_callState( callState );
|
|
|
|
// Clear out for new actions based on call state
|
|
fire_ClearCurrentActions( lCallID );
|
|
|
|
// Get Address Caps
|
|
long lCaps = 0;
|
|
ITAddress *pITAddress;
|
|
if ( SUCCEEDED(pAVCall->get_ITAddress(&pITAddress)) )
|
|
{
|
|
ITAddressCapabilities *pCaps;
|
|
if ( SUCCEEDED(pITAddress->QueryInterface(IID_ITAddressCapabilities, (void **) &pCaps)) )
|
|
{
|
|
pCaps->get_AddressCapability( AC_CALLFEATURES1, &lCaps );
|
|
pCaps->Release();
|
|
}
|
|
pITAddress->Release();
|
|
}
|
|
|
|
switch ( callState )
|
|
{
|
|
// Inbound call
|
|
case CS_OFFERING:
|
|
{
|
|
ATLTRACE(_T(".1.CAVTapi::fire_SetCallState(CS_OFFERING).\n"));
|
|
pAVCall->put_nCallLogType( CL_CALL_INCOMING );
|
|
fire_AddCurrentAction( lCallID, CM_ACTIONS_TAKECALL, NULL );
|
|
fire_AddCurrentAction( lCallID, CM_ACTIONS_REJECTCALL, NULL );
|
|
|
|
ITCallInfo* pCallInfo = NULL;
|
|
HRESULT hr = pAVCall->get_ITCallInfo( &pCallInfo );
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
USBOffering( pCallInfo );
|
|
pCallInfo->Release();
|
|
}
|
|
|
|
FETCH_STRING( CM_STATES_OFFERING, IDS_PLACECALL_OFFERING );
|
|
}
|
|
break;
|
|
|
|
case CS_INPROGRESS:
|
|
{
|
|
ATLTRACE(_T(".1.CAVTapi::fire_SetCallState(CS_INPROGRESS).\n"));
|
|
fire_AddCurrentAction( lCallID, CM_ACTIONS_DISCONNECT, NULL );
|
|
FETCH_STRING( CM_STATES_RINGING, IDS_PLACECALL_INPROGRESS );
|
|
|
|
ITCallInfo* pCallInfo = NULL;
|
|
HRESULT hr = pAVCall->get_ITCallInfo( &pCallInfo );
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
USBInprogress( pCallInfo );
|
|
pCallInfo->Release();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CS_CONNECTED:
|
|
ATLTRACE(_T(".1.CAVTapi::fire_SetCallState(CS_CONNECTED).\n"));
|
|
// Only do something if we don't want to disconnect
|
|
if ( SUCCEEDED(pAVCall->CheckKillMe()) )
|
|
{
|
|
DWORD dwAddressType = 0;
|
|
pAVCall->get_dwAddressType( &dwAddressType );
|
|
if ( (dwAddressType & LINEADDRESSTYPE_SDP) == NULL )
|
|
{
|
|
// Normal call, connect as usual
|
|
IF_ADD_ACTION( LINECALLFEATURE_HOLD, CM_ACTIONS_HOLD );
|
|
#ifdef _BAKEOFF
|
|
// If there are any calls on hold, show the swap hold button.
|
|
IAVTapiCall *pAVCandidate;
|
|
if ( SUCCEEDED(GetSwapHoldCallCandidate(pAVCall, &pAVCandidate)) )
|
|
{
|
|
IF_ADD_ACTION( LINECALLFEATURE_SWAPHOLD, CM_ACTIONS_CALLBACK );
|
|
pAVCandidate->Release();
|
|
}
|
|
IF_ADD_ACTION( LINECALLFEATURE_BLINDTRANSFER, CM_ACTIONS_TRANSFER );
|
|
#endif
|
|
|
|
fire_AddCurrentAction( lCallID, CM_ACTIONS_DISCONNECT, NULL );
|
|
cms = CM_STATES_CONNECTED;
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case CS_HOLD:
|
|
ATLTRACE(_T(".1.CAVTapi::fire_SetCallState(CS_HOLD).\n"));
|
|
fire_AddCurrentAction( lCallID, CM_ACTIONS_TAKECALL, NULL );
|
|
cms = CM_STATES_HOLDING;
|
|
break;
|
|
|
|
case CS_DISCONNECTED:
|
|
{
|
|
bool bClearCall = false;
|
|
|
|
// Hide video windows, and stop all streaming
|
|
ShowMedia( lCallID, NULL, FALSE );
|
|
ShowMediaPreview( lCallID, NULL, FALSE );
|
|
fire_AddCurrentAction( lCallID, CM_ACTIONS_NOTIFY_PREVIEW_STOP, NULL );
|
|
fire_AddCurrentAction( lCallID, CM_ACTIONS_NOTIFY_STREAMSTOP, NULL );
|
|
|
|
USBDisconnected( lCallID );
|
|
|
|
// Log all calls that go to the disconnected state
|
|
pAVCall->Log( CL_UNKNOWN );
|
|
|
|
// Auto close, closes the slider window for all calls
|
|
VARIANT_BOOL bAutoClose = false;
|
|
get_bAutoCloseCalls( &bAutoClose );
|
|
|
|
ATLTRACE(_T(".1.CAVTapi::fire_SetCallState(CS_DISCONNECTED).\n"));
|
|
|
|
if ( (bAutoClose || FAILED(pAVCall->CheckKillMe()) || (nCallType == AV_DATA_CALL) ) && SUCCEEDED(fire_CloseCallControl(lCallID)) )
|
|
{
|
|
bClearCall = true;
|
|
}
|
|
else
|
|
{
|
|
// Want to leave call visible because the remote party hung up
|
|
if ( SUCCEEDED(pAVCall->Disconnect(FALSE)) )
|
|
{
|
|
bClearCall = true;
|
|
fire_AddCurrentAction( lCallID, CM_ACTIONS_CLOSE, NULL );
|
|
switch ( nCec )
|
|
{
|
|
case CEC_DISCONNECT_BUSY: FETCH_STRING( CM_STATES_BUSY, IDS_PLACECALL_DISCONNECT_BUSY ); break;
|
|
case CEC_DISCONNECT_NOANSWER: FETCH_STRING( CM_STATES_UNAVAILABLE, IDS_PLACECALL_DISCONNECT_NOANSWER); break;
|
|
case CEC_DISCONNECT_REJECTED: FETCH_STRING( CM_STATES_UNAVAILABLE, IDS_PLACECALL_DISCONNECT_REJECTED); break;
|
|
case CEC_DISCONNECT_BADADDRESS: FETCH_STRING( CM_STATES_UNAVAILABLE, IDS_PLACECALL_DISCONNECT_BADADDRESS); break;
|
|
case CEC_DISCONNECT_CANCELLED: FETCH_STRING( CM_STATES_UNAVAILABLE, IDS_PLACECALL_DISCONNECT_CANCELLED); break;
|
|
case CEC_DISCONNECT_FAILED: FETCH_STRING( CM_STATES_UNAVAILABLE, IDS_PLACECALL_DISCONNECT_FAILED); break;
|
|
// Normal
|
|
default: cms = CM_STATES_DISCONNECTED; break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove all ref's to the call
|
|
if ( bClearCall )
|
|
{
|
|
ITBasicCallControl *pControl = NULL;
|
|
if ( SUCCEEDED(pAVCall->get_ITBasicCallControl(&pControl)) )
|
|
{
|
|
RemoveAVTapiCall( pControl );
|
|
pControl->Release();
|
|
}
|
|
else
|
|
{
|
|
RemoveAVTapiCall( NULL );
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Notify application of call state changing
|
|
ATLTRACE(_T(".exit.CAVTapi::fire_SetCallState() -- preparing to bail.\n") );
|
|
BAIL_ON_DATA_OR_CONFCALL;
|
|
if ( cms != CM_STATES_UNKNOWN )
|
|
{
|
|
ATLTRACE(_T(".enter.CAVTapi::fire_SetCallState(%ld, cms=%d).\n"), lCallID, cms );
|
|
FIRE_VECTOR( SetCallState(lCallID, cms, bstrText));
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::fire_CloseCallControl(long lCallID)
|
|
{
|
|
BAIL_ON_CONFCALL;
|
|
ATLTRACE(_T(".enter.CAVTapi::fire_CloseCallControl(%ld).\n"), lCallID );
|
|
FIRE_VECTOR( CloseCallControl(lCallID));
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::fire_ErrorNotify(long * pErrorInfo)
|
|
{
|
|
ATLTRACE(_T(".enter.CAVTapi::fire_ErrorInfo().\n") );
|
|
_ASSERT( pErrorInfo );
|
|
CErrorInfo *pEr = (CErrorInfo *) pErrorInfo;
|
|
#ifdef _DEBUG
|
|
USES_CONVERSION;
|
|
ATLTRACE(_T(".1.\tOperation: %s\n"), OLE2CT(pEr->m_bstrOperation) );
|
|
ATLTRACE(_T(".1.\tDetails : %s\n"), OLE2CT(pEr->m_bstrDetails) );
|
|
ATLTRACE(_T(".1.\tHRESULT : 0x%08lx\n"), pEr->m_hr );
|
|
#endif
|
|
FIRE_VECTOR( ErrorNotify( pEr->m_bstrOperation, pEr->m_bstrDetails, pEr->m_hr ) );
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::fire_SetCallState_CMS(long lCallID, CallManagerStates cms, BSTR bstrText)
|
|
{
|
|
ATLTRACE(_T(".enter.CAVTapi::fire_SetCallState_CMS(%ld,cms=%d).\n"), lCallID, cms );
|
|
BAIL_ON_DATA_OR_CONFCALL;
|
|
FIRE_VECTOR( SetCallState(lCallID, cms, bstrText));
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::fire_ActionSelected(CallClientActions cca)
|
|
{
|
|
ATLTRACE(_T(".enter.CAVTapi::fire_ActionSelected(%d).\n"), cca );
|
|
FIRE_VECTOR( ActionSelected(cca));
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::fire_LogCall(long lCallID, CallLogType nType, DATE dateStart, DATE dateEnd, BSTR bstrAddr, BSTR bstrName)
|
|
{
|
|
ATLTRACE(_T(".enter.CAVTapi::fire_LogCall(%d).\n"), nType );
|
|
FIRE_VECTOR( LogCall(lCallID, nType, dateStart, dateEnd, bstrAddr, bstrName));
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::fire_NotifyUserUserInfo(long lCallID, ULONG_PTR hMem)
|
|
{
|
|
ATLTRACE(_T(".enter.CAVTapi::fire_NotifyUserUserInfo(%p).\n"), hMem );
|
|
FIRE_VECTOR( NotifyUserUserInfo(lCallID, hMem));
|
|
}
|
|
|
|
|
|
#include "PageAddress.h"
|
|
|
|
STDMETHODIMP CAVTapi::ShowOptions()
|
|
{
|
|
USES_CONVERSION;
|
|
IUnknown *pUnk = GetUnknown();
|
|
|
|
#ifdef _NEWPROPS
|
|
CPageConf::s_nCount = 0;
|
|
|
|
CLSID clsidPages[4];
|
|
clsidPages[0] = CLSID_PageGeneral;
|
|
clsidPages[1] = CLSID_PageConf;
|
|
clsidPages[2] = CLSID_PageConf;
|
|
clsidPages[3] = CLSID_PageConf;
|
|
#else
|
|
CLSID clsidPages[2];
|
|
clsidPages[0] = CLSID_PageAddress;
|
|
clsidPages[1] = CLSID_PageTerminals;
|
|
#endif
|
|
|
|
TCHAR szTitle[255];
|
|
LoadString( _Module.GetResourceInstance(), IDS_OPTIONS_TITLE, szTitle, ARRAYSIZE(szTitle) );
|
|
|
|
HRESULT hr;
|
|
hr = OleCreatePropertyFrame( _Module.GetParentWnd(), 100, 100, T2COLE(szTitle),
|
|
1, &pUnk, // Objects being invoked on behalf of
|
|
ARRAYSIZE(clsidPages), clsidPages, // Property pages
|
|
LOCALE_USER_DEFAULT, // System locale
|
|
0, NULL ); // Reserved
|
|
|
|
//
|
|
// Read the new audio echo cancellation flag
|
|
//
|
|
|
|
m_bAEC = AECGetRegistryValue();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// Static helper functions
|
|
void CAVTapi::SetVideoWindowProperties( IVideoWindow *pVideoWindow, HWND hWndParent, BOOL bVisible )
|
|
{
|
|
ATLTRACE(_T(".enter.SetVideoWindowProperties(%p, %d).\n"), pVideoWindow, bVisible );
|
|
|
|
HWND hWndTemp;
|
|
if ( SUCCEEDED(pVideoWindow->get_Owner((OAHWND FAR*) &hWndTemp)) )
|
|
{
|
|
if ( hWndParent )
|
|
{
|
|
// We have a pointer to the VideoWindow, now set up the parent and stuff
|
|
pVideoWindow->put_Owner((ULONG_PTR) hWndParent);
|
|
pVideoWindow->put_MessageDrain((ULONG_PTR) hWndParent);
|
|
pVideoWindow->put_WindowStyle(WS_CHILD | WS_BORDER);
|
|
|
|
// Drop video onto call control
|
|
RECT rc;
|
|
GetClientRect( hWndParent, &rc );
|
|
pVideoWindow->SetWindowPosition(rc.left, rc.top, rc.right, rc.bottom);
|
|
pVideoWindow->put_AutoShow( (bVisible) ? OATRUE : OAFALSE );
|
|
pVideoWindow->put_Visible( (bVisible) ? OATRUE : OAFALSE );
|
|
}
|
|
else
|
|
{
|
|
// Release ownership of window
|
|
pVideoWindow->put_AutoShow( OAFALSE );
|
|
pVideoWindow->put_Visible( OAFALSE );
|
|
pVideoWindow->put_Owner( NULL );
|
|
pVideoWindow->put_MessageDrain( NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::PopulateAddressDialog(DWORD *pdwPreferred, HWND hWndPots, HWND hWndIP, HWND hWndConf)
|
|
{
|
|
_ASSERT( IsWindow(hWndPots) && IsWindow(hWndIP) && IsWindow(hWndConf) );
|
|
_ASSERT( pdwPreferred );
|
|
|
|
// Is TAPI running?
|
|
_ASSERT(m_pITTapi);
|
|
if ( !m_pITTapi ) return E_PENDING;
|
|
|
|
// Enumerate through addresses, adding them to each listbox
|
|
USES_CONVERSION;
|
|
HRESULT hr;
|
|
IEnumAddress *pEnumAddresses;
|
|
if ( FAILED(hr = m_pITTapi->EnumerateAddresses(&pEnumAddresses)) ) return hr;
|
|
|
|
HCURSOR hCurOld = SetCursor( LoadCursor(NULL, IDC_APPSTARTING) );
|
|
|
|
// Retrieve preferred media type
|
|
get_dwPreferredMedia( pdwPreferred );
|
|
|
|
CMyAddressID *pMyID;
|
|
ITAddress *pITAddress;
|
|
while ( pEnumAddresses->Next(1, &pITAddress, NULL) == S_OK )
|
|
{
|
|
ITMediaSupport *pITMediaSupport;
|
|
if ( SUCCEEDED(hr = pITAddress->QueryInterface(IID_ITMediaSupport, (void **) &pITMediaSupport)) )
|
|
{
|
|
// Must support audio in and out
|
|
VARIANT_BOOL bSupport;
|
|
if ( SUCCEEDED(pITMediaSupport->QueryMediaType(TAPIMEDIATYPE_AUDIO, &bSupport)) && bSupport )
|
|
{
|
|
// Determine the types of media the address supports
|
|
ITAddressCapabilities *pCaps;
|
|
if ( SUCCEEDED(pITAddress->QueryInterface(IID_ITAddressCapabilities, (void **) &pCaps)) )
|
|
{
|
|
BSTR bstrName = NULL;
|
|
pITAddress->get_AddressName( &bstrName );
|
|
|
|
if ( bstrName && (SysStringLen(bstrName) > 0) )
|
|
{
|
|
long lAddrTypes = 0;
|
|
pCaps->get_AddressCapability( AC_ADDRESSTYPES, &lAddrTypes );
|
|
|
|
for ( int i = 0; i < 3; i++ )
|
|
{
|
|
HWND hWnd = NULL;
|
|
switch ( i )
|
|
{
|
|
// Multicast Conferences
|
|
case 0:
|
|
if ( (lAddrTypes & LINEADDRESSTYPE_SDP) != NULL )
|
|
hWnd = hWndConf;
|
|
break;
|
|
|
|
// Phone Calls
|
|
case 1:
|
|
if ( (lAddrTypes & LINEADDRESSTYPE_PHONENUMBER) != NULL )
|
|
hWnd = hWndPots;
|
|
break;
|
|
|
|
// Network based calls
|
|
case 2:
|
|
if ( (lAddrTypes & LINEADDRESSTYPE_NETCALLS) != NULL )
|
|
hWnd = hWndIP;
|
|
break;
|
|
}
|
|
|
|
// Do we have something to add?
|
|
if ( hWnd )
|
|
{
|
|
int nInd = SendMessage( hWnd, CB_ADDSTRING, 0, (LPARAM) OLE2CT(bstrName) );
|
|
if ( nInd >= 0 )
|
|
{
|
|
pMyID = new CMyAddressID;
|
|
if ( pMyID )
|
|
{
|
|
pCaps->get_AddressCapability( AC_PERMANENTDEVICEID, &pMyID->m_lPermID );
|
|
pCaps->get_AddressCapability( AC_ADDRESSID, &pMyID->m_lAddrID );
|
|
SendMessage( hWnd, CB_SETITEMDATA, nInd, (LPARAM) pMyID );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
SysFreeString( bstrName );
|
|
pCaps->Release();
|
|
}
|
|
}
|
|
pITMediaSupport->Release();
|
|
}
|
|
pITAddress->Release();
|
|
}
|
|
pEnumAddresses->Release();
|
|
|
|
// Add default for all lists
|
|
TCHAR szText[255];
|
|
CRegKey regKey;
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_KEY, szText, ARRAYSIZE(szText) );
|
|
regKey.Open( HKEY_CURRENT_USER, szText, KEY_READ );
|
|
DWORD dwAddrType[] = { LINEADDRESSTYPE_PHONENUMBER, LINEADDRESSTYPE_IPADDRESS, LINEADDRESSTYPE_SDP };
|
|
|
|
HWND hWndTemp[] = { hWndPots, hWndIP, hWndConf };
|
|
for ( int i = 0; i < ARRAYSIZE(hWndTemp); i++ )
|
|
{
|
|
// Selecte a default list item for the combo box
|
|
UINT nIDS = IDS_DEFAULT_LINENAME;
|
|
if ( !SendMessage(hWndTemp[i], CB_GETCOUNT, 0, 0) )
|
|
{
|
|
::EnableWindow( hWndTemp[i], false );
|
|
nIDS = IDS_NO_LINES;
|
|
}
|
|
|
|
// Add item to list
|
|
if ( nIDS )
|
|
{
|
|
LoadString( _Module.GetResourceInstance(), nIDS, szText, ARRAYSIZE(szText) );
|
|
int nInd = SendMessage( hWndTemp[i], CB_INSERTSTRING, 0, (LPARAM) szText );
|
|
if ( nInd >= 0 )
|
|
SendMessage( hWndTemp[i], CB_SETITEMDATA, 0, 0 );
|
|
}
|
|
|
|
// Retrieve previously selected item from registry
|
|
DWORD dwPermID = 0, dwAddrID;
|
|
if ( regKey.m_hKey )
|
|
{
|
|
LoadString( _Module.GetResourceInstance(), AddressTypeToRegKey(dwAddrType[i], true), szText, ARRAYSIZE(szText) );
|
|
regKey.QueryValue( dwPermID, szText );
|
|
|
|
LoadString( _Module.GetResourceInstance(), AddressTypeToRegKey(dwAddrType[i], false), szText, ARRAYSIZE(szText) );
|
|
regKey.QueryValue( dwAddrID, szText );
|
|
}
|
|
|
|
// Look for item in the listbox
|
|
int nCurSel = 0;
|
|
if ( dwPermID )
|
|
{
|
|
int nCount = SendMessage( hWndTemp[i], CB_GETCOUNT, 0, 0 );
|
|
for ( int j = 0; j < nCount; j++ )
|
|
{
|
|
pMyID = (CMyAddressID *) SendMessage(hWndTemp[i], CB_GETITEMDATA, j, 0);
|
|
if ( pMyID && ((DWORD) pMyID->m_lPermID == dwPermID) && ((DWORD) pMyID->m_lAddrID == dwAddrID) )
|
|
{
|
|
nCurSel = j;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Line device no longer exists
|
|
if ( !nCurSel )
|
|
{
|
|
// Add temporary place holder
|
|
pMyID = new CMyAddressID;
|
|
if ( pMyID )
|
|
{
|
|
pMyID->m_lPermID = dwPermID;
|
|
pMyID->m_lAddrID = dwAddrID;
|
|
|
|
LoadString( _Module.GetResourceInstance(), IDS_LINENOTFOUND, szText, ARRAYSIZE(szText) );
|
|
int nInd = SendMessage( hWndTemp[i], CB_INSERTSTRING, 0, (LPARAM) szText );
|
|
if ( nInd >= 0 )
|
|
SendMessage( hWndTemp[i], CB_SETITEMDATA, 0, (LPARAM) pMyID );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Select item in the combo box
|
|
SendMessage( hWndTemp[i], CB_SETCURSEL, nCurSel, 0 );
|
|
}
|
|
|
|
// Restore the cursor
|
|
SetCursor( hCurOld );
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::PopulateTerminalsDialog(DWORD dwAddressType, HWND *phWnd)
|
|
{
|
|
USES_CONVERSION;
|
|
HRESULT hr = S_OK;
|
|
bool bFoundAddress = false;
|
|
|
|
int i;
|
|
|
|
HCURSOR hCurOld = SetCursor( LoadCursor(NULL, IDC_APPSTARTING) );
|
|
|
|
// Clear out the lists
|
|
for ( i = 0; i < NUM_CB_TERMINALS; i++ )
|
|
{
|
|
SendMessage( phWnd[i], CB_RESETCONTENT, 0, 0 );
|
|
EnableWindow( phWnd[i], true );
|
|
}
|
|
|
|
// Resolve address type to an active address
|
|
ITAddress *pITAddress;
|
|
if ( SUCCEEDED(hr = GetAddress(dwAddressType, false, &pITAddress)) )
|
|
{
|
|
HRESULT hrTemp;
|
|
bFoundAddress = true;
|
|
|
|
// Get terminals supported by address
|
|
ITTerminalSupport *pITTerminalSupport;
|
|
if ( SUCCEEDED(hrTemp = pITAddress->QueryInterface(IID_ITTerminalSupport, (void **) &pITTerminalSupport)) )
|
|
{
|
|
IEnumTerminal *pEnumTerminal;
|
|
if ( (hrTemp = pITTerminalSupport->EnumerateStaticTerminals(&pEnumTerminal)) == S_OK )
|
|
{
|
|
// What type of terminal do we have? (audio in, audio out, video in, etc.)
|
|
ITTerminal *pITTerminal;
|
|
while ( pEnumTerminal->Next(1, &pITTerminal, NULL) == S_OK )
|
|
{
|
|
TERMINAL_DIRECTION nTD;
|
|
long nTerminalType;
|
|
// Render or Capture?
|
|
if ( SUCCEEDED(pITTerminal->get_Direction(&nTD)) )
|
|
{
|
|
// Audio or Video?
|
|
BSTR bstrName = NULL;
|
|
HWND hWnd = NULL;
|
|
|
|
pITTerminal->get_Name( &bstrName );
|
|
if ( bstrName && (SysStringLen(bstrName) > 0) )
|
|
{
|
|
pITTerminal->get_MediaType( &nTerminalType );
|
|
|
|
switch ( nTerminalType )
|
|
{
|
|
// ------------------
|
|
case TAPIMEDIATYPE_VIDEO:
|
|
if ( nTD == TD_CAPTURE )
|
|
hWnd = phWnd[VIDEO_CAPTURE];
|
|
break;
|
|
|
|
// ------------
|
|
case TAPIMEDIATYPE_AUDIO:
|
|
hWnd = (nTD == TD_CAPTURE) ? phWnd[AUDIO_CAPTURE] : phWnd[AUDIO_RENDER];
|
|
break;
|
|
}
|
|
|
|
// Add item to appropriate listbox
|
|
if ( hWnd )
|
|
SendMessage( hWnd, CB_ADDSTRING, 0, (LPARAM) OLE2CT(bstrName) );
|
|
}
|
|
|
|
// Clean up
|
|
SysFreeString( bstrName );
|
|
}
|
|
// Clean up
|
|
pITTerminal->Release();
|
|
}
|
|
pEnumTerminal->Release();
|
|
}
|
|
|
|
// Does the address support video render?
|
|
if ( phWnd[VIDEO_RENDER] )
|
|
{
|
|
bool bSupported = false;
|
|
|
|
IEnumTerminalClass *pEnumClass = NULL;
|
|
if ( SUCCEEDED(pITTerminalSupport->EnumerateDynamicTerminalClasses(&pEnumClass)) && pEnumClass )
|
|
{
|
|
GUID guidTerminal;
|
|
while ( pEnumClass->Next(1, &guidTerminal, NULL) == S_OK )
|
|
{
|
|
if ( guidTerminal == CLSID_VideoWindowTerm )
|
|
{
|
|
bSupported = true;
|
|
break;
|
|
}
|
|
}
|
|
pEnumClass->Release();
|
|
}
|
|
EnableWindow( phWnd[VIDEO_RENDER], bSupported );
|
|
}
|
|
|
|
// Clean up
|
|
pITTerminalSupport->Release();
|
|
}
|
|
pITAddress->Release();
|
|
}
|
|
else
|
|
{
|
|
// Disable video playback
|
|
if ( phWnd[VIDEO_RENDER] )
|
|
EnableWindow( phWnd[VIDEO_RENDER], false );
|
|
}
|
|
|
|
|
|
// Add default for all lists
|
|
CRegKey regKey;
|
|
UINT nIDS_Key[] = { IDN_REG_REDIAL_TERMINAL_AUDIO_CAPTURE, IDN_REG_REDIAL_TERMINAL_AUDIO_RENDER, IDN_REG_REDIAL_TERMINAL_VIDEO_CAPTURE };
|
|
|
|
// Open the key for the addresstype
|
|
TCHAR szText[255], szType[255];
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_KEY, szText, ARRAYSIZE(szText) );
|
|
LoadString( _Module.GetResourceInstance(), AddressTypeToRegKey(dwAddressType, true), szType, ARRAYSIZE(szType) );
|
|
_tcscat( szText, _T("\\") );
|
|
_tcscat( szText, szType );
|
|
regKey.Open( HKEY_CURRENT_USER, szText, KEY_READ );
|
|
|
|
// Load audio and video terminal settings
|
|
for ( i = 0; i < NUM_CB_TERMINALS; i++ )
|
|
{
|
|
// Selecte a default list item for the combo box
|
|
UINT nIDS = IDS_PREFERRED_DEVICE;
|
|
if ( !SendMessage(phWnd[i], CB_GETCOUNT, 0, 0) )
|
|
{
|
|
::EnableWindow( phWnd[i], false );
|
|
nIDS = (bFoundAddress) ? IDS_NO_DEVICES : IDS_NO_LINE_SUPPORTING_CALL_TYPE;
|
|
}
|
|
|
|
// Add item to list
|
|
if ( nIDS )
|
|
{
|
|
// Do we want to give them the capability of selecting no terminal?
|
|
if ( nIDS == IDS_PREFERRED_DEVICE )
|
|
{
|
|
LoadString( _Module.GetResourceInstance(), IDS_NONE_DEVICE, szText, ARRAYSIZE(szText) );
|
|
SendMessage( phWnd[i], CB_INSERTSTRING, 0, (LPARAM) szText );
|
|
}
|
|
|
|
LoadString( _Module.GetResourceInstance(), nIDS, szText, ARRAYSIZE(szText) );
|
|
SendMessage( phWnd[i], CB_INSERTSTRING, 0, (LPARAM) szText );
|
|
}
|
|
|
|
// Select an item
|
|
int nCurSel = 0;
|
|
if ( regKey.m_hKey )
|
|
{
|
|
LoadString( _Module.GetResourceInstance(), nIDS_Key[i], szText, ARRAYSIZE(szText) );
|
|
DWORD dwCount = ARRAYSIZE(szType) - 1;
|
|
if ( (regKey.QueryValue(szType, szText, &dwCount) == ERROR_SUCCESS) && (dwCount > 0) )
|
|
nCurSel = SendMessage( phWnd[i], CB_FINDSTRINGEXACT, 1, (LPARAM) szType );
|
|
}
|
|
|
|
if ( nCurSel >= 0 )
|
|
{
|
|
SendMessage( phWnd[i], CB_SETCURSEL, nCurSel, 0 );
|
|
}
|
|
else
|
|
{
|
|
SendMessage( phWnd[i], CB_SETCURSEL, 0, 0 );
|
|
}
|
|
}
|
|
|
|
// Should we check show video windows?
|
|
SendMessage( phWnd[3], BM_SETCHECK, (WPARAM) (CanCreateVideoWindows(dwAddressType) == S_OK), 0 );
|
|
|
|
// Max video windows
|
|
if ( dwAddressType == LINEADDRESSTYPE_SDP )
|
|
{
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_TERMINAL_MAX_VIDEO, szText, ARRAYSIZE(szText) );
|
|
IConfRoom *pConfRoom;
|
|
if ( SUCCEEDED(get_ConfRoom(&pConfRoom)) )
|
|
{
|
|
short nMax;
|
|
pConfRoom->get_nMaxTerms( &nMax );
|
|
|
|
DWORD dwTemp = nMax;
|
|
regKey.QueryValue( dwTemp, szText );
|
|
dwTemp = min( MAX_VIDEO, max(1, dwTemp) );
|
|
|
|
TCHAR szText[100];
|
|
_ltot( dwTemp, szText, 10 );
|
|
SetWindowText( phWnd[4], szText );
|
|
|
|
pConfRoom->Release();
|
|
}
|
|
}
|
|
|
|
SetCursor( hCurOld );
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::UnpopulateAddressDialog(DWORD dwPreferred, HWND hWndPOTS, HWND hWndIP, HWND hWndConf)
|
|
{
|
|
// Store preferred device
|
|
put_dwPreferredMedia( dwPreferred );
|
|
|
|
// Store selected provider for each line
|
|
HWND hWnd[] = { hWndPOTS, hWndIP, hWndConf };
|
|
|
|
CRegKey regKey;
|
|
TCHAR szText[255];
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_KEY, szText, ARRAYSIZE(szText) );
|
|
if ( regKey.Create(HKEY_CURRENT_USER, szText) == ERROR_SUCCESS )
|
|
{
|
|
CMyAddressID *pMyID;
|
|
DWORD arAddr[] = { LINEADDRESSTYPE_PHONENUMBER, LINEADDRESSTYPE_IPADDRESS, LINEADDRESSTYPE_SDP };
|
|
_ASSERT( ARRAYSIZE(hWnd) == ARRAYSIZE(arAddr) );
|
|
|
|
// Write provider ID's out to the registry
|
|
for ( int i = 0; i < ARRAYSIZE(hWnd); i++ )
|
|
{
|
|
_ASSERT( IsWindow(hWnd[i]) );
|
|
int nSel = (int) SendMessage( hWnd[i], CB_GETCURSEL, 0, 0 );
|
|
if ( nSel >= 0 )
|
|
{
|
|
pMyID = (CMyAddressID *) SendMessage(hWnd[i], CB_GETITEMDATA, nSel, 0);
|
|
|
|
LoadString( _Module.GetResourceInstance(), AddressTypeToRegKey(arAddr[i], true), szText, ARRAYSIZE(szText) );
|
|
regKey.SetValue( (pMyID) ? pMyID->m_lPermID : 0, szText );
|
|
|
|
LoadString( _Module.GetResourceInstance(), AddressTypeToRegKey(arAddr[i], false), szText, ARRAYSIZE(szText) );
|
|
regKey.SetValue( (pMyID) ? pMyID->m_lAddrID : 0, szText );
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::UnpopulateTerminalsDialog(DWORD dwAddressType, HWND *phWnd)
|
|
{
|
|
UINT nIDS_Key[] = { IDN_REG_REDIAL_TERMINAL_AUDIO_CAPTURE, IDN_REG_REDIAL_TERMINAL_AUDIO_RENDER, IDN_REG_REDIAL_TERMINAL_VIDEO_CAPTURE };
|
|
|
|
// Create the registry key, its a combination of redial and
|
|
CRegKey regKey;
|
|
TCHAR szText[255], szType[50];
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_KEY, szText, ARRAYSIZE(szText) );
|
|
LoadString( _Module.GetResourceInstance(), AddressTypeToRegKey(dwAddressType, true), szType, ARRAYSIZE(szType) );
|
|
_tcscat( szText, _T("\\") );
|
|
_tcscat( szText, szType );
|
|
regKey.Create( HKEY_CURRENT_USER, szText );
|
|
|
|
// Store all terminals for audio in, audio out and video in
|
|
for ( int i = 0; i < NUM_CB_TERMINALS; i++ )
|
|
{
|
|
_ASSERT( IsWindow(phWnd[i]) );
|
|
|
|
// Store name of terminal in a registry key
|
|
if ( regKey.m_hKey )
|
|
{
|
|
LoadString( _Module.GetResourceInstance(), nIDS_Key[i], szText, ARRAYSIZE(szText) );
|
|
|
|
// What is selected? Preferred device or a specific one
|
|
bool bSetValue = false;
|
|
|
|
int nCurSel = SendMessage( phWnd[i], CB_GETCURSEL, 0, 0 );
|
|
if ( nCurSel > 0 )
|
|
{
|
|
int nSize = SendMessage(phWnd[i], CB_GETLBTEXTLEN, nCurSel, 0) + 1;
|
|
if ( nSize > 0 )
|
|
{
|
|
TCHAR *pszTerminal = new TCHAR[nSize];
|
|
if ( pszTerminal )
|
|
{
|
|
bSetValue = true;
|
|
SendMessage( phWnd[i], CB_GETLBTEXT, nCurSel, (LPARAM) pszTerminal );
|
|
regKey.SetValue( pszTerminal, szText );
|
|
delete pszTerminal;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clean out the entry
|
|
if ( !bSetValue )
|
|
regKey.DeleteValue( szText );
|
|
}
|
|
}
|
|
|
|
// Should we check show video windows?
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_TERMINAL_VIDEO_RENDER, szText, ARRAYSIZE(szText) );
|
|
DWORD dwTemp = SendMessage( phWnd[VIDEO_RENDER], BM_GETCHECK, 0, 0 );
|
|
regKey.SetValue( dwTemp, szText );
|
|
|
|
// Max video windows
|
|
if ( dwAddressType == LINEADDRESSTYPE_SDP )
|
|
{
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_TERMINAL_MAX_VIDEO, szText, ARRAYSIZE(szText) );
|
|
IConfRoom *pConfRoom;
|
|
if ( SUCCEEDED(get_ConfRoom(&pConfRoom)) )
|
|
{
|
|
TCHAR szNum[100];
|
|
GetWindowText( phWnd[4], szNum, ARRAYSIZE(szNum) - 1 );
|
|
dwTemp = _ttol( szNum );
|
|
dwTemp = min( MAX_VIDEO, max(1, dwTemp) );
|
|
|
|
pConfRoom->put_nMaxTerms( (short) dwTemp );
|
|
regKey.SetValue( dwTemp, szText );
|
|
|
|
pConfRoom->Release();
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::get_dwPreferredMedia(DWORD * pVal)
|
|
{
|
|
// Load preferred media type from registry
|
|
_ASSERT( pVal );
|
|
*pVal = LINEADDRESSTYPE_IPADDRESS; // set a default
|
|
|
|
CRegKey regKey;
|
|
TCHAR szTemp[255];
|
|
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_KEY, szTemp, ARRAYSIZE(szTemp) );
|
|
if ( regKey.Open(HKEY_CURRENT_USER, szTemp, KEY_READ) == ERROR_SUCCESS )
|
|
{
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_PREFERRED_MEDIA, szTemp, ARRAYSIZE(szTemp) );
|
|
regKey.QueryValue( *pVal, szTemp );
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::put_dwPreferredMedia(DWORD newVal)
|
|
{
|
|
// Save prefered media type to registry
|
|
CRegKey regKey;
|
|
TCHAR szTemp[255];
|
|
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_KEY, szTemp, ARRAYSIZE(szTemp) );
|
|
if ( regKey.Create(HKEY_CURRENT_USER, szTemp) == ERROR_SUCCESS )
|
|
{
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_PREFERRED_MEDIA, szTemp, ARRAYSIZE(szTemp) );
|
|
regKey.SetValue( newVal, szTemp );
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CAVTapi::GetAllCallsAtState( AVTAPICALLLIST *pList, CALL_STATE callState )
|
|
{
|
|
CALL_STATE nState;
|
|
|
|
// $CRIT_ENTER
|
|
m_critLstAVTapiCalls.Lock();
|
|
AVTAPICALLLIST::iterator i, iEnd = m_lstAVTapiCalls.end();
|
|
for ( i = m_lstAVTapiCalls.begin(); i != iEnd; i++ )
|
|
{
|
|
// If call states match, add to list
|
|
if ( SUCCEEDED((*i)->get_callState(&nState)) && (nState == callState) )
|
|
{
|
|
(*i)->AddRef();
|
|
pList->push_back( *i );
|
|
}
|
|
}
|
|
m_critLstAVTapiCalls.Unlock();
|
|
// $CRIT_EXIT
|
|
|
|
return (pList->empty()) ? E_FAIL : S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::FindAVTapiCallFromCallHub(ITCallHub * pCallHub, IAVTapiCall * * ppCall)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
*ppCall = NULL;
|
|
|
|
// $CRIT_ENTER
|
|
m_critLstAVTapiCalls.Lock();
|
|
AVTAPICALLLIST::iterator i, iEnd = m_lstAVTapiCalls.end();
|
|
for ( i = m_lstAVTapiCalls.begin(); i != iEnd; i++ )
|
|
{
|
|
if ( (*i)->IsSameCallHub(pCallHub) == S_OK )
|
|
{
|
|
*ppCall = (*i);
|
|
(*ppCall)->AddRef();
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
}
|
|
m_critLstAVTapiCalls.Unlock();
|
|
// $CRIT_EXIT
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::FindAVTapiCallFromCallInfo(ITCallInfo * pCallInfo, IAVTapiCall **ppCall)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
*ppCall = NULL;
|
|
|
|
// $CRIT_ENTER
|
|
m_critLstAVTapiCalls.Lock();
|
|
AVTAPICALLLIST::iterator i, iEnd = m_lstAVTapiCalls.end();
|
|
for ( i = m_lstAVTapiCalls.begin(); i != iEnd; i++ )
|
|
{
|
|
ITCallInfo *pMyCallInfo;
|
|
if ( SUCCEEDED((*i)->get_ITCallInfo(&pMyCallInfo)) )
|
|
{
|
|
if ( pCallInfo == pMyCallInfo )
|
|
{
|
|
*ppCall = (*i);
|
|
(*ppCall)->AddRef();
|
|
hr = S_OK;
|
|
}
|
|
|
|
pMyCallInfo->Release();
|
|
}
|
|
|
|
if ( SUCCEEDED(hr) ) break;
|
|
}
|
|
m_critLstAVTapiCalls.Unlock();
|
|
// $CRIT_EXIT
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CAVTapi::get_nNumCalls(long * pVal)
|
|
{
|
|
// m_critLstAVTapiCalls.Lock();
|
|
*pVal = m_lstAVTapiCalls.size();
|
|
// m_critLstAVTapiCalls.Unlock();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CAVTapi::FindAVTapiCallFromParticipant(ITParticipant * pParticipant, IAVTapiCall **ppCall)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
*ppCall = NULL;
|
|
|
|
// $CRIT_ENTER
|
|
m_critLstAVTapiCalls.Lock();
|
|
AVTAPICALLLIST::iterator i, iEnd = m_lstAVTapiCalls.end();
|
|
for ( i = m_lstAVTapiCalls.begin(); i != iEnd; i++ )
|
|
{
|
|
if ( (*i)->IsMyParticipant(pParticipant) == S_OK )
|
|
{
|
|
*ppCall = (*i);
|
|
(*ppCall)->AddRef();
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
}
|
|
m_critLstAVTapiCalls.Unlock();
|
|
// $CRIT_EXIT
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::CanCreateVideoWindows(DWORD dwAddressType)
|
|
{
|
|
// Load default registry values...
|
|
CRegKey regKey;
|
|
TCHAR szText[255], szType[255];
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_KEY, szText, ARRAYSIZE(szText) );
|
|
LoadString( _Module.GetResourceInstance(), AddressTypeToRegKey(dwAddressType, true), szType, ARRAYSIZE(szType) );
|
|
_tcscat( szText, _T("\\") );
|
|
_tcscat( szText, szType );
|
|
regKey.Open( HKEY_CURRENT_USER, szText, KEY_READ );
|
|
|
|
|
|
// Retrieve information on creating video window?
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_TERMINAL_VIDEO_RENDER, szText, ARRAYSIZE(szText) );
|
|
DWORD dwTemp = 1;
|
|
regKey.QueryValue( dwTemp, szText );
|
|
dwTemp = min( 1, dwTemp);
|
|
|
|
return (dwTemp == 1) ? S_OK : S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::RefreshDS()
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if ( InterlockedIncrement(&m_lRefreshDS) == 1 )
|
|
{
|
|
// Start up the thread
|
|
DWORD dwID;
|
|
HANDLE hThread = CreateThread(NULL, 0, ThreadDSProc, (void *) &m_lRefreshDS, NULL, &dwID);
|
|
if ( hThread )
|
|
{
|
|
CloseHandle( hThread );
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
// Decrement count since we didn't start the thread for one reason
|
|
// or another.
|
|
if ( FAILED(hr) )
|
|
InterlockedDecrement( &m_lRefreshDS );
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::CreateCallEx(BSTR bstrName, BSTR bstrAddress, BSTR bstrUser1, BSTR bstrUser2, DWORD dwAddressType)
|
|
{
|
|
_ASSERT( bstrAddress && dwAddressType );
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Wait for Dialer to register as client
|
|
//
|
|
|
|
if( m_hEventDialerReg)
|
|
{
|
|
WaitForSingleObject( m_hEventDialerReg, INFINITE);
|
|
}
|
|
|
|
CErrorInfo er;
|
|
er.set_Operation( IDS_ER_PLACECALL );
|
|
er.set_Details( IDS_ER_GET_ADDRESS );
|
|
|
|
ITAddress *pITAddress;
|
|
if ( SUCCEEDED(hr = er.set_hr(GetAddress(dwAddressType, true, &pITAddress))) )
|
|
{
|
|
// Setup dialing info to pass to dialing thread
|
|
er.set_Details( IDS_ER_CREATE_THREAD );
|
|
CThreadDialingInfo *pInfo = new CThreadDialingInfo;
|
|
if ( pInfo )
|
|
{
|
|
// Copy information into the info structure
|
|
pInfo->set_ITAddress( pITAddress );
|
|
if ( bstrName ) pInfo->m_bstrName = SysAllocString( bstrName );
|
|
if ( bstrAddress )
|
|
{
|
|
pInfo->m_bstrAddress = SysAllocString( bstrAddress );
|
|
pInfo->m_bstrOriginalAddress = SysAllocString( bstrAddress );
|
|
}
|
|
if ( bstrUser1 ) pInfo->m_bstrUser1 = SysAllocString( bstrUser1 );
|
|
if ( bstrUser2 ) pInfo->m_bstrUser2 = SysAllocString( bstrUser2 );
|
|
pInfo->m_dwAddressType = dwAddressType;
|
|
pInfo->TranslateAddress();
|
|
|
|
// Dialing takes place on separate thread
|
|
DWORD dwID;
|
|
HANDLE hThread = CreateThread(NULL, 0, ThreadDialingProc, (void *) pInfo, NULL, &dwID);
|
|
if ( !hThread )
|
|
{
|
|
hr = er.set_hr( E_UNEXPECTED );
|
|
ATLTRACE(_T(".error.CAVTapi::CreateCall() -- failed to creat the dialing thread.\n") );
|
|
delete pInfo;
|
|
}
|
|
else
|
|
{
|
|
CloseHandle( hThread );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = er.set_hr( E_OUTOFMEMORY );
|
|
}
|
|
|
|
pITAddress->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::get_Call(long lCallID, IAVTapiCall **ppCall)
|
|
{
|
|
*ppCall = NULL;
|
|
*ppCall = FindAVTapiCall( lCallID );
|
|
|
|
return (*ppCall) ? S_OK : E_FAIL;
|
|
}
|
|
|
|
|
|
bool CAVTapi::IsPreferredAddress( ITAddress *pITAddress, DWORD dwAddressType )
|
|
{
|
|
bool bRet = false;
|
|
|
|
CRegKey regKey;
|
|
TCHAR szText[255];
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_KEY, szText, ARRAYSIZE(szText) );
|
|
regKey.Open( HKEY_CURRENT_USER, szText, KEY_READ );
|
|
|
|
DWORD dwPermID = 0, dwAddrID;
|
|
if ( regKey.m_hKey )
|
|
{
|
|
LoadString( _Module.GetResourceInstance(), AddressTypeToRegKey(dwAddressType, true), szText, ARRAYSIZE(szText) );
|
|
regKey.QueryValue( dwPermID, szText );
|
|
|
|
if ( dwPermID )
|
|
{
|
|
// Is the call on the preferred address
|
|
LoadString( _Module.GetResourceInstance(), AddressTypeToRegKey(dwAddressType, false), szText, ARRAYSIZE(szText) );
|
|
regKey.QueryValue( dwAddrID, szText );
|
|
|
|
long lPreferredPermID = 0, lPreferredAddrID = 0;
|
|
ITAddressCapabilities *pCaps;
|
|
if ( SUCCEEDED(pITAddress->QueryInterface(IID_ITAddressCapabilities, (void **) &pCaps)) )
|
|
{
|
|
pCaps->get_AddressCapability( AC_PERMANENTDEVICEID, &lPreferredPermID );
|
|
pCaps->get_AddressCapability( AC_ADDRESSID, &lPreferredAddrID );
|
|
pCaps->Release();
|
|
}
|
|
|
|
bRet = ((DWORD) lPreferredPermID == dwPermID) && ((DWORD) lPreferredAddrID == dwAddrID);
|
|
}
|
|
else
|
|
{
|
|
// If user has not specified a preferred address, use the selected terminals for everything
|
|
bRet = true;
|
|
}
|
|
}
|
|
|
|
ATLTRACE(_T(".1.CAVTapi::IsPreferredAddress() returning %d.\n"), bRet );
|
|
return bRet;
|
|
}
|
|
|
|
void CAVTapi::CloseExtraneousCallWindows()
|
|
{
|
|
m_critLstAVTapiCalls.Lock();
|
|
if ( m_lstAVTapiCalls.size() >= MAX_CALLWINDOWS )
|
|
{
|
|
AVTAPICALLLIST::iterator i, iEnd = m_lstAVTapiCalls.end();
|
|
for ( i = m_lstAVTapiCalls.begin(); i != iEnd; i++ )
|
|
{
|
|
CALL_STATE callState = CS_IDLE;
|
|
ITCallInfo *pCallInfo;
|
|
if ( SUCCEEDED((*i)->get_ITCallInfo(&pCallInfo)) )
|
|
{
|
|
pCallInfo->get_CallState( &callState );
|
|
pCallInfo->Release();
|
|
}
|
|
|
|
// Clear the window slider if it's disconnected
|
|
if ( callState == CS_DISCONNECTED )
|
|
{
|
|
long lCallID = 0;
|
|
(*i)->get_lCallID( &lCallID );
|
|
|
|
// Pop out of crit temporarily
|
|
m_critLstAVTapiCalls.Unlock();
|
|
ActionSelected( lCallID, CM_ACTIONS_CLOSE );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_critLstAVTapiCalls.Unlock();
|
|
}
|
|
|
|
|
|
STDMETHODIMP CAVTapi::RegisterUser(VARIANT_BOOL bCreate, BSTR bstrServer)
|
|
{
|
|
DWORD dwID = 0;
|
|
|
|
CPublishUserInfo *pInfo = NULL;
|
|
if ( bstrServer )
|
|
{
|
|
// Allocate user info structure
|
|
pInfo = new CPublishUserInfo();
|
|
if ( !pInfo )
|
|
return E_OUTOFMEMORY;
|
|
|
|
// Use the server passed in as an argument
|
|
BSTR bstrTemp = SysAllocString( bstrServer );
|
|
if ( !bstrTemp )
|
|
{
|
|
delete pInfo;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// add server to the list
|
|
pInfo->m_lstServers.push_back( bstrTemp );
|
|
pInfo->m_bCreateUser = (bool) (bCreate != 0);
|
|
}
|
|
|
|
// If we fail to create the thread clean up appropriately
|
|
HANDLE hThread = CreateThread(NULL, 0, ThreadPublishUserProc, (void *) pInfo, NULL, &dwID);
|
|
if ( !hThread )
|
|
{
|
|
if ( pInfo )
|
|
delete pInfo;
|
|
|
|
return E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
CloseHandle( hThread );
|
|
}
|
|
|
|
return (dwID) ? S_OK : E_FAIL;
|
|
}
|
|
|
|
HRESULT CAVTapi::GetSwapHoldCallCandidate( IAVTapiCall *pAVCall, IAVTapiCall **ppAVCandidate )
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
_ASSERT( pAVCall && ppAVCandidate );
|
|
*ppAVCandidate = NULL;
|
|
|
|
ITAddress *pITAddress;
|
|
if ( SUCCEEDED(pAVCall->get_ITAddress(&pITAddress)) )
|
|
{
|
|
// Calls on the list
|
|
AVTAPICALLLIST lstCalls;
|
|
GetAllCallsAtState( &lstCalls, CS_HOLD );
|
|
|
|
AVTAPICALLLIST::iterator i, iEnd = lstCalls.end();
|
|
for ( i = lstCalls.begin(); i != iEnd; i++ )
|
|
{
|
|
ITAddress *pITAddressInd;
|
|
(*i)->get_ITAddress( &pITAddressInd );
|
|
|
|
// found a match
|
|
if ( pITAddress == pITAddressInd )
|
|
{
|
|
hr = (*i)->QueryInterface( IID_IAVTapiCall, (void **) ppAVCandidate );
|
|
break;
|
|
}
|
|
}
|
|
|
|
RELEASE_LIST( lstCalls );
|
|
|
|
// Clean-up
|
|
pITAddress->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CAVTapi::SelectTerminalOnStream( ITStreamControl *pStreamControl,
|
|
long lMediaMode,
|
|
long nDir,
|
|
ITTerminal *pTerminal,
|
|
IAVTapiCall *pAVCall )
|
|
{
|
|
HRESULT hr;
|
|
|
|
IEnumStream *pEnumStreams;
|
|
if ( SUCCEEDED(hr = pStreamControl->EnumerateStreams(&pEnumStreams)) )
|
|
{
|
|
// Loop through streams
|
|
bool bSelectedTerminal = false;
|
|
ITStream *pStream = NULL;
|
|
|
|
while ( !bSelectedTerminal && ((hr = pEnumStreams->Next(1, &pStream, NULL)) == S_OK) && pStream )
|
|
{
|
|
long lStreamMediaMode;
|
|
TERMINAL_DIRECTION nStreamDir;
|
|
|
|
pStream->get_Direction( &nStreamDir );
|
|
pStream->get_MediaType( &lStreamMediaMode );
|
|
|
|
// If the media and direction are correct, select the terminal
|
|
if ( (lMediaMode == lStreamMediaMode) && (nDir == nStreamDir) )
|
|
{
|
|
hr = pStream->SelectTerminal( pTerminal );
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
// Preview terminal is a special case
|
|
TERMINAL_DIRECTION nTermDir = TD_CAPTURE;
|
|
pTerminal->get_Direction( &nTermDir );
|
|
if ( (nTermDir == TD_RENDER) && (nDir == TD_CAPTURE) && (lMediaMode == TAPIMEDIATYPE_VIDEO) )
|
|
pAVCall->put_ITTerminalPreview( pTerminal );
|
|
else
|
|
pAVCall->AddTerminal( pTerminal );
|
|
|
|
bSelectedTerminal = true;
|
|
}
|
|
}
|
|
|
|
// clean up
|
|
pStream->Release();
|
|
pStream = NULL;
|
|
}
|
|
|
|
pEnumStreams->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CAVTapi::UnselectTerminalOnStream( ITStreamControl *pStreamControl,
|
|
long lMediaMode,
|
|
long nDir,
|
|
ITTerminal *pTerminal,
|
|
IAVTapiCall *pAVCall )
|
|
{
|
|
HRESULT hr;
|
|
|
|
IEnumStream *pEnumStreams;
|
|
if ( SUCCEEDED(hr = pStreamControl->EnumerateStreams(&pEnumStreams)) )
|
|
{
|
|
// Loop through streams
|
|
bool bUnselectedTerminal = false;
|
|
ITStream *pStream = NULL;
|
|
|
|
while ( !bUnselectedTerminal && ((hr = pEnumStreams->Next(1, &pStream, NULL)) == S_OK) && pStream )
|
|
{
|
|
long lStreamMediaMode;
|
|
TERMINAL_DIRECTION nStreamDir;
|
|
|
|
pStream->get_Direction( &nStreamDir );
|
|
pStream->get_MediaType( &lStreamMediaMode );
|
|
|
|
// If the media and direction are correct, select the terminal
|
|
if ( (lMediaMode == lStreamMediaMode) && (nDir == nStreamDir) )
|
|
{
|
|
hr = pStream->UnselectTerminal( pTerminal );
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
// Preview terminal is a special case
|
|
TERMINAL_DIRECTION nTermDir = TD_CAPTURE;
|
|
pTerminal->get_Direction( &nTermDir );
|
|
if ( (nTermDir == TD_RENDER) && (nDir == TD_CAPTURE) && (lMediaMode == TAPIMEDIATYPE_VIDEO) )
|
|
{
|
|
pAVCall->put_ITTerminalPreview( NULL );
|
|
}
|
|
else
|
|
pAVCall->RemoveTerminal( pTerminal );
|
|
|
|
bUnselectedTerminal = true;
|
|
}
|
|
}
|
|
|
|
// clean up
|
|
pStream->Release();
|
|
pStream = NULL;
|
|
}
|
|
|
|
pEnumStreams->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::SendUserUserInfo(long lCallID, BYTE * pBuf, DWORD dwSizeBuf)
|
|
{
|
|
ATLTRACE(_T(".enter.CAVTapiCall::SendUserUserInfo(call=%ld, size=%ld).\n"), lCallID, dwSizeBuf );
|
|
|
|
IAVTapiCall *pAVCall = FindAVTapiCall( lCallID );
|
|
if ( pAVCall )
|
|
{
|
|
ITCallInfo *pCallInfo;
|
|
if ( SUCCEEDED(pAVCall->get_ITCallInfo(&pCallInfo)) )
|
|
{
|
|
pCallInfo->SetCallInfoBuffer( CIB_USERUSERINFO, dwSizeBuf, pBuf );
|
|
pCallInfo->Release();
|
|
}
|
|
pAVCall->Release();
|
|
}
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::CreateDataCall(long lCallID, BSTR bstrName, BSTR bstrAddress, BYTE *pBuf, DWORD dwBufSize )
|
|
{
|
|
USES_CONVERSION;
|
|
ATLTRACE(_T(".enter.CAVTapi::CreateDataCall().\n"));
|
|
_ASSERT( lCallID && bstrAddress && pBuf && (dwBufSize > 0) );
|
|
|
|
HRESULT hr = E_POINTER;
|
|
|
|
CErrorInfo er;
|
|
er.set_Operation( IDS_ER_PLACECALL );
|
|
er.set_Details( IDS_ER_GET_ADDRESS );
|
|
|
|
ITAddress *pITAddress;
|
|
if ( bstrAddress && SUCCEEDED(hr = er.set_hr(GetAddress(LINEADDRESSTYPE_IPADDRESS, false, &pITAddress))) )
|
|
{
|
|
// Setup dialing info to pass to dialing thread
|
|
er.set_Details( IDS_ER_CREATE_THREAD );
|
|
|
|
CThreadDialingInfo *pThreadInfo = new CThreadDialingInfo;
|
|
if ( pThreadInfo )
|
|
{
|
|
// Store information in dialing structure
|
|
pThreadInfo->set_ITAddress( pITAddress );
|
|
|
|
if ( bstrName ) pThreadInfo->m_bstrName = SysAllocString( bstrName );
|
|
SysReAllocString( &pThreadInfo->m_bstrAddress, bstrAddress );
|
|
pThreadInfo->m_dwAddressType = LINEADDRESSTYPE_IPADDRESS;
|
|
pThreadInfo->m_nCallType = AV_DATA_CALL;
|
|
pThreadInfo->m_lCallID = lCallID;
|
|
pThreadInfo->TranslateAddress();
|
|
|
|
// Get first user-user data ready to send to remote party
|
|
HGLOBAL hMem = GlobalAlloc( GMEM_MOVEABLE | GMEM_DISCARDABLE, dwBufSize );
|
|
if ( hMem )
|
|
{
|
|
void *pbUU = GlobalLock( hMem );
|
|
if ( pbUU )
|
|
{
|
|
// Get user to user info
|
|
memcpy( pbUU, pBuf, dwBufSize);
|
|
GlobalUnlock( hMem );
|
|
}
|
|
|
|
pThreadInfo->m_hMem = hMem;
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
|
|
// Dialing takes place on separate thread
|
|
DWORD dwID;
|
|
HANDLE hThread = NULL;
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
hThread = CreateThread( NULL, 0, ThreadDialingProc, (void *) pThreadInfo, NULL, &dwID );
|
|
if ( hThread ) CloseHandle( hThread );
|
|
}
|
|
|
|
if ( FAILED(hr) || !hThread )
|
|
{
|
|
hr = er.set_hr( E_UNEXPECTED );
|
|
ATLTRACE(_T(".error.CAVTapi::CreateCall() -- failed to create the dialing thread.\n") );
|
|
delete pThreadInfo;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = er.set_hr( E_OUTOFMEMORY );
|
|
}
|
|
|
|
pITAddress->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::FindAVTapiCallFromCallID(long lCallID, IAVTapiCall * * ppAVCall)
|
|
{
|
|
*ppAVCall = FindAVTapiCall( lCallID );
|
|
return (*ppAVCall) ? S_OK : E_FAIL;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::get_bstrDefaultServer(BSTR * pVal)
|
|
{
|
|
_ASSERT( pVal );
|
|
*pVal = NULL;
|
|
|
|
Lock();
|
|
HRESULT hr = SysReAllocString( pVal, m_bstrDefaultServer );
|
|
Unlock();
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::put_bstrDefaultServer(BSTR newVal)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// Don't want this checked in
|
|
#ifdef _USE_DEFAULTSERVER
|
|
Lock();
|
|
if ( !newVal || (SysStringLen(newVal) == 0) )
|
|
{
|
|
SysFreeString( m_bstrDefaultServer );
|
|
m_bstrDefaultServer = NULL;
|
|
}
|
|
else
|
|
{
|
|
hr = SysReAllocString( &m_bstrDefaultServer, newVal );
|
|
}
|
|
Unlock();
|
|
#endif
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::get_bAutoCloseCalls(VARIANT_BOOL * pVal)
|
|
{
|
|
Lock();
|
|
*pVal = (VARIANT_BOOL) (m_bAutoCloseCalls != false);
|
|
Unlock();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CAVTapi::put_bAutoCloseCalls(VARIANT_BOOL newVal)
|
|
{
|
|
Lock();
|
|
m_bAutoCloseCalls = (newVal != FALSE);
|
|
Unlock();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
USBFindPhone
|
|
|
|
Try to find out if there is an USBPhone
|
|
The critical section should be lock outsite
|
|
--*/
|
|
HRESULT CAVTapi::USBFindPhone(
|
|
OUT ITPhone** ppUSBPhone
|
|
)
|
|
{
|
|
_ASSERT(ppUSBPhone);
|
|
_ASSERT(m_pITTapi);
|
|
|
|
//
|
|
//Critical section
|
|
//
|
|
|
|
m_critUSBPhone.Lock();
|
|
|
|
//
|
|
// Don't return garbage
|
|
//
|
|
|
|
*ppUSBPhone = NULL;
|
|
|
|
//
|
|
// Get the H323 address
|
|
//
|
|
|
|
ITAddress2* pH323Address = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
hr = USBGetH323Address(&pH323Address);
|
|
if( FAILED(hr) )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Get the phone object if on this address is
|
|
// someone
|
|
//
|
|
|
|
ITPhone* pPhone = NULL;
|
|
hr = USBGetPhoneFromAddress(
|
|
pH323Address,
|
|
&pPhone
|
|
);
|
|
|
|
//
|
|
// Clean-up pH32Address
|
|
//
|
|
|
|
pH323Address->Release();
|
|
|
|
//
|
|
// We failed to get a phone object on
|
|
// H323 address
|
|
//
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Initialize the phone
|
|
//
|
|
|
|
hr = USBPhoneInitialize(
|
|
pPhone );
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
//
|
|
// Clean-up the phone object
|
|
//
|
|
|
|
pPhone->Release();
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Set the phone
|
|
//
|
|
*ppUSBPhone = pPhone;
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
USBIsPresent
|
|
|
|
Determine if the USBPhone was detected
|
|
--*/
|
|
STDMETHODIMP CAVTapi::USBIsPresent(
|
|
OUT BOOL* pVal
|
|
)
|
|
{
|
|
_ASSERT( pVal );
|
|
|
|
m_critUSBPhone.Lock();
|
|
|
|
*pVal = (NULL != m_pUSBPhone);
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
USBGetDefaultUse
|
|
|
|
Returns the value for "Audio/Video" checkbox
|
|
--*/
|
|
STDMETHODIMP CAVTapi::USBGetDefaultUse(
|
|
OUT BOOL* pVal
|
|
)
|
|
{
|
|
_ASSERT( pVal );
|
|
|
|
if( NULL == m_pUSBPhone )
|
|
{
|
|
*pVal = FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
*pVal = USBGetCheckboxValue();
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
USBNewPhone
|
|
|
|
Is called by CTapiNotification::Address_Event when
|
|
an AE_NEWPHONE is fired
|
|
--*/
|
|
STDMETHODIMP CAVTapi::USBNewPhone(
|
|
IN ITPhone* pPhone
|
|
)
|
|
{
|
|
// Critical Section
|
|
m_critUSBPhone.Lock();
|
|
|
|
if( NULL != m_pUSBPhone)
|
|
{
|
|
//
|
|
// we already have a phone
|
|
// sorry, we don't support two phone objects
|
|
//
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
//
|
|
// Validates argument
|
|
//
|
|
|
|
if( pPhone == NULL)
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
//
|
|
// Determine if the phone is a real one
|
|
// has a H323 address
|
|
//
|
|
IEnumAddress* pAddresses = NULL;
|
|
HRESULT hr = pPhone->EnumerateAddresses( &pAddresses );
|
|
if( FAILED(hr) )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Go to each address to see if we have
|
|
// a H323 one
|
|
//
|
|
|
|
ITAddress* pAddress = NULL;
|
|
ULONG uFetched = 0;
|
|
BOOL bHasH323Address = FALSE;
|
|
|
|
while( S_OK == pAddresses->Next(1, &pAddress, &uFetched))
|
|
{
|
|
if( USBIsH323Address( pAddress ) )
|
|
{
|
|
bHasH323Address = TRUE;
|
|
pAddress->Release();
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Clean-up
|
|
//
|
|
|
|
pAddress->Release();
|
|
}
|
|
|
|
//
|
|
// Clean-up enumeration
|
|
//
|
|
pAddresses->Release();
|
|
|
|
//
|
|
// Phone object supports H323 address?
|
|
//
|
|
if( !bHasH323Address )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// Initialize the phone object
|
|
//
|
|
|
|
hr = USBPhoneInitialize(
|
|
pPhone);
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Set the new phone object
|
|
//
|
|
|
|
m_pUSBPhone = pPhone;
|
|
m_pUSBPhone->AddRef();
|
|
|
|
//
|
|
// Set the registry value
|
|
//
|
|
|
|
USBSetCheckboxValue( TRUE );
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
USBRemovePhone
|
|
|
|
Is called by CTapiNotification::Address_Event when
|
|
an AE_REMOVEPHONE is fired
|
|
--*/
|
|
STDMETHODIMP CAVTapi::USBRemovePhone(
|
|
IN ITPhone* pPhone
|
|
)
|
|
{
|
|
// Critical Section
|
|
m_critUSBPhone.Lock();
|
|
|
|
//
|
|
// If we don't have a phone
|
|
// bad luck
|
|
//
|
|
|
|
if( NULL == m_pUSBPhone )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Validates argument
|
|
//
|
|
|
|
if( pPhone == NULL)
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
//
|
|
// Are the same phone?
|
|
// We use the IUnknow interface to
|
|
// see if they are identical
|
|
//
|
|
|
|
//
|
|
// Get IUnknown interface for existing phone object
|
|
//
|
|
IUnknown* pUSBUnk = NULL;
|
|
|
|
HRESULT hr = m_pUSBPhone->QueryInterface(IID_IUnknown, (void**)&pUSBUnk);
|
|
if( FAILED(hr) )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Get IUnknown interface for removed phone
|
|
//
|
|
IUnknown* pRemUnk = NULL;
|
|
hr = pPhone->QueryInterface(IID_IUnknown, (void**)&pRemUnk);
|
|
if( FAILED(hr) )
|
|
{
|
|
pUSBUnk->Release();
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Let's compare the two interfaces
|
|
//
|
|
|
|
if( pUSBUnk == pRemUnk )
|
|
{
|
|
m_pUSBPhone->Close();
|
|
USBRegDelTerminals();
|
|
m_pUSBPhone->Release();
|
|
m_pUSBPhone = NULL;
|
|
|
|
if( m_bstrUSBCaptureTerm )
|
|
{
|
|
SysFreeString( m_bstrUSBCaptureTerm );
|
|
m_bstrUSBCaptureTerm = NULL;
|
|
}
|
|
|
|
if( m_bstrUSBRenderTerm )
|
|
{
|
|
SysFreeString( m_bstrUSBRenderTerm );
|
|
m_bstrUSBRenderTerm = NULL;
|
|
}
|
|
|
|
//
|
|
// If there are some calls destroy them,
|
|
// as cleany is possible
|
|
//
|
|
|
|
HWND hWnd = NULL;
|
|
get_hWndParent( &hWnd );
|
|
|
|
if( ::IsWindow(hWnd) )
|
|
{
|
|
RELEASE_CRITLIST(m_lstAVTapiCalls, m_critLstAVTapiCalls);
|
|
::SendMessage( hWnd, WM_USBPHONE, AVUSB_CANCELCALL, 0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set registry value on FALSE
|
|
//
|
|
|
|
USBSetCheckboxValue( FALSE );
|
|
|
|
//
|
|
// Clean-up the IUnknown interfaces
|
|
//
|
|
pUSBUnk->Release();
|
|
pRemUnk->Release();
|
|
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
USBCancellCall
|
|
|
|
Is called by CTapiNotification::Phone_Event when
|
|
a PE_HOOKSWITCH (hhok state is PHSS_ONHOOK) is
|
|
fired
|
|
--*/
|
|
HRESULT CAVTapi::USBCancellCall( )
|
|
{
|
|
// Critical Section
|
|
m_critUSBPhone.Lock();
|
|
|
|
// We don't have USB phone
|
|
if( m_pUSBPhone == NULL)
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
// We don't have the dialog
|
|
if( NULL == m_pDlgCall )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
// Delete the dialog box
|
|
if( ::IsWindow(m_pDlgCall->m_hWnd))
|
|
{
|
|
::SendMessage(m_pDlgCall->m_hWnd, WM_CLOSE, 0, 0);
|
|
}
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
USBMakeCall
|
|
|
|
Is called by CTapiNotification::Phone_Event when
|
|
a PE_HOOKSWITCH (hhok state is PHSS_OFFHOOK) is
|
|
fired
|
|
--*/
|
|
HRESULT CAVTapi::USBMakeCall()
|
|
{
|
|
m_critUSBPhone.Lock();
|
|
|
|
if( NULL == m_pUSBPhone )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Get the IAutomatedPhoneControl interface
|
|
//
|
|
|
|
ITAutomatedPhoneControl* pAutomated = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
hr = m_pUSBPhone->QueryInterface(
|
|
IID_ITAutomatedPhoneControl,
|
|
(void**)&pAutomated
|
|
);
|
|
|
|
//
|
|
// Try to see if we are already into a call
|
|
// If are 'offering calls' then we tre to
|
|
// answer to these calls
|
|
//
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
//
|
|
// Enumerate the calls
|
|
//
|
|
HRESULT hr = E_FAIL;
|
|
IEnumCall* pCalls = NULL;
|
|
hr= pAutomated->EnumerateSelectedCalls(&pCalls);
|
|
|
|
//
|
|
// Clean up
|
|
//
|
|
pAutomated->Release();
|
|
|
|
//
|
|
// Are there a selected call ?
|
|
//
|
|
|
|
bool bCallsSelected = false;
|
|
ULONG cFetched = 0;
|
|
ITCallInfo* pCallInfo = NULL;
|
|
|
|
while( S_OK == pCalls->Next(1, &pCallInfo, &cFetched))
|
|
{
|
|
bCallsSelected = true;
|
|
|
|
// Get the call state
|
|
CALL_STATE callState = CS_IDLE;
|
|
pCallInfo->get_CallState( &callState );
|
|
|
|
if(callState != CS_OFFERING)
|
|
{
|
|
pCallInfo->Release();
|
|
pCallInfo = NULL;
|
|
}
|
|
|
|
//
|
|
// Just one call could be selected
|
|
// on phone object
|
|
//
|
|
break;
|
|
}
|
|
|
|
// Clean-up
|
|
pCalls->Release();
|
|
|
|
if( bCallsSelected )
|
|
{
|
|
//
|
|
// We are into a call
|
|
//
|
|
if( pCallInfo )
|
|
{
|
|
//
|
|
// We are into an offering call
|
|
// We should simultate the CreateTerminalArray
|
|
//
|
|
|
|
IAVTapiCall* pAVTapiCall = NULL;
|
|
FindAVTapiCallFromCallInfo( pCallInfo, &pAVTapiCall);
|
|
|
|
if( pAVTapiCall )
|
|
{
|
|
ITAddress* pAddress = NULL;
|
|
pCallInfo->get_Address( &pAddress );
|
|
|
|
if( pAddress )
|
|
{
|
|
CreateTerminalArray( pAddress, pAVTapiCall, pCallInfo);
|
|
|
|
// Clean-up
|
|
pAddress->Release();
|
|
}
|
|
|
|
// Clean-up
|
|
pAVTapiCall->Release();
|
|
}
|
|
|
|
// Clean-up
|
|
pCallInfo->Release();
|
|
}
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
HWND hWnd = NULL;
|
|
get_hWndParent( &hWnd );
|
|
|
|
if( ::IsWindow(hWnd) && (m_pDlgCall == NULL) )
|
|
{
|
|
::SendMessage( hWnd, WM_USBPHONE, AVUSB_MAKECALL, 0);
|
|
}
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
USBKeyPress
|
|
|
|
Is called from CTapiNotification::Phone_Event
|
|
when a PE_BUTTON or PE_NUMBERGATHERED is fired
|
|
from the phone
|
|
--*/
|
|
HRESULT CAVTapi::USBKeyPress(long lButton)
|
|
{
|
|
m_critUSBPhone.Lock();
|
|
|
|
if( NULL == m_pUSBPhone )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// If the PlaceCall dialog is not pop-up
|
|
// then we send this key as DTMF tones
|
|
// If the PlaceCall dialog is pop-up
|
|
// then this key should be add to the
|
|
// 'PhoneNumber' editbox into PlaceCall dialog
|
|
//
|
|
|
|
if( NULL == m_pDlgCall )
|
|
{
|
|
if( (0<= lButton) && (lButton <=11) )
|
|
{
|
|
//
|
|
// There are calls on this phone
|
|
// If yes, send DTMFs
|
|
//
|
|
|
|
ITAutomatedPhoneControl* pAutomated;
|
|
IEnumCall* pCalls = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
hr = m_pUSBPhone->QueryInterface(
|
|
IID_ITAutomatedPhoneControl, (void**)&pAutomated);
|
|
if( FAILED(hr) )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
hr = pAutomated->EnumerateSelectedCalls( &pCalls );
|
|
if( FAILED(hr) )
|
|
{
|
|
pAutomated->Release();
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
pAutomated->Release(); //Clean-up
|
|
|
|
// Enumerate the calls
|
|
ITCallInfo* pCall = NULL;
|
|
ULONG cFetched = 0;
|
|
while( S_OK == pCalls->Next(1, &pCall, &cFetched) )
|
|
{
|
|
// Get the ITBasiccallControl
|
|
ITBasicCallControl* pControl = NULL;
|
|
hr = pCall->QueryInterface(
|
|
IID_ITBasicCallControl,
|
|
(void**)&pControl
|
|
);
|
|
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
// Get AVCall
|
|
IAVTapiCall *pAVCall = FindAVTapiCall( pControl );
|
|
if( pAVCall )
|
|
{
|
|
// Get the Call ID
|
|
long lCallID = 0;
|
|
pAVCall->get_lCallID( &lCallID );
|
|
|
|
// Call DigitPrees for this call
|
|
if( (0 <= lButton) && (lButton <= 11))
|
|
{
|
|
//
|
|
// Digit Press
|
|
PhonePadKey PPKey = PP_DTMF_0;
|
|
BOOL bDtmf = TRUE;
|
|
switch( lButton )
|
|
{
|
|
case 0: PPKey = PP_DTMF_0; break;
|
|
case 1: PPKey = PP_DTMF_1; break;
|
|
case 2: PPKey = PP_DTMF_2; break;
|
|
case 3: PPKey = PP_DTMF_3; break;
|
|
case 4: PPKey = PP_DTMF_4; break;
|
|
case 5: PPKey = PP_DTMF_5; break;
|
|
case 6: PPKey = PP_DTMF_6; break;
|
|
case 7: PPKey = PP_DTMF_7; break;
|
|
case 8: PPKey = PP_DTMF_8; break;
|
|
case 9: PPKey = PP_DTMF_9; break;
|
|
case 10: PPKey = PP_DTMF_STAR; break;
|
|
case 11: PPKey = PP_DTMF_POUND; break;
|
|
default: bDtmf = FALSE; break;
|
|
}
|
|
|
|
if( bDtmf )
|
|
{
|
|
DigitPress( lCallID, PPKey);
|
|
}
|
|
}
|
|
|
|
// Release the pAVCall
|
|
pAVCall->Release();
|
|
}
|
|
|
|
// Clean-up
|
|
pControl->Release();
|
|
}
|
|
|
|
|
|
// Clean-up
|
|
pCall->Release();
|
|
}
|
|
|
|
// Clean-up
|
|
pCalls->Release();
|
|
}
|
|
else
|
|
{
|
|
// Determine the button function
|
|
PHONE_BUTTON_FUNCTION nFunction;
|
|
if( SUCCEEDED(m_pUSBPhone->get_ButtonFunction(lButton, &nFunction)) )
|
|
{
|
|
if( nFunction == PBF_LASTNUM )
|
|
{
|
|
//
|
|
// Redial
|
|
//
|
|
|
|
HWND hWnd = NULL;
|
|
get_hWndParent( &hWnd );
|
|
|
|
if( ::IsWindow(hWnd) && (m_pDlgCall == NULL) )
|
|
{
|
|
::SendMessage( hWnd, WM_USBPHONE, AVUSB_REDIAL, 0);
|
|
}
|
|
}
|
|
} // Succeeded
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The Dial Dialog is opened, so show the digit
|
|
// in the edit control
|
|
m_pDlgCall->KeyPress( lButton );
|
|
}
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
USBOffering
|
|
|
|
Is called by CAVTapi::fire_SetCallState when the call state
|
|
is CS_OFFERING (incoming calls)
|
|
--*/
|
|
HRESULT CAVTapi::USBOffering(
|
|
IN ITCallInfo* pCallInfo
|
|
)
|
|
{
|
|
m_critUSBPhone.Lock();
|
|
|
|
//
|
|
// Incoming call
|
|
//
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
if( NULL == m_pUSBPhone)
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Validate the USB checkbox value
|
|
//
|
|
BOOL bUSBCheckbox = FALSE;
|
|
bUSBCheckbox = USBGetCheckboxValue();
|
|
if( !bUSBCheckbox )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Get the state of hook
|
|
//
|
|
PHONE_HOOK_SWITCH_STATE HookState = PHSS_OFFHOOK;
|
|
hr = m_pUSBPhone->get_HookSwitchState( PHSD_HANDSET, &HookState);
|
|
if( FAILED(hr) )
|
|
{
|
|
//
|
|
// Something wrong
|
|
// Go ahead and handle the call into common way
|
|
//
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
if( HookState == PHSS_OFFHOOK )
|
|
{
|
|
//
|
|
// We are really busy
|
|
// Reject the call
|
|
//
|
|
|
|
ITBasicCallControl* pCallControl = NULL;
|
|
hr = pCallInfo->QueryInterface(
|
|
IID_ITBasicCallControl,
|
|
(void**)&pCallControl
|
|
);
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
// This is really bad
|
|
m_critUSBPhone.Unlock();
|
|
return E_FAIL;
|
|
}
|
|
|
|
pCallControl->Disconnect(DC_REJECTED);
|
|
pCallControl->Release();
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// OK let's ring
|
|
//
|
|
ITAutomatedPhoneControl* pAutomated = NULL;
|
|
hr = m_pUSBPhone->QueryInterface(
|
|
IID_ITAutomatedPhoneControl,
|
|
(void**)&pAutomated
|
|
);
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Select call
|
|
//
|
|
|
|
VARIANT_BOOL varSelectDefault = VARIANT_TRUE;
|
|
varSelectDefault = USBGetCheckboxValue() ? VARIANT_TRUE : VARIANT_FALSE;
|
|
|
|
hr = pAutomated->SelectCall(
|
|
pCallInfo,
|
|
varSelectDefault
|
|
);
|
|
|
|
// Clean-up
|
|
pAutomated->Release();
|
|
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
// Hmmm! This is a problem
|
|
// Anyway, go ahead and handle the call
|
|
//
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
USBDisconnected
|
|
|
|
Is called by CAVTapi::fire_SetCallState when the call state
|
|
is CS_DISCONECTED (outgoing/incoming calls)
|
|
--*/
|
|
HRESULT CAVTapi::USBDisconnected(
|
|
IN long lCallID)
|
|
{
|
|
|
|
// Critical Section
|
|
m_critUSBPhone.Lock();
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// We are have an UPBPhone
|
|
//
|
|
|
|
if( NULL == m_pUSBPhone)
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
m_critUSBPhone.Unlock();
|
|
hr = fire_CloseCallControl(lCallID);
|
|
return hr;
|
|
}
|
|
|
|
/*++
|
|
USBTakeCallEnabled
|
|
|
|
Is called by AVDialer to determine if 'Take call' button
|
|
should be enabled or not
|
|
--*/
|
|
HRESULT CAVTapi::USBTakeCallEnabled(
|
|
OUT BOOL* pEnabled
|
|
)
|
|
{
|
|
// Critical Section
|
|
m_critUSBPhone.Lock();
|
|
|
|
//
|
|
// Get the checkbox value
|
|
//
|
|
|
|
BOOL bCheckboxValue = USBGetCheckboxValue();
|
|
if( !bCheckboxValue )
|
|
{
|
|
//
|
|
// The Dialer should work as the phone is not
|
|
// present, so the Take Call button should be enabled
|
|
// always in this case
|
|
//
|
|
|
|
*pEnabled = TRUE;
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Validate argument
|
|
//
|
|
if( IsBadWritePtr( pEnabled, sizeof( BOOL )) )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return E_POINTER;
|
|
}
|
|
|
|
//
|
|
// Have we an USBPhone
|
|
//
|
|
|
|
if( NULL == m_pUSBPhone )
|
|
{
|
|
*pEnabled = TRUE;
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Does phone support Speakers?
|
|
//
|
|
|
|
long lCaps = 0;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
// Get the caps
|
|
hr = m_pUSBPhone->get_PhoneCapsLong(
|
|
PCL_HOOKSWITCHES,
|
|
&lCaps);
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
*pEnabled = FALSE;
|
|
m_critUSBPhone.Unlock();
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Supports the speakerphone
|
|
if( lCaps & ((long)PHSD_SPEAKERPHONE))
|
|
{
|
|
*pEnabled = TRUE;
|
|
}
|
|
else
|
|
{
|
|
*pEnabled = FALSE;
|
|
}
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
USBGetCheckboxValue
|
|
|
|
Goes to registry and read the value for USB checkbox from
|
|
'Options' property page
|
|
Is called from USBOffering
|
|
--*/
|
|
BOOL CAVTapi::USBGetCheckboxValue(
|
|
IN BOOL bVerifyUSB /*TRUE*/
|
|
)
|
|
{
|
|
//
|
|
// Have we USBPhone present?
|
|
//
|
|
if( bVerifyUSB && (NULL == m_pUSBPhone))
|
|
{
|
|
// No Always, even was seted before
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Read the registry for the previous
|
|
// setting for USB Phone
|
|
//
|
|
|
|
TCHAR szText[255], szType[255];
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_KEY, szText, ARRAYSIZE(szText) );
|
|
|
|
CRegKey regKey;
|
|
if( regKey.Open( HKEY_CURRENT_USER, szText, KEY_READ )!= ERROR_SUCCESS)
|
|
{
|
|
return FALSE;
|
|
};
|
|
|
|
//
|
|
// Read data
|
|
//
|
|
|
|
DWORD dwValue = 0;
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_USBALWAYS, szType, ARRAYSIZE(szType) );
|
|
if( regKey.QueryValue(dwValue, szType) != ERROR_SUCCESS)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return (BOOL)dwValue;
|
|
}
|
|
|
|
/*++
|
|
USBSetCheckboxValue
|
|
|
|
Is called by USBNewPhone. Sets the registry value
|
|
for the USB checkbox in 'Options' dialog,
|
|
also is called bt USBremovePhone
|
|
--*/
|
|
HRESULT CAVTapi::USBSetCheckboxValue(
|
|
IN BOOL bCheckValue
|
|
)
|
|
{
|
|
//
|
|
// Set the entries in registry
|
|
//
|
|
CRegKey regKey;
|
|
TCHAR szText[255], szType[255];
|
|
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_KEY, szText, ARRAYSIZE(szText) );
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_USBALWAYS, szType, ARRAYSIZE(szType) );
|
|
|
|
if( regKey.Create( HKEY_CURRENT_USER, szText) != ERROR_SUCCESS)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Set the value
|
|
if( regKey.SetValue((DWORD)bCheckValue, szType) != ERROR_SUCCESS)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*++
|
|
USBInprogress
|
|
|
|
Is called by CAVTapi::fire_SetCallState when the call state
|
|
is CS_INPROGRESS (outgoing calls)
|
|
--*/
|
|
HRESULT CAVTapi::USBInprogress(
|
|
IN ITCallInfo* pCallInfo
|
|
)
|
|
{
|
|
// Critical section
|
|
m_critUSBPhone.Lock();
|
|
|
|
//
|
|
// Outgoing call
|
|
//
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
if( NULL == m_pUSBPhone)
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Validate the USB checkbox value
|
|
//
|
|
BOOL bUSBCheckbox = FALSE;
|
|
bUSBCheckbox = USBGetCheckboxValue();
|
|
if( !bUSBCheckbox )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//
|
|
// OK let's ring
|
|
//
|
|
|
|
ITAutomatedPhoneControl* pAutomated = NULL;
|
|
hr = m_pUSBPhone->QueryInterface(
|
|
IID_ITAutomatedPhoneControl,
|
|
(void**)&pAutomated
|
|
);
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Select call
|
|
//
|
|
|
|
VARIANT_BOOL varSelectDefault = VARIANT_TRUE;
|
|
varSelectDefault = USBGetCheckboxValue() ? VARIANT_TRUE : VARIANT_FALSE;
|
|
|
|
hr = pAutomated->SelectCall(
|
|
pCallInfo,
|
|
varSelectDefault
|
|
);
|
|
|
|
// Clean-up
|
|
pAutomated->Release();
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
//
|
|
// Hmmm! This is a problem
|
|
// Anyway, go ahead and handle the call
|
|
//
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
USBIsH323Address
|
|
|
|
Detects if the address is a H323 one,
|
|
is called by USBFindPhone.
|
|
we don't need critical section is seted when
|
|
USBFindPhone was called
|
|
--*/
|
|
BOOL CAVTapi::USBIsH323Address(
|
|
IN ITAddress* pAddress)
|
|
{
|
|
_ASSERTE( pAddress );
|
|
|
|
//
|
|
// Get ITAddressCapabilities
|
|
//
|
|
|
|
ITAddressCapabilities* pCap = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
hr = pAddress->QueryInterface(
|
|
IID_ITAddressCapabilities,
|
|
(void**)&pCap
|
|
);
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
// Bad luck
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Determine the propocol
|
|
//
|
|
|
|
BSTR bstrProtocol = NULL;
|
|
hr = pCap->get_AddressCapabilityString(
|
|
ACS_PROTOCOL,
|
|
&bstrProtocol
|
|
);
|
|
|
|
// Clean-up
|
|
pCap->Release();
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
// Bad luck
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Get the CLSID for the protocol
|
|
//
|
|
|
|
CLSID clsid;
|
|
hr = CLSIDFromString( bstrProtocol, &clsid);
|
|
SysFreeString( bstrProtocol );
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
// Somebody try to make a joke!
|
|
return FALSE;
|
|
}
|
|
|
|
if( TAPIPROTOCOL_H323 != clsid)
|
|
{
|
|
// It's someting different
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*++
|
|
USBGetH323Address
|
|
|
|
Returns a H323 address if exists someone
|
|
Else returns E_FAIL. It is called by USBFindPhone()
|
|
method
|
|
--*/
|
|
HRESULT CAVTapi::USBGetH323Address(
|
|
OUT ITAddress2** ppAddress2
|
|
)
|
|
{
|
|
//
|
|
// We should have Tapi object
|
|
//
|
|
|
|
if( NULL == m_pITTapi )
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// set on NULL just in case
|
|
//
|
|
|
|
*ppAddress2 = NULL;
|
|
|
|
//
|
|
// Enumerate the addresses
|
|
//
|
|
|
|
IEnumAddress* pAddresses = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
hr = m_pITTapi->EnumerateAddresses(&pAddresses);
|
|
if( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Parse the enumeration
|
|
//
|
|
|
|
ITAddress* pAddress = NULL;
|
|
ULONG cFetched = 0;
|
|
|
|
while( pAddresses->Next(1, &pAddress, &cFetched) == S_OK)
|
|
{
|
|
//
|
|
// Is it a H323 address?
|
|
//
|
|
|
|
BOOL bH323 = USBIsH323Address( pAddress );
|
|
if( !bH323 )
|
|
{
|
|
//
|
|
// Clean-up and go to next address
|
|
//
|
|
|
|
pAddress->Release();
|
|
pAddress = NULL;
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// OK, we got the H323 address
|
|
// we break the loop, we keep pAddress
|
|
// and release it later
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// Clean-up the addresses enumeration
|
|
//
|
|
|
|
pAddresses->Release();
|
|
|
|
//
|
|
// Did we find a H323 address?
|
|
//
|
|
|
|
if( NULL == pAddress )
|
|
{
|
|
//
|
|
// No, there is no H323 address
|
|
// the *ppAddress2 was already set on NULL
|
|
//
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// We got an H323 address
|
|
// so we need to get ITAddress2 interface
|
|
//
|
|
|
|
hr = pAddress->QueryInterface(
|
|
IID_ITAddress2,
|
|
(void**)ppAddress2
|
|
);
|
|
|
|
//
|
|
// Clean-up pAddress
|
|
//
|
|
|
|
pAddress->Release();
|
|
|
|
//
|
|
// That's all, return the hr
|
|
//
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*++
|
|
USBGetPhoneFromAddress
|
|
|
|
Returns the ITPhone object on this address
|
|
if a phone object exists. It is called by
|
|
USBFindPhone.
|
|
--*/
|
|
HRESULT CAVTapi::USBGetPhoneFromAddress(
|
|
IN ITAddress2* pAddress,
|
|
OUT ITPhone** ppPhone
|
|
)
|
|
{
|
|
//
|
|
// Enumerate the phones
|
|
//
|
|
|
|
IEnumPhone* pPhones = NULL;
|
|
HRESULT hr = pAddress->EnumeratePhones(&pPhones);
|
|
if( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Parse the phones enumeration and try to find out
|
|
// if we have a Phone object. For this we should
|
|
// enumerate the terminals and find out if the phone
|
|
// supports both audio terminals: capture and render
|
|
//
|
|
|
|
ITPhone* pPhone = NULL;
|
|
ULONG cPhoneFetched = 0;
|
|
while( pPhones->Next(1, &pPhone, &cPhoneFetched) == S_OK)
|
|
{
|
|
//
|
|
// Enumerate terminals
|
|
//
|
|
IEnumTerminal* pTerminals = NULL;
|
|
hr = pPhone->EnumerateTerminals(pAddress, &pTerminals);
|
|
if( FAILED(hr) )
|
|
{
|
|
pPhone->Release();
|
|
pPhone = NULL;
|
|
continue; // Go to another phone object
|
|
}
|
|
|
|
//
|
|
// The phone should support both audio
|
|
// terminals: capture & render
|
|
//
|
|
|
|
BOOL bCapture = FALSE;
|
|
BOOL bRender = FALSE;
|
|
|
|
//
|
|
// Parse terminals enumeration
|
|
//
|
|
ITTerminal* pTerminal = NULL;
|
|
ULONG cTermFetched = 0;
|
|
BSTR bstrCapture = NULL;
|
|
BSTR bstrRender = NULL;
|
|
|
|
while( pTerminals->Next(1, &pTerminal, &cTermFetched) == S_OK)
|
|
{
|
|
//Get direction
|
|
TERMINAL_DIRECTION Dir;
|
|
hr = pTerminal->get_Direction(&Dir);
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
// Capture?
|
|
if( TD_CAPTURE == Dir )
|
|
{
|
|
// Clean-up
|
|
if( bstrCapture )
|
|
{
|
|
SysFreeString( bstrCapture );
|
|
bstrCapture = NULL;
|
|
}
|
|
|
|
// Get terminal name
|
|
pTerminal->get_Name( &bstrCapture );
|
|
bCapture = TRUE;
|
|
} else if( TD_RENDER == Dir )
|
|
{
|
|
if( bstrRender )
|
|
{
|
|
SysFreeString( bstrRender );
|
|
bstrRender = NULL;
|
|
}
|
|
pTerminal->get_Name( &bstrRender );
|
|
bRender = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Clean-up terminal
|
|
//
|
|
pTerminal->Release();
|
|
}
|
|
|
|
//
|
|
// Clean-up terminals
|
|
//
|
|
pTerminals->Release();
|
|
|
|
//
|
|
// The phone should support both directions
|
|
//
|
|
if( bCapture && bRender )
|
|
{
|
|
//
|
|
// We keep the reference to pPhone
|
|
// We'll release this reference later
|
|
//
|
|
*ppPhone = pPhone;
|
|
|
|
//
|
|
// Save the terminals names
|
|
//
|
|
|
|
m_bstrUSBCaptureTerm = SysAllocString(bstrCapture);
|
|
m_bstrUSBRenderTerm = SysAllocString(bstrRender);
|
|
|
|
//
|
|
// Clean-up
|
|
//
|
|
if( bstrCapture )
|
|
{
|
|
SysFreeString( bstrCapture );
|
|
bstrCapture = NULL;
|
|
}
|
|
|
|
if( bstrRender )
|
|
{
|
|
SysFreeString( bstrRender );
|
|
bstrRender = NULL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Clean-up
|
|
pPhone->Release();
|
|
pPhone = NULL;
|
|
|
|
if( bstrCapture )
|
|
{
|
|
SysFreeString( bstrCapture );
|
|
bstrCapture = NULL;
|
|
}
|
|
if( bstrRender )
|
|
{
|
|
SysFreeString( bstrRender );
|
|
bstrRender = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Clean-up the phones enumeration
|
|
//
|
|
|
|
pPhones->Release();
|
|
|
|
return (pPhone != NULL) ? S_OK : E_FAIL;
|
|
}
|
|
|
|
/*++
|
|
USBPhoneInitialize
|
|
|
|
Initialize (open, set handling on true)
|
|
the phone object.
|
|
Is called by USBFindPhone
|
|
--*/
|
|
HRESULT CAVTapi::USBPhoneInitialize(
|
|
IN ITPhone* pPhone
|
|
)
|
|
{
|
|
//
|
|
// Get the registry value
|
|
//
|
|
|
|
BOOL bUSBEnabled = USBGetCheckboxValue(FALSE);
|
|
|
|
//
|
|
// Open the Phone
|
|
//
|
|
if( !bUSBEnabled )
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Error object
|
|
//
|
|
CErrorInfo er;
|
|
er.set_Operation( IDS_ER_USB );
|
|
er.set_Details( IDS_ER_USB_OPEN );
|
|
HRESULT hr = pPhone->Open( PP_OWNER );
|
|
er.set_hr( hr );
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
USBSetCheckboxValue( FALSE );
|
|
m_bUSBOpened = FALSE;
|
|
return S_FALSE;
|
|
}
|
|
|
|
//
|
|
// MArk as opened
|
|
//
|
|
|
|
m_bUSBOpened = TRUE;
|
|
|
|
//
|
|
// Set the registry with the handset terminals
|
|
//
|
|
USBRegPutTerminals();
|
|
|
|
//
|
|
// Get the ITAutomatedPhoneControl interface
|
|
//
|
|
|
|
er.set_hr( S_OK );
|
|
er.set_Details( IDS_ER_USB_INITIALIZE );
|
|
|
|
ITAutomatedPhoneControl* pAutomated = NULL;
|
|
hr = pPhone->QueryInterface(
|
|
IID_ITAutomatedPhoneControl, (void**)&pAutomated);
|
|
er.set_hr( hr );
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
USBSetCheckboxValue( FALSE );
|
|
m_bUSBOpened = FALSE;
|
|
pPhone->Close();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Set on true Phone handling
|
|
//
|
|
|
|
hr = pAutomated->put_PhoneHandlingEnabled(VARIANT_TRUE);
|
|
er.set_hr( hr );
|
|
if( FAILED(hr) )
|
|
{
|
|
USBSetCheckboxValue( FALSE );
|
|
m_bUSBOpened = FALSE;
|
|
pAutomated->Release();
|
|
pPhone->Close();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Clean-up
|
|
//
|
|
pAutomated->Release();
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
USBSetHandling - call put_PhoneHandlingEnabled
|
|
after read the registry value
|
|
--*/
|
|
HRESULT CAVTapi::USBSetHandling(
|
|
IN BOOL bUSBEnabled
|
|
)
|
|
{
|
|
m_critUSBPhone.Lock();
|
|
|
|
if( m_pUSBPhone == NULL )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// If the phone is enabled, try to Open
|
|
//
|
|
|
|
CErrorInfo er;
|
|
er.set_Operation( IDS_ER_USB );
|
|
er.set_Details( IDS_ER_USB_INITIALIZE );
|
|
|
|
if( bUSBEnabled )
|
|
{
|
|
//
|
|
// Try to open if is necessary
|
|
//
|
|
if( m_bUSBOpened == FALSE )
|
|
{
|
|
HRESULT hr = m_pUSBPhone->Open( PP_OWNER );
|
|
if( FAILED(hr) )
|
|
{
|
|
//
|
|
// Reset the registry and the mark
|
|
//
|
|
m_bUSBOpened = FALSE;
|
|
USBSetCheckboxValue( FALSE );
|
|
er.set_hr( hr );
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Set the registry with the handset terminals
|
|
//
|
|
USBRegPutTerminals();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Try to close the phone
|
|
// First let's see if we have a call selected on the phone
|
|
// object
|
|
//
|
|
ITAutomatedPhoneControl* pAutomated = NULL;
|
|
HRESULT hr = m_pUSBPhone->QueryInterface(
|
|
IID_ITAutomatedPhoneControl,
|
|
(void**)&pAutomated
|
|
);
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
IEnumCall* pEnumCalls = NULL;
|
|
hr = pAutomated->EnumerateSelectedCalls(&pEnumCalls);
|
|
|
|
// Clean-up
|
|
pAutomated->Release();
|
|
pAutomated = NULL;
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
// Close the phone
|
|
m_pUSBPhone->Close();
|
|
USBRegDelTerminals();
|
|
m_bUSBOpened = FALSE;
|
|
USBSetCheckboxValue( FALSE );
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Browse for selected calls
|
|
//
|
|
BOOL bSelectedCalls = FALSE;
|
|
ITCallInfo* pCallInfo = NULL;
|
|
ULONG uFetched = 0;
|
|
hr = pEnumCalls->Next(1, &pCallInfo, &uFetched);
|
|
|
|
// Clean-up
|
|
pEnumCalls->Release();
|
|
pEnumCalls = NULL;
|
|
|
|
//
|
|
// Is there a call on this phone object?
|
|
//
|
|
if( hr == S_OK)
|
|
{
|
|
pCallInfo->Release();
|
|
pCallInfo = NULL;
|
|
|
|
bSelectedCalls = TRUE;
|
|
}
|
|
|
|
//
|
|
// Do we have selected calls
|
|
//
|
|
if( bSelectedCalls )
|
|
{
|
|
//
|
|
// Do not close the phone
|
|
//
|
|
if( m_bUSBOpened )
|
|
{
|
|
// Reset the registry value
|
|
USBSetCheckboxValue( TRUE );
|
|
|
|
// Error message
|
|
er.set_Details( IDS_ER_USB_CLOSE );
|
|
er.set_hr(E_FAIL);
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return E_FAIL;
|
|
}
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
m_pUSBPhone->Close();
|
|
USBRegDelTerminals();
|
|
m_bUSBOpened = FALSE;
|
|
USBSetCheckboxValue( FALSE );
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Is already opened?
|
|
//
|
|
if( m_bUSBOpened == bUSBEnabled )
|
|
{
|
|
// Don't try twice to open the USBPhone
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Get the ITAutomatedPhoneControl interface
|
|
//
|
|
|
|
ITAutomatedPhoneControl* pAutomated = NULL;
|
|
HRESULT hr = m_pUSBPhone->QueryInterface(
|
|
IID_ITAutomatedPhoneControl,
|
|
(void**)&pAutomated
|
|
);
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
//
|
|
// Reset the registry and the mark
|
|
//
|
|
m_pUSBPhone->Close();
|
|
USBRegDelTerminals();
|
|
m_bUSBOpened = FALSE;
|
|
USBSetCheckboxValue( FALSE );
|
|
er.set_hr( hr );
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Set on true Phone handling
|
|
//
|
|
|
|
hr = pAutomated->put_PhoneHandlingEnabled( (bUSBEnabled ? VARIANT_TRUE : VARIANT_FALSE) );
|
|
if( FAILED(hr) )
|
|
{
|
|
//
|
|
// Reset the registry and the mark
|
|
//
|
|
m_pUSBPhone->Close();
|
|
USBRegDelTerminals();
|
|
m_bUSBOpened = FALSE;
|
|
USBSetCheckboxValue( FALSE );
|
|
er.set_hr( hr );
|
|
|
|
// Clean-up
|
|
pAutomated->Release();
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
// Save in registry
|
|
m_bUSBOpened = TRUE;
|
|
USBSetCheckboxValue( TRUE );
|
|
|
|
// Clean-up
|
|
pAutomated->Release();
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
Get the USB handset terminals name
|
|
--*/
|
|
HRESULT CAVTapi::USBGetTerminalName(
|
|
IN AVTerminalDirection Direction,
|
|
OUT BSTR* pbstrName
|
|
)
|
|
{
|
|
*pbstrName = NULL;
|
|
|
|
if( Direction == AVTERM_CAPTURE )
|
|
{
|
|
if( m_bstrUSBCaptureTerm )
|
|
{
|
|
*pbstrName = SysAllocString( m_bstrUSBCaptureTerm );
|
|
}
|
|
else
|
|
{
|
|
*pbstrName = SysAllocString( _T("") );
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
if( Direction == AVTERM_RENDER )
|
|
{
|
|
if( m_bstrUSBRenderTerm )
|
|
{
|
|
*pbstrName = SysAllocString( m_bstrUSBRenderTerm );
|
|
}
|
|
else
|
|
{
|
|
*pbstrName = SysAllocString( _T("") );
|
|
}
|
|
return S_OK;
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT CAVTapi::USBSetVolume(
|
|
IN AVTerminalDirection avDirection,
|
|
OUT long nVolume
|
|
)
|
|
{
|
|
m_critUSBPhone.Lock();
|
|
|
|
TCHAR szTrace[256];
|
|
_stprintf( szTrace, _T("VLDTRACE * USBSetVolume - Dir=%d, Vol=%d\n"), avDirection, nVolume);
|
|
OutputDebugString( szTrace );
|
|
|
|
TERMINAL_DIRECTION TermDirection = TD_CAPTURE;
|
|
|
|
//
|
|
// Get terminal direction
|
|
//
|
|
switch(avDirection)
|
|
{
|
|
case AVTERM_CAPTURE:
|
|
TermDirection = TD_CAPTURE;
|
|
break;
|
|
case AVTERM_RENDER:
|
|
TermDirection = TD_RENDER;
|
|
break;
|
|
default:
|
|
m_critUSBPhone.Unlock();
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
//
|
|
// Check the phone object
|
|
//
|
|
if(NULL == m_pUSBPhone)
|
|
{
|
|
// no phone
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Get automated interface
|
|
//
|
|
ITAutomatedPhoneControl* pAutomated = NULL;
|
|
HRESULT hr = m_pUSBPhone->QueryInterface(
|
|
IID_ITAutomatedPhoneControl,
|
|
(void**)&pAutomated
|
|
);
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Get the call selected on the phone
|
|
//
|
|
IEnumCall* pEnumCalls = NULL;
|
|
hr = pAutomated->EnumerateSelectedCalls( &pEnumCalls );
|
|
|
|
//
|
|
// Clean-up
|
|
//
|
|
pAutomated->Release();
|
|
pAutomated = NULL;
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Get the calls
|
|
//
|
|
ITCallInfo* pCallInfo = NULL;
|
|
ULONG uFetched = 0;
|
|
while( S_OK == pEnumCalls->Next(1, &pCallInfo, &uFetched))
|
|
{
|
|
// Get the address
|
|
ITAddress* pAddress = NULL;
|
|
HRESULT hr = pCallInfo->get_Address(&pAddress);
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
// Enumerate terminals on this address
|
|
IEnumTerminal* pEnumTerminals = NULL;
|
|
hr = m_pUSBPhone->EnumerateTerminals( pAddress, &pEnumTerminals);
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
ITTerminal* pTerminal = NULL;
|
|
ULONG uFetched = 0;
|
|
|
|
while( S_OK == pEnumTerminals->Next(1, &pTerminal, &uFetched))
|
|
{
|
|
// Get the direction
|
|
TERMINAL_DIRECTION Direction = TD_CAPTURE;
|
|
hr = pTerminal->get_Direction(&Direction);
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
if( Direction == TermDirection )
|
|
{
|
|
// Get ITBasicAudioTerminal interface
|
|
ITBasicAudioTerminal* pAudio = NULL;
|
|
hr = pTerminal->QueryInterface(IID_ITBasicAudioTerminal, (void**)&pAudio);
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
// Set the volume
|
|
pAudio->put_Volume( nVolume);
|
|
|
|
// Set the member volume
|
|
if( TermDirection == TD_CAPTURE)
|
|
{
|
|
m_nUSBInVolume = nVolume;
|
|
}
|
|
else
|
|
{
|
|
m_nUSBOutVolume = nVolume;
|
|
}
|
|
|
|
// Clean-up
|
|
pAudio->Release();
|
|
pAudio = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clean-up
|
|
pTerminal->Release();
|
|
pTerminal = NULL;
|
|
}
|
|
|
|
//Clean-up
|
|
pEnumTerminals->Release();
|
|
pEnumTerminals = NULL;
|
|
}
|
|
|
|
// Clean-up
|
|
pAddress->Release();
|
|
pAddress = NULL;
|
|
}
|
|
|
|
// Clean-up
|
|
pCallInfo->Release();
|
|
pCallInfo = NULL;
|
|
}
|
|
|
|
//
|
|
// Clean-up
|
|
//
|
|
pEnumCalls->Release();
|
|
pEnumCalls = NULL;
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CAVTapi::USBGetVolume(
|
|
IN AVTerminalDirection avDirection,
|
|
OUT long* pVolume
|
|
)
|
|
{
|
|
m_critUSBPhone.Lock();
|
|
|
|
TERMINAL_DIRECTION TermDirection =
|
|
(avDirection == AVTERM_CAPTURE) ?
|
|
TD_CAPTURE :
|
|
TD_RENDER;
|
|
|
|
*pVolume = USB_NULLVOLUME;
|
|
|
|
//
|
|
// Set volume
|
|
//
|
|
*pVolume = (avDirection == AVTERM_CAPTURE) ?
|
|
m_nUSBInVolume :
|
|
m_nUSBOutVolume;
|
|
//
|
|
// Check the phone object
|
|
//
|
|
if(NULL == m_pUSBPhone)
|
|
{
|
|
// no phone
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Validate direction
|
|
//
|
|
if( (avDirection != AVTERM_CAPTURE) &&
|
|
(avDirection != AVTERM_RENDER) )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
//
|
|
// The volume was initialized?
|
|
//
|
|
if( *pVolume != USB_NULLVOLUME )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Let's go and initialize the volume
|
|
// Get automated interface
|
|
//
|
|
ITAutomatedPhoneControl* pAutomated = NULL;
|
|
HRESULT hr = m_pUSBPhone->QueryInterface(
|
|
IID_ITAutomatedPhoneControl,
|
|
(void**)&pAutomated
|
|
);
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Get the call selected on the phone
|
|
//
|
|
IEnumCall* pEnumCalls = NULL;
|
|
hr = pAutomated->EnumerateSelectedCalls( &pEnumCalls );
|
|
|
|
//
|
|
// Clean-up
|
|
//
|
|
pAutomated->Release();
|
|
pAutomated = NULL;
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Get the calls
|
|
//
|
|
ITCallInfo* pCallInfo = NULL;
|
|
ULONG uFetched = 0;
|
|
long nVolume = USB_NULLVOLUME;
|
|
HRESULT hrVolume = S_OK;
|
|
while( S_OK == pEnumCalls->Next(1, &pCallInfo, &uFetched))
|
|
{
|
|
// Get the address
|
|
ITAddress* pAddress = NULL;
|
|
HRESULT hr = pCallInfo->get_Address(&pAddress);
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
// Enumerate terminals on this address
|
|
IEnumTerminal* pEnumTerminals = NULL;
|
|
hr = m_pUSBPhone->EnumerateTerminals( pAddress, &pEnumTerminals);
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
ITTerminal* pTerminal = NULL;
|
|
ULONG uFetched = 0;
|
|
|
|
while( S_OK == pEnumTerminals->Next(1, &pTerminal, &uFetched))
|
|
{
|
|
// Get the direction
|
|
TERMINAL_DIRECTION Direction = TD_CAPTURE;
|
|
hr = pTerminal->get_Direction(&Direction);
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
if( Direction == TermDirection )
|
|
{
|
|
// Get ITBasicAudioTerminal interface
|
|
ITBasicAudioTerminal* pAudio = NULL;
|
|
hr = pTerminal->QueryInterface(IID_ITBasicAudioTerminal, (void**)&pAudio);
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
// Set the volume
|
|
hrVolume = pAudio->get_Volume( &nVolume);
|
|
|
|
// Clean-up
|
|
pAudio->Release();
|
|
pAudio = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clean-up
|
|
pTerminal->Release();
|
|
pTerminal = NULL;
|
|
}
|
|
|
|
//Clean-up
|
|
pEnumTerminals->Release();
|
|
pEnumTerminals = NULL;
|
|
}
|
|
|
|
// Clean-up
|
|
pAddress->Release();
|
|
pAddress = NULL;
|
|
}
|
|
|
|
// Clean-up
|
|
pCallInfo->Release();
|
|
pCallInfo = NULL;
|
|
}
|
|
|
|
//
|
|
// Clean-up
|
|
//
|
|
pEnumCalls->Release();
|
|
pEnumCalls = NULL;
|
|
|
|
if( FAILED(hrVolume) )
|
|
{
|
|
TCHAR szTrace[256];
|
|
_stprintf( szTrace, _T("VLDTRECE * Erro get_Volume 0x%08x\n"), hr);
|
|
OutputDebugString( szTrace );
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return hrVolume;
|
|
}
|
|
|
|
//
|
|
// Set the volume
|
|
//
|
|
*pVolume = nVolume;
|
|
if( (avDirection == AVTERM_CAPTURE) )
|
|
{
|
|
m_nUSBInVolume = nVolume;
|
|
}
|
|
else
|
|
{
|
|
m_nUSBOutVolume = nVolume;
|
|
}
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*++
|
|
USBRegPutTerminals
|
|
It is called when the phone is opened.
|
|
Add in the registry the name of the handset terminals
|
|
--*/
|
|
HRESULT CAVTapi::USBRegPutTerminals(
|
|
)
|
|
{
|
|
// Create the registry key, its a combination of redial and USB terminals
|
|
CRegKey regKey;
|
|
TCHAR szText[255], szType[50];
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_KEY, szText, ARRAYSIZE(szText) );
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_USBTERMS , szType, ARRAYSIZE(szType) );
|
|
_tcscat( szText, _T("\\") );
|
|
_tcscat( szText, szType );
|
|
regKey.Create( HKEY_CURRENT_USER, szText );
|
|
|
|
if ( regKey.m_hKey == NULL)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// Capture terminal
|
|
//
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_USBCAPTURE, szText, ARRAYSIZE(szText) );
|
|
regKey.SetValue( (m_bstrUSBCaptureTerm!=NULL)? m_bstrUSBCaptureTerm : _T(""), szText );
|
|
|
|
//
|
|
// Render terminal
|
|
//
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_USBRENDER, szText, ARRAYSIZE(szText) );
|
|
regKey.SetValue( (m_bstrUSBRenderTerm!=NULL)? m_bstrUSBRenderTerm : _T(""), szText );
|
|
|
|
//
|
|
// Close the registry key
|
|
//
|
|
regKey.Close();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
USBRegDelTerminals
|
|
Delete the resgistry entry for USB handset terminals
|
|
Is caled when USB handset is called
|
|
--*/
|
|
HRESULT CAVTapi::USBRegDelTerminals(
|
|
)
|
|
{
|
|
// Create the registry key, its a combination of redial and USB terminals
|
|
CRegKey regKey;
|
|
TCHAR szText[255], szType[50];
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_KEY, szText, ARRAYSIZE(szText) );
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_USBTERMS , szType, ARRAYSIZE(szType) );
|
|
_tcscat( szText, _T("\\") );
|
|
_tcscat( szText, szType );
|
|
regKey.Create( HKEY_CURRENT_USER, szText );
|
|
|
|
if ( regKey.m_hKey == NULL)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// Capture terminal
|
|
//
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_USBCAPTURE, szText, ARRAYSIZE(szText) );
|
|
regKey.SetValue( _T(""), szText );
|
|
|
|
//
|
|
// Render terminal
|
|
//
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_USBRENDER, szText, ARRAYSIZE(szText) );
|
|
regKey.SetValue( _T(""), szText );
|
|
|
|
//
|
|
// Close the registry key
|
|
//
|
|
regKey.Close();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*++
|
|
DoneRegistration
|
|
|
|
Is called by AVDialer after IConnectionPoint::Advise method
|
|
was called
|
|
--*/
|
|
STDMETHODIMP CAVTapi::DoneRegistration()
|
|
{
|
|
//
|
|
// Signal the event
|
|
// The Dialer just register as client for the events
|
|
//
|
|
|
|
if( m_hEventDialerReg)
|
|
{
|
|
SetEvent( m_hEventDialerReg );
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
USBReserveStreamForPhone
|
|
|
|
Is called by CreateTerminalArray
|
|
Allocate a termnal name that represents
|
|
'Don't select a terminal for this stream'
|
|
That stream will be reserved for the Phone terminals
|
|
return;
|
|
S_OK - the stream was reserved and pbstrTerminal was allocated
|
|
by USBReserveStreamForPhone.
|
|
E_FAIL - the stream wasn't reserved
|
|
--*/
|
|
HRESULT CAVTapi::USBReserveStreamForPhone(
|
|
IN UINT nStream,
|
|
OUT BSTR* pbstrTerminal
|
|
)
|
|
{
|
|
|
|
if( (nStream != IDN_REG_REDIAL_TERMINAL_AUDIO_CAPTURE) &&
|
|
(nStream != IDN_REG_REDIAL_TERMINAL_AUDIO_RENDER))
|
|
{
|
|
//
|
|
// Phone works just on Audio streams
|
|
//
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if( !USBGetCheckboxValue() )
|
|
{
|
|
//
|
|
// We are not interested in USBPhone
|
|
//
|
|
return E_FAIL;
|
|
}
|
|
|
|
TCHAR szTemp[255];
|
|
int nRetVal = LoadString( _Module.GetResourceInstance(), IDS_NONE_DEVICE, szTemp, ARRAYSIZE(szTemp) );
|
|
if( 0 == nRetVal )
|
|
{
|
|
//
|
|
// No resource string
|
|
//
|
|
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
*pbstrTerminal = SysAllocString( T2COLE(szTemp) );
|
|
if( NULL == *pbstrTerminal )
|
|
{
|
|
// E_OUTOFMEMORY
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*++
|
|
AECGetRegistryValue
|
|
Read the flag from the registry
|
|
++*/
|
|
BOOL CAVTapi::AECGetRegistryValue(
|
|
)
|
|
{
|
|
BOOL bAEC = FALSE;
|
|
//
|
|
// Read the registry for the previous
|
|
// setting for AEC
|
|
//
|
|
|
|
TCHAR szText[255], szType[255];
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_KEY, szText, ARRAYSIZE(szText) );
|
|
|
|
CRegKey regKey;
|
|
if( regKey.Open( HKEY_CURRENT_USER, szText, KEY_READ )!= ERROR_SUCCESS)
|
|
{
|
|
return bAEC;
|
|
};
|
|
|
|
//
|
|
// Read data
|
|
//
|
|
|
|
DWORD dwValue = 0;
|
|
LoadString( _Module.GetResourceInstance(), IDN_REG_REDIAL_AEC, szType, ARRAYSIZE(szType) );
|
|
if( regKey.QueryValue(dwValue, szType) != ERROR_SUCCESS)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return (BOOL)dwValue;
|
|
}
|
|
|
|
/*++
|
|
Sets on Audio capture the AEC on true
|
|
--*/
|
|
HRESULT CAVTapi::AECSetOnStream(
|
|
IN ITStreamControl *pStreamControl,
|
|
IN BOOL bAEC
|
|
)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
//
|
|
// Get the streams
|
|
//
|
|
IEnumStream *pEnumStreams = NULL;
|
|
hr = pStreamControl->EnumerateStreams(&pEnumStreams);
|
|
if( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Go to the audio capture stream
|
|
//
|
|
|
|
ITStream *pStream = NULL;
|
|
|
|
while(pEnumStreams->Next(1, &pStream, NULL) == S_OK)
|
|
{
|
|
//
|
|
// Get media type and the direction
|
|
//
|
|
long lStreamMediaMode = 0;
|
|
TERMINAL_DIRECTION nStreamDir = TD_CAPTURE;
|
|
|
|
pStream->get_Direction( &nStreamDir );
|
|
pStream->get_MediaType( &lStreamMediaMode );
|
|
|
|
//
|
|
// Set the audio AEC on audio capture streams
|
|
//
|
|
|
|
if( (lStreamMediaMode == TAPIMEDIATYPE_AUDIO) &&
|
|
(nStreamDir == TD_CAPTURE) )
|
|
{
|
|
//
|
|
// Get ITAudioDeviceControl interface
|
|
//
|
|
ITAudioDeviceControl* pAudioDevice = NULL;
|
|
hr = pStream->QueryInterface(
|
|
IID_ITAudioDeviceControl,
|
|
(void**)&pAudioDevice
|
|
);
|
|
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
//
|
|
// Set the value for AEC
|
|
//
|
|
hr = pAudioDevice->Set(
|
|
AudioDevice_AcousticEchoCancellation,
|
|
bAEC,
|
|
TAPIControl_Flags_None
|
|
);
|
|
|
|
//
|
|
// Clean-up
|
|
//
|
|
pAudioDevice->Release();
|
|
pAudioDevice = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Clean up the stream
|
|
//
|
|
pStream->Release();
|
|
pStream = NULL;
|
|
}
|
|
|
|
//
|
|
// Clean-up the enumeration
|
|
//
|
|
pEnumStreams->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CAVTapi::USBAnswer()
|
|
{
|
|
// Critical Section
|
|
m_critUSBPhone.Lock();
|
|
|
|
// We don't have USB phone
|
|
if( m_pUSBPhone == NULL)
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
// IS the phone opened ?
|
|
if( !m_bUSBOpened )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return S_OK;
|
|
}
|
|
|
|
// Get the automated interface
|
|
ITAutomatedPhoneControl* pAutomated = NULL;
|
|
HRESULT hr = m_pUSBPhone->QueryInterface(
|
|
IID_ITAutomatedPhoneControl,
|
|
(void**)&pAutomated);
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
// Get the calls selected on this phone object
|
|
IEnumCall* pEnumCalls = NULL;
|
|
hr = pAutomated->EnumerateSelectedCalls(&pEnumCalls);
|
|
// Clean-up
|
|
pAutomated->Release();
|
|
pAutomated = NULL;
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
// Browse the enumeration
|
|
ITCallInfo* pCallInfo = NULL;
|
|
ULONG uFetched = 0;
|
|
|
|
// Get the first call. Right now the phone supports just one
|
|
// call. In the future, if the phone will support many calls
|
|
// there should be a method to find out what is the call for
|
|
// this event
|
|
hr = pEnumCalls->Next(1, &pCallInfo, &uFetched);
|
|
|
|
// Clean-up the enumeration
|
|
pEnumCalls->Release();
|
|
pEnumCalls = NULL;
|
|
|
|
if( pCallInfo == NULL )
|
|
{
|
|
m_critUSBPhone.Unlock();
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
//
|
|
// Get the ITBasicCallControl
|
|
//
|
|
ITBasicCallControl* pControl = NULL;
|
|
hr = pCallInfo->QueryInterface(
|
|
IID_ITBasicCallControl,
|
|
(void**)&pControl);
|
|
if( FAILED(hr) )
|
|
{
|
|
// Clean-up the call
|
|
pCallInfo->Release();
|
|
pCallInfo = NULL;
|
|
|
|
m_critUSBPhone.Unlock();
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Get IAVCall interface
|
|
//
|
|
IAVTapiCall* pAVCall = FindAVTapiCall( pControl );
|
|
if( pAVCall == NULL )
|
|
{
|
|
// Clean-up the call
|
|
pControl->Release();
|
|
pControl = NULL;
|
|
|
|
pCallInfo->Release();
|
|
pCallInfo = NULL;
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// Select terminals and the preview window
|
|
//
|
|
hr = AnswerAction(
|
|
pCallInfo,
|
|
pControl,
|
|
pAVCall,
|
|
TRUE
|
|
);
|
|
|
|
// Clean-up the call
|
|
pAVCall->Release();
|
|
pAVCall = NULL;
|
|
pControl->Release();
|
|
pControl = NULL;
|
|
pCallInfo->Release();
|
|
pCallInfo = NULL;
|
|
|
|
m_critUSBPhone.Unlock();
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CAVTapi::AnswerAction(
|
|
IN ITCallInfo* pInfo,
|
|
IN ITBasicCallControl* pControl,
|
|
IN IAVTapiCall* pAVCall,
|
|
IN BOOL bUSBAnswer
|
|
)
|
|
{
|
|
if( (pInfo == NULL) ||
|
|
(pControl == NULL) )
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
CThreadAnswerInfo *pAnswerInfo = new CThreadAnswerInfo;
|
|
if ( pAnswerInfo == NULL )
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
//
|
|
// Was 'Take call' answer (FALSE) or
|
|
// a USB phone answer
|
|
//
|
|
pAnswerInfo->m_bUSBAnswer = bUSBAnswer;
|
|
|
|
if ( SUCCEEDED(hr = pAnswerInfo->set_AVTapiCall(pAVCall)) &&
|
|
SUCCEEDED(hr = pAnswerInfo->set_ITCallInfo(pInfo)) &&
|
|
SUCCEEDED(hr = pAnswerInfo->set_ITBasicCallControl(pControl)) )
|
|
{
|
|
DWORD dwID = 0;
|
|
HANDLE hThread = CreateThread( NULL, 0, ThreadAnswerProc, (void *) pAnswerInfo, NULL, &dwID );
|
|
if ( !hThread )
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
delete pAnswerInfo;
|
|
}
|
|
else
|
|
{
|
|
CloseHandle( hThread );
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|