1104 lines
25 KiB
C++
1104 lines
25 KiB
C++
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation. All Rights Reserved.
|
|
|
|
--*/
|
|
|
|
#define UNICODE
|
|
#include <list>
|
|
#include <windows.h>
|
|
#include <tapi3.h>
|
|
#include <control.h>
|
|
#include "callnot.h"
|
|
#include "resource.h"
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// T3IN.EXE
|
|
//
|
|
// Sample application that handles incoming TAPI calls.
|
|
// In order to receive incoming calls, the application must
|
|
// implement and register the outgoing ITCallNotification
|
|
// interface.
|
|
//
|
|
// This application will register to receive calls on
|
|
// all addresses that support at least the audio media type.
|
|
//
|
|
// NOTE: This application is limited to working with one
|
|
// call at at time, and will not work correctly if multiple
|
|
// calls are present at the same time.
|
|
//////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// constants
|
|
//////////////////////////////////////////////////////////
|
|
|
|
const DWORD MAXTERMINALS = 5;
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// GLOBALS
|
|
//////////////////////////////////////////////////////////
|
|
|
|
HINSTANCE ghInst;
|
|
ITTAPI * gpTapi;
|
|
ITBasicCallControl * gpCall;
|
|
HWND ghDlg = NULL;
|
|
|
|
BOOL gfAutoAnswer = FALSE;
|
|
|
|
WCHAR gszTapi30[] = L"TAPI 3.0 Incoming Call Sample";
|
|
|
|
|
|
ULONG gulAdvise;
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// PROTOTYPES
|
|
//////////////////////////////////////////////////////////
|
|
|
|
INT_PTR
|
|
CALLBACK
|
|
MainDialogProc(
|
|
HWND hDlg,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
);
|
|
|
|
HRESULT
|
|
RegisterTapiEventInterface(CTAPIEventNotification *pTAPIEventNotification);
|
|
|
|
HRESULT
|
|
ListenOnAddresses();
|
|
|
|
HRESULT
|
|
ListenOnThisAddress(
|
|
ITAddress * pAddress
|
|
);
|
|
|
|
HRESULT
|
|
AnswerTheCall();
|
|
|
|
HRESULT
|
|
DisconnectTheCall();
|
|
|
|
void
|
|
ReleaseTheCall();
|
|
|
|
void
|
|
DoMessage(
|
|
LPWSTR pszMessage
|
|
);
|
|
|
|
void
|
|
SetStatusMessage(
|
|
LPWSTR pszMessage
|
|
);
|
|
|
|
HRESULT
|
|
InitializeTapi();
|
|
|
|
void
|
|
ShutdownTapi();
|
|
|
|
void
|
|
EnableButton(
|
|
int ID
|
|
);
|
|
void
|
|
DisableButton(
|
|
int ID
|
|
);
|
|
|
|
BOOL
|
|
AddressSupportsMediaType(
|
|
ITAddress * pAddress,
|
|
long lMediaType
|
|
);
|
|
|
|
//////////////////////////////////////////////////////////
|
|
//
|
|
// FUNCTIONS
|
|
//
|
|
//////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////
|
|
// WinMain
|
|
//////////////////////////////////////////////////////////
|
|
int
|
|
WINAPI
|
|
WinMain(
|
|
HINSTANCE hInst,
|
|
HINSTANCE hPrevInst,
|
|
LPSTR lpCmdLine,
|
|
int nCmdShow
|
|
)
|
|
{
|
|
ghInst = hInst;
|
|
|
|
//
|
|
// Initialize COM.
|
|
//
|
|
|
|
if (!SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// do all tapi initialization
|
|
//
|
|
|
|
if (S_OK != InitializeTapi())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// everything is initialized, so
|
|
// start the main dialog box
|
|
//
|
|
|
|
DialogBox(
|
|
ghInst,
|
|
MAKEINTRESOURCE(IDD_MAINDLG),
|
|
NULL,
|
|
MainDialogProc
|
|
);
|
|
|
|
|
|
//
|
|
// clean up tapi
|
|
//
|
|
|
|
ShutdownTapi();
|
|
|
|
//
|
|
// Shut down COM.
|
|
//
|
|
|
|
CoUninitialize();
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
// InitializeTapi
|
|
//
|
|
// Various tapi initializations
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
HRESULT
|
|
InitializeTapi()
|
|
{
|
|
HRESULT hr;
|
|
|
|
//
|
|
// cocreate the TAPI object
|
|
//
|
|
|
|
hr = CoCreateInstance(
|
|
CLSID_TAPI,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_ITTAPI,
|
|
(LPVOID *)&gpTapi
|
|
);
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
DoMessage(L"CoCreateInstance on TAPI failed");
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// call initialize. this must be called before
|
|
// any other tapi functions are called.
|
|
//
|
|
|
|
hr = gpTapi->Initialize();
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
DoMessage(L"TAPI failed to initialize");
|
|
|
|
gpTapi->Release();
|
|
gpTapi = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Create our own event notification object and register it
|
|
// see callnot.h and callnot.cpp
|
|
//
|
|
|
|
CTAPIEventNotification *pTAPIEventNotification = new CTAPIEventNotification;
|
|
|
|
hr = RegisterTapiEventInterface(pTAPIEventNotification);
|
|
|
|
|
|
//
|
|
// no longer need a reference to the callback object. tapi will keep a
|
|
// reference to it for as long as it is using it.
|
|
//
|
|
|
|
pTAPIEventNotification->Release();
|
|
|
|
|
|
|
|
// Set the Event filter to only give us only the events we process
|
|
gpTapi->put_EventFilter(TE_CALLNOTIFICATION | TE_CALLSTATE | TE_CALLMEDIA);
|
|
|
|
|
|
//
|
|
// find all address objects that
|
|
// we will use to listen for calls on
|
|
//
|
|
|
|
hr = ListenOnAddresses();
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
DoMessage(L"Could not find any addresses to listen on");
|
|
|
|
gpTapi->Release();
|
|
gpTapi = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
// ShutdownTapi
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
ShutdownTapi()
|
|
{
|
|
//
|
|
// if there is still a call, release it
|
|
//
|
|
|
|
if (NULL != gpCall)
|
|
{
|
|
gpCall->Release();
|
|
gpCall = NULL;
|
|
}
|
|
|
|
//
|
|
// release main object.
|
|
//
|
|
|
|
if (NULL != gpTapi)
|
|
{
|
|
gpTapi->Shutdown();
|
|
gpTapi->Release();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// MainDlgProc
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
INT_PTR
|
|
CALLBACK
|
|
MainDialogProc(
|
|
HWND hDlg,
|
|
UINT uMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_PRIVATETAPIEVENT:
|
|
{
|
|
OnTapiEvent(
|
|
(TAPI_EVENT) wParam,
|
|
(IDispatch *) lParam
|
|
);
|
|
|
|
return 0;
|
|
}
|
|
|
|
case WM_INITDIALOG:
|
|
{
|
|
//
|
|
// set up dialog
|
|
//
|
|
|
|
ghDlg = hDlg;
|
|
|
|
DisableButton( IDC_ANSWER );
|
|
DisableButton( IDC_DISCONNECT );
|
|
|
|
SetStatusMessage( L"Waiting for a call..." );
|
|
|
|
return 0;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
if ( LOWORD(wParam) == IDCANCEL )
|
|
{
|
|
//
|
|
// quit
|
|
//
|
|
|
|
EndDialog( hDlg, 0 );
|
|
|
|
return 1;
|
|
}
|
|
|
|
switch ( LOWORD(wParam) )
|
|
{
|
|
case IDC_AUTOANSWER:
|
|
{
|
|
//
|
|
// auto answer check box was fiddled around with
|
|
//
|
|
|
|
gfAutoAnswer = !gfAutoAnswer;
|
|
return 1;
|
|
}
|
|
|
|
case IDC_ANSWER:
|
|
{
|
|
//
|
|
// answer the call
|
|
//
|
|
|
|
SetStatusMessage(L"Answering...");
|
|
|
|
if ( S_OK == AnswerTheCall() )
|
|
{
|
|
SetStatusMessage(L"Connected");
|
|
|
|
EnableButton( IDC_DISCONNECT );
|
|
DisableButton( IDC_ANSWER );
|
|
}
|
|
else
|
|
{
|
|
DisableButton( IDC_ANSWER );
|
|
DoMessage(L"Answer failed");
|
|
SetStatusMessage(L"Waiting for a call...");
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
case IDC_DISCONNECT:
|
|
{
|
|
//
|
|
// disconnect request
|
|
//
|
|
|
|
SetStatusMessage(L"Disconnecting...");
|
|
if (S_OK != DisconnectTheCall())
|
|
{
|
|
DoMessage(L"Disconnect failed");
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
case IDC_DISCONNECTED:
|
|
{
|
|
//
|
|
// disconnected notification -- release the call
|
|
//
|
|
|
|
ReleaseTheCall();
|
|
|
|
EnableButton( IDOK );
|
|
DisableButton( IDC_DISCONNECT );
|
|
|
|
SetStatusMessage(L"Waiting for a call...");
|
|
|
|
return 1;
|
|
}
|
|
default:
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
default:
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// RegisterTapiEventInterface(CTAPIEventNotification *pTAPIEventNotification)
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT
|
|
RegisterTapiEventInterface(CTAPIEventNotification *pTAPIEventNotification)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IConnectionPointContainer * pCPC;
|
|
IConnectionPoint * pCP;
|
|
|
|
|
|
hr = gpTapi->QueryInterface(
|
|
IID_IConnectionPointContainer,
|
|
(void **)&pCPC
|
|
);
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = pCPC->FindConnectionPoint(
|
|
IID_ITTAPIEventNotification,
|
|
&pCP
|
|
);
|
|
|
|
pCPC->Release();
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = pCP->Advise(
|
|
pTAPIEventNotification,
|
|
&gulAdvise
|
|
);
|
|
|
|
pCP->Release();
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// ListenOnAddresses
|
|
//
|
|
// This procedure will find all addresses that support audioin and audioout
|
|
// and will call ListenOnThisAddress to start listening on it.
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT
|
|
ListenOnAddresses()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IEnumAddress * pEnumAddress;
|
|
ITAddress * pAddress;
|
|
|
|
//
|
|
// enumerate the addresses
|
|
//
|
|
|
|
hr = gpTapi->EnumerateAddresses( &pEnumAddress );
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
while ( TRUE )
|
|
{
|
|
//
|
|
// get the next address
|
|
//
|
|
|
|
hr = pEnumAddress->Next( 1, &pAddress, NULL );
|
|
|
|
if (S_OK != hr)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// does the address support audio?
|
|
//
|
|
|
|
if ( AddressSupportsMediaType(pAddress, TAPIMEDIATYPE_AUDIO) )
|
|
{
|
|
//
|
|
// If it does then we'll listen.
|
|
//
|
|
|
|
hr = ListenOnThisAddress( pAddress );
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
DoMessage(L"Listen failed on an address");
|
|
}
|
|
}
|
|
|
|
pAddress->Release();
|
|
}
|
|
|
|
pEnumAddress->Release();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
// ListenOnThisAddress
|
|
//
|
|
// We call RegisterCallNotifications to inform TAPI that we want
|
|
// notifications of calls on this address. We already resistered
|
|
// our notification interface with TAPI, so now we are just telling
|
|
// TAPI that we want calls from this address to trigger events on
|
|
// our existing notification interface.
|
|
//
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT
|
|
ListenOnThisAddress(
|
|
ITAddress * pAddress
|
|
)
|
|
{
|
|
|
|
//
|
|
// RegisterCallNotifications takes a media type bitmap indicating
|
|
// the set of media types we are interested in. We know the
|
|
// address supports audio, but let's add in video as well
|
|
// if the address supports it.
|
|
//
|
|
|
|
long lMediaTypes = TAPIMEDIATYPE_AUDIO;
|
|
|
|
if ( AddressSupportsMediaType(pAddress, TAPIMEDIATYPE_VIDEO) )
|
|
{
|
|
lMediaTypes |= TAPIMEDIATYPE_VIDEO;
|
|
}
|
|
|
|
|
|
HRESULT hr;
|
|
long lRegister;
|
|
|
|
hr = gpTapi->RegisterCallNotifications(
|
|
pAddress,
|
|
VARIANT_TRUE,
|
|
VARIANT_TRUE,
|
|
lMediaTypes,
|
|
0,
|
|
&lRegister
|
|
);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// IsVideoCaptureStream
|
|
//
|
|
// Returns true if the stream is for video capture
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
BOOL
|
|
IsVideoCaptureStream(
|
|
ITStream * pStream
|
|
)
|
|
{
|
|
TERMINAL_DIRECTION tdStreamDirection;
|
|
long lStreamMediaType;
|
|
|
|
if ( FAILED( pStream ->get_Direction(&tdStreamDirection) ) ) { return FALSE; }
|
|
if ( FAILED( pStream ->get_MediaType(&lStreamMediaType) ) ) { return FALSE; }
|
|
|
|
return (tdStreamDirection == TD_CAPTURE) &&
|
|
(lStreamMediaType == TAPIMEDIATYPE_VIDEO);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////
|
|
// GetVideoRenderTerminal
|
|
//
|
|
// Creates a dynamic terminal for the Video Render mediatype / direction
|
|
//
|
|
/////////////////////////////////////////////////////////
|
|
HRESULT
|
|
GetVideoRenderTerminal(
|
|
ITAddress * pAddress,
|
|
ITTerminal ** ppTerminal
|
|
)
|
|
{
|
|
//
|
|
// Construct a BSTR for the correct IID.
|
|
//
|
|
|
|
LPOLESTR lpTerminalClass;
|
|
|
|
HRESULT hr;
|
|
|
|
hr = StringFromIID(CLSID_VideoWindowTerm,
|
|
&lpTerminalClass);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
BSTR bstrTerminalClass;
|
|
|
|
bstrTerminalClass = SysAllocString ( lpTerminalClass );
|
|
|
|
CoTaskMemFree( lpTerminalClass );
|
|
|
|
if ( bstrTerminalClass == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// Get the terminal support interface
|
|
//
|
|
|
|
ITTerminalSupport * pTerminalSupport;
|
|
|
|
hr = pAddress->QueryInterface(IID_ITTerminalSupport,
|
|
(void **)&pTerminalSupport);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
//
|
|
// Create the video render terminal.
|
|
//
|
|
|
|
hr = pTerminalSupport->CreateTerminal(bstrTerminalClass,
|
|
TAPIMEDIATYPE_VIDEO,
|
|
TD_RENDER,
|
|
ppTerminal);
|
|
|
|
pTerminalSupport->Release();
|
|
|
|
//
|
|
// We do not make the window visible now -- we will do that
|
|
// when the CME_STREAM_ACTIVE event is received for the stream
|
|
// (and we have placed the window within our frame).
|
|
//
|
|
}
|
|
|
|
SysFreeString( bstrTerminalClass );
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// EnablePreview
|
|
//
|
|
// Selects a video render terminal on a video capture stream,
|
|
// thereby enabling video preview.
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT
|
|
EnablePreview(
|
|
ITAddress * pAddress,
|
|
ITStream * pStream
|
|
)
|
|
{
|
|
ITTerminal * pTerminal;
|
|
|
|
HRESULT hr = GetVideoRenderTerminal(pAddress, &pTerminal);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
hr = pStream->SelectTerminal(pTerminal);
|
|
|
|
pTerminal->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////
|
|
// GetTerminal
|
|
//
|
|
// Creates the default terminal for the passed-in stream.
|
|
//
|
|
/////////////////////////////////////////////////////////
|
|
HRESULT
|
|
GetTerminal(
|
|
ITAddress * pAddress,
|
|
ITStream * pStream,
|
|
ITTerminal ** ppTerminal
|
|
)
|
|
{
|
|
//
|
|
// Determine the media type and direction of this stream.
|
|
//
|
|
|
|
HRESULT hr;
|
|
long lMediaType;
|
|
TERMINAL_DIRECTION dir;
|
|
|
|
hr = pStream->get_MediaType( &lMediaType );
|
|
if ( FAILED(hr) ) return hr;
|
|
|
|
hr = pStream->get_Direction( &dir );
|
|
if ( FAILED(hr) ) return hr;
|
|
|
|
//
|
|
// Since video render is a dynamic terminal, the procedure for creating
|
|
// it is different.
|
|
//
|
|
|
|
if ( ( lMediaType == TAPIMEDIATYPE_VIDEO ) &&
|
|
( dir == TD_RENDER ) )
|
|
{
|
|
return GetVideoRenderTerminal(pAddress, ppTerminal);
|
|
}
|
|
|
|
//
|
|
// For all other terminals we use GetDefaultStaticTerminal.
|
|
// First, get the terminal support interface.
|
|
//
|
|
|
|
ITTerminalSupport * pTerminalSupport;
|
|
|
|
hr = pAddress->QueryInterface( IID_ITTerminalSupport,
|
|
(void **)&pTerminalSupport);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
//
|
|
// get the default terminal for this MediaType and direction
|
|
//
|
|
|
|
hr = pTerminalSupport->GetDefaultStaticTerminal(lMediaType,
|
|
dir,
|
|
ppTerminal);
|
|
|
|
pTerminalSupport->Release();
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// SelectTerminalOnCall
|
|
//
|
|
// Selects a given terminal on the first compatible stream that
|
|
// exists on the given call.
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT
|
|
SelectTerminalsOnCall(
|
|
ITAddress * pAddress,
|
|
ITBasicCallControl * pCall
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
//
|
|
// get the ITStreamControl interface for this call
|
|
//
|
|
|
|
ITStreamControl * pStreamControl;
|
|
|
|
hr = pCall->QueryInterface(IID_ITStreamControl,
|
|
(void **) &pStreamControl);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
//
|
|
// enumerate the streams
|
|
//
|
|
|
|
IEnumStream * pEnumStreams;
|
|
|
|
hr = pStreamControl->EnumerateStreams(&pEnumStreams);
|
|
|
|
pStreamControl->Release();
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
//
|
|
// for each stream
|
|
//
|
|
|
|
ITStream * pStream;
|
|
|
|
while ( S_OK == pEnumStreams->Next(1, &pStream, NULL) )
|
|
{
|
|
ITTerminal * pTerminal;
|
|
|
|
//
|
|
// Find out the media type and direction of this stream,
|
|
// and create the default terminal for this media type and
|
|
// direction.
|
|
//
|
|
|
|
hr = GetTerminal(pAddress,
|
|
pStream,
|
|
&pTerminal);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
//
|
|
// Select the terminal on the stream.
|
|
//
|
|
|
|
hr = pStream->SelectTerminal(pTerminal);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
//
|
|
// Also enable preview on the video capture stream.
|
|
//
|
|
|
|
if ( IsVideoCaptureStream( pStream ) )
|
|
{
|
|
EnablePreview( pAddress, pStream );
|
|
}
|
|
}
|
|
|
|
pTerminal->Release();
|
|
}
|
|
|
|
pStream->Release();
|
|
}
|
|
|
|
pEnumStreams->Release();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// Answer the call
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT
|
|
AnswerTheCall()
|
|
{
|
|
HRESULT hr;
|
|
ITCallInfo * pCallInfo;
|
|
ITAddress * pAddress;
|
|
|
|
|
|
|
|
if (NULL == gpCall)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
|
|
//
|
|
// get the address object of this call
|
|
//
|
|
|
|
hr = gpCall->QueryInterface( IID_ITCallInfo, (void**)&pCallInfo );
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
gpCall->Release();
|
|
gpCall = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
hr = pCallInfo->get_Address( &pAddress );
|
|
|
|
pCallInfo->Release();
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
gpCall->Release();
|
|
gpCall = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Select our terminals on the call; if any of the selections fail
|
|
// we proceed without that terminal
|
|
//
|
|
|
|
hr = SelectTerminalsOnCall( pAddress, gpCall );
|
|
|
|
pAddress->Release();
|
|
|
|
//
|
|
// Now we can actually answer the call
|
|
//
|
|
|
|
hr = gpCall->Answer();
|
|
|
|
return hr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// DisconnectTheCall
|
|
//
|
|
// Disconnects the call
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT
|
|
DisconnectTheCall()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (NULL != gpCall)
|
|
{
|
|
hr = gpCall->Disconnect( DC_NORMAL );
|
|
|
|
// Do not release the call yet, as that would prevent
|
|
// us from receiving the disconnected notification.
|
|
|
|
return hr;
|
|
}
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// ReleaseTheCall
|
|
//
|
|
// Releases the call
|
|
//////////////////////////////////////////////////////////////////////
|
|
void
|
|
ReleaseTheCall()
|
|
{
|
|
if (NULL != gpCall)
|
|
{
|
|
gpCall->Release();
|
|
gpCall = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
//
|
|
// HELPER FUNCTIONS
|
|
//
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
// DoMessage
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
DoMessage(
|
|
LPWSTR pszMessage
|
|
)
|
|
{
|
|
MessageBox(
|
|
ghDlg,
|
|
pszMessage,
|
|
gszTapi30,
|
|
MB_OK
|
|
);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
// SetStatusMessage
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
SetStatusMessage(
|
|
LPWSTR pszMessage
|
|
)
|
|
{
|
|
SetDlgItemText(
|
|
ghDlg,
|
|
IDC_STATUS,
|
|
pszMessage
|
|
);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
// EnableButton
|
|
//
|
|
// Enable, make default, and setfocus to a button
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
EnableButton(
|
|
int ID
|
|
)
|
|
{
|
|
SendDlgItemMessage(
|
|
ghDlg,
|
|
ID,
|
|
BM_SETSTYLE,
|
|
BS_DEFPUSHBUTTON,
|
|
0
|
|
);
|
|
EnableWindow(
|
|
GetDlgItem( ghDlg, ID ),
|
|
TRUE
|
|
);
|
|
SetFocus(
|
|
GetDlgItem( ghDlg, ID )
|
|
);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
// DisableButton
|
|
//
|
|
// Disable a button
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
DisableButton(
|
|
int ID
|
|
)
|
|
{
|
|
SendDlgItemMessage(
|
|
ghDlg,
|
|
ID,
|
|
BM_SETSTYLE,
|
|
BS_PUSHBUTTON,
|
|
0
|
|
);
|
|
EnableWindow(
|
|
GetDlgItem( ghDlg, ID ),
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
// AddressSupportsMediaType
|
|
//
|
|
// Finds out if the given address supports the given media
|
|
// type, and returns TRUE if it does.
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
BOOL
|
|
AddressSupportsMediaType(
|
|
ITAddress * pAddress,
|
|
long lMediaType
|
|
)
|
|
{
|
|
VARIANT_BOOL bSupport = VARIANT_FALSE;
|
|
ITMediaSupport * pMediaSupport;
|
|
|
|
if ( SUCCEEDED( pAddress->QueryInterface( IID_ITMediaSupport,
|
|
(void **)&pMediaSupport ) ) )
|
|
{
|
|
//
|
|
// does it support this media type?
|
|
//
|
|
|
|
pMediaSupport->QueryMediaType(
|
|
lMediaType,
|
|
&bSupport
|
|
);
|
|
|
|
pMediaSupport->Release();
|
|
}
|
|
|
|
return (bSupport == VARIANT_TRUE);
|
|
}
|