#define UNICODE
#include <list>
#include <windows.h>
#include <tapi3.h>
#include "callnot.h"
#include "resource.h"
//////////////////////////////////////////////////////////
// T3IN.EXE
//
// Sample application that handling incoming TAPI calls.
// In order to receive incoming calls, the application must
// implement and register the outgoing ITCallNotification
// interface.
//
// This application will register to recieve calls on
// all addresses that support at least audioin and audioout.
//
// 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;
//////////////////////////////////////////////////////////
// TYPEDEFS
//////////////////////////////////////////////////////////
using namespace std;
typedef list<CCallNotification *> CCallNotificationPtrList;
//////////////////////////////////////////////////////////
// GLOBALS
//////////////////////////////////////////////////////////
HINSTANCE ghInst;
ITTAPI * gpTapi;
ITBasicCallControl * gpCall;
HWND ghDlg = NULL;
BSTR gbstrAudioIn;
BSTR gbstrAudioOut;
BSTR gbstrVideoIn;
BSTR gbstrVideoOut;
WCHAR gszTapi30[] = L"TAPI 3.0 Incoming Call Sample";
// list of outgoing interface objects we have registered
CCallNotificationPtrList gpCallNotificationList;
//////////////////////////////////////////////////////////
// PROTOTYPES
//////////////////////////////////////////////////////////
BOOL
CALLBACK
MainDialogProc(
HWND hDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);
HRESULT
GetMediaTerminal(
ITAddress *,
BSTR bstrMedia,
ITMediaTerminal ** ppMediaTerminal
);
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
);
//////////////////////////////////////////////////////////
//
// FUNCTIONS
//
//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
// WinMain
//////////////////////////////////////////////////////////
int
WINAPI
WinMain(
HINSTANCE hInst,
HINSTANCE hPrevInst,
LPSTR lpCmdLine,
int nCmdShow
)
{
ghInst = hInst;
// need to coinit
if (!SUCCEEDED(CoInitialize(NULL)))
{
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
ShutdownTapi();
CoUninitialize();
return 1;
}
//////////////////////////////////////////////////////////////
// InitializeTapi
//
// Various initializations
///////////////////////////////////////////////////////////////
HRESULT
InitializeTapi()
{
HRESULT hr;
LPWSTR psz;
// cocreate the TAPI object
hr = CoCreateInstance(
CLSID_TAPI,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITTAPI,
(LPVOID *)&gpTapi
);
if (hr != S_OK)
{
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 (S_OK != hr)
{
DoMessage(L"TAPI failed to initialize");
gpTapi->Release();
gpTapi = NULL;
return hr;
}
// convert the TAPIMEDIATYPEs to BSTRs for
// convenience throughout the program
StringFromIID( TAPIMEDIATYPE_AudioIn, &psz );
gbstrAudioIn = SysAllocString( psz );
CoTaskMemFree( psz );
StringFromIID( TAPIMEDIATYPE_AudioOut, &psz );
gbstrAudioOut = SysAllocString( psz );
CoTaskMemFree( psz );
StringFromIID( TAPIMEDIATYPE_VideoIn, &psz );
gbstrVideoIn = SysAllocString( psz );
CoTaskMemFree( psz );
StringFromIID( TAPIMEDIATYPE_VideoOut, &psz );
gbstrVideoOut = SysAllocString( psz );
CoTaskMemFree( psz );
// find all address objects that
// we will use to listen for calls on
hr = ListenOnAddresses();
if (S_OK != hr)
{
DoMessage(L"Could not find any addresses to listen on");
gpTapi->Release();
gpTapi = NULL;
return hr;
}
return S_OK;
}
///////////////////////////////////////////////////////////////
// ShutdownTapi
///////////////////////////////////////////////////////////////
void
ShutdownTapi()
{
CCallNotificationPtrList::iterator i;
// if there is still a call,
// release it
if (NULL != gpCall)
{
gpCall->Release();
gpCall = NULL;
}
// Release all CallNotification objects
for (
i = gpCallNotificationList.begin();
i != gpCallNotificationList.end();
i++
)
{
(*i)->Shutdown();
// At this point, we should call Unadvise on
// the ConnectionPoint. However, TAPI 3.0
// does not support Unadvise for the ITCallNotification
// interface. So, here we just release the object
(*i)->Release();
}
// release main object.
if (NULL != gpTapi)
{
gpTapi->Shutdown();
gpTapi->Release();
}
// free the BSTRs
SysFreeString( gbstrAudioIn );
SysFreeString( gbstrAudioOut );
SysFreeString( gbstrVideoIn );
SysFreeString( gbstrVideoOut );
}
///////////////////////////////////////////////////////////////////////////
// MainDlgProc
///////////////////////////////////////////////////////////////////////////
BOOL
CALLBACK
MainDialogProc(
HWND hDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
switch (uMsg)
{
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) )
{
// dial request
case IDC_ANSWER:
{
SetStatusMessage(L"Answering...");
// answer the call
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;
}
// disconnect request
case IDC_DISCONNECT:
{
SetStatusMessage(L"Disconnecting...");
// disconnect
if (S_OK != DisconnectTheCall())
{
DoMessage(L"Disconnect failed");
}
return 1;
}
// disconnected notification
case IDC_DISCONNECTED:
{
// release
ReleaseTheCall();
EnableButton( IDOK );
DisableButton( IDC_DISCONNECT );
SetStatusMessage(L"Waiting for a call...");
return 1;
}
default:
return 0;
}
}
default:
return 0;
}
}
////////////////////////////////////////////////////////////////////////
// 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;
ITMediaSupport * pMediaSupport;
VARIANT_BOOL bSupport;
// enumerate the addresses
hr = gpTapi->_EnumerateAddresses( &pEnumAddress );
if (S_OK != hr)
{
return hr;
}
while ( TRUE )
{
// get the next address
hr = pEnumAddress->Next( 1, &pAddress, NULL );
if (S_OK != hr)
{
break;
}
pAddress->QueryInterface( IID_ITMediaSupport, (void **)&pMediaSupport );
// does it support AudioIn?
pMediaSupport->QueryMediaType(
gbstrAudioIn,
&bSupport
);
if (bSupport)
{
// does it also support AudioOut?
pMediaSupport->QueryMediaType(
gbstrAudioOut,
&bSupport
);
if (bSupport)
{
// listen
hr = ListenOnThisAddress( pAddress );
if (S_OK != hr)
{
DoMessage(L"Listen failed on an address");
}
}
}
pMediaSupport->Release();
pAddress->Release();
}
pEnumAddress->Release();
return S_OK;
}
///////////////////////////////////////////////////////////////////
// ListenOnThisAddress
//
// Perform the steps involved in setting up an address to listen
// for calls. Setting up an application to listen for calls is a two
// step process.
//
// First, the app must call RegisterCallTypes
// on the address that it wants calls on
//
// Second, it must register it's implementation of ITCallNotification
// with the address. Registration is done via COM's ConnectionPoint
// methods. See the COM documentation for more informations on
// ConnectionPoints
//
///////////////////////////////////////////////////////////////////
HRESULT
ListenOnThisAddress(
ITAddress * pAddress
)
{
HRESULT hr = S_OK;
IConnectionPointContainer * pCPC;
IConnectionPoint * pCP;
CCallNotification * pCallNotification;
DWORD dwCookie;
VARIANT var;
// RegisterCallTypes takes an array (in a variant) of
// MediaTypes for the address to listen for.
// If NULL is specified for the array, that means to listen
// for all the media types that the address supports.
//
// Since we already know the address supports audioin/audioout,
// just tell it to listen for all types. Also, we will tell
// it that we are only interested in being the owner of calls,
// not monitoring
VariantInit( &var );
var.vt = VT_ARRAY;
var.parray = NULL;
hr = pAddress->RegisterCallTypes(
FALSE,
TRUE, // Want to be owner
FALSE, // Don't want to be monitor
var // mediatypes
);
if (S_OK != hr)
{
return hr;
}
// now we need to register our outgoing interface through
// the ConnectionPoint methods.
// First we create our CCallNotification object
pCallNotification = new CCallNotification;
if (NULL == pCallNotification)
{
return E_OUTOFMEMORY;
}
// initialize the object
hr = pCallNotification->Initialize( pAddress );
if (S_OK != hr)
{
pCallNotification->Release();
return hr;
}
// get the IConnectionPointContainer interface
// from the address object
hr = pAddress->QueryInterface(
IID_IConnectionPointContainer,
(void **)&pCPC
);
if (S_OK != hr)
{
pCallNotification->Release();
pCallNotification = NULL;
return hr;
}
// find the ConnectionPoint we are interested in
hr = pCPC->FindConnectionPoint( IID_ITCallNotification, &pCP );
pCPC->Release();
if (S_OK != hr)
{
pCallNotification->Release();
pCallNotification = NULL;
return hr;
}
// call the advise function
//
// if this function succeeds, the address will start
// listening for calls.
hr = pCP->Advise(
(IUnknown *)pCallNotification,
&dwCookie
);
pCP->Release();
if (S_OK != hr)
{
pCallNotification->Release();
pCallNotification = NULL;
return hr;
}
// save the notification object
// note that normally you would want
// to save the dwCookie as well. Refer
// to the COM ConnectionPoint documentation
// for more details.
gpCallNotificationList.push_back( pCallNotification );
return S_OK;
}
/////////////////////////////////////////////////////////
// GetMediaTerminal
//
// Creates a MediaTerminal for the bstrMediaType passed
// in, using the default terminal for the bstrMediaType
//
/////////////////////////////////////////////////////////
HRESULT
GetMediaTerminal(
ITAddress * pAddress,
BSTR bstrMediaType,
ITMediaTerminal ** ppMediaTerminal
)
{
HRESULT hr = S_OK;
ITTerminalSupport * pTerminalSupport;
ITTerminal * pTerminal;
// get the terminal support interface
pAddress->QueryInterface( IID_ITTerminalSupport, (void **)&pTerminalSupport );
// get the default terminal for MediaType
hr = pTerminalSupport->GetDefaultTerminal(
bstrMediaType,
&pTerminal
);
pTerminalSupport->Release();
if (S_OK != hr)
{
return hr;
}
// Create a media terminal for MediaType
// Use the default terminal we just got
hr = gpTapi->CreateMediaTerminal(
bstrMediaType,
pTerminal,
ppMediaTerminal
);
pTerminal->Release();
if (S_OK != hr)
{
return hr;
}
return S_OK;
}
/////////////////////////////////////////////////////////
// GetVideoInMediaTerminal
//
// Creates a MediaTerminal for the VideoIn mediatype
// This is a dynamic terminal type.
//
/////////////////////////////////////////////////////////
HRESULT
GetVideoInMediaTerminal(
ITAddress * pAddress,
ITMediaTerminal ** ppMediaTerminal
)
{
HRESULT hr = S_OK;
ITTerminalSupport * pTerminalSupport;
ITTerminal * pTerminal;
// get the terminal support interface
pAddress->QueryInterface( IID_ITTerminalSupport, (void **)&pTerminalSupport );
BSTR bstrTerminalClass;
LPOLESTR lpTerminalClass;
StringFromIID(
CLSID_VideoWindowTerm,
&lpTerminalClass
);
bstrTerminalClass = SysAllocString ( lpTerminalClass );
CoTaskMemFree( lpTerminalClass );
hr = pTerminalSupport->CreateTerminal(
bstrTerminalClass,
&pTerminal
);
SysFreeString( bstrTerminalClass );
pTerminalSupport->Release();
if (FAILED(hr))
{
return hr;
}
// Create a media terminal for MediaType
// Use the default terminal we just got
hr = gpTapi->CreateMediaTerminal(
gbstrVideoIn,
pTerminal,
ppMediaTerminal
);
pTerminal->Release();
if (S_OK != hr)
{
return hr;
}
return S_OK;
}
/////////////////////////////////////////////////////////////////
// CreateMediaTerminals
//
// Create audioin and audioout terminals. Videoin and videoout terminals
// are created only when the address supports them.
//
// This function assumes that ppMediaTerminals has a size no less than four.
/////////////////////////////////////////////////////////////////
HRESULT
CreateMediaTerminals(
ITAddress *pAddress,
ITMediaTerminal ** ppMediaTerminals,
PLONG pNumMediaTerminals
)
{
int count = 0;
HRESULT hr;
// get the mediaterminal for audioin
hr = GetMediaTerminal(
pAddress,
gbstrAudioIn,
&ppMediaTerminals[count]
);
if (S_OK != hr)
{
return hr;
}
count ++;
// get the mediaterminal for audioout
hr = GetMediaTerminal(
pAddress,
gbstrAudioOut,
&ppMediaTerminals[count]
);
if (S_OK != hr)
{
ppMediaTerminals[0]->Release();
return hr;
}
count ++;
// Find out if the address supports video.
ITMediaSupport *pMediaSupport;
VARIANT_BOOL bSupport;
pAddress->QueryInterface( IID_ITMediaSupport, (void **)&pMediaSupport );
// does it support VideoIn?
pMediaSupport->QueryMediaType(
gbstrVideoIn,
&bSupport
);
if (bSupport)
{
// get the mediaterminal for VideoIn
hr = GetVideoInMediaTerminal(
pAddress,
&ppMediaTerminals[count]
);
if (S_OK == hr)
{
count ++;
}
// does it also support VideoOut?
pMediaSupport->QueryMediaType(
gbstrVideoOut,
&bSupport
);
if (bSupport)
{
// get the mediaterminal for Videoout
hr = GetMediaTerminal(
pAddress,
gbstrVideoOut,
&ppMediaTerminals[count]
);
if (S_OK == hr)
{
count ++;
}
}
}
pMediaSupport->Release();
*pNumMediaTerminals = count;
return S_OK;
}
/////////////////////////////////////////////////////////////////
// CreateMediaTerminalSafeArray
//
/////////////////////////////////////////////////////////////////
SAFEARRAY *
CreateMediaTerminalSafeArray(
ITMediaTerminal ** ppMediaTerminals,
LONG nNumMediaTerminals
)
{
SAFEARRAY * psa;
SAFEARRAYBOUND sabound[1];
// create a safearray with two elements
// to pass the mediaterminals to tapi
sabound[0].lLbound = 0;
sabound[0].cElements = nNumMediaTerminals;
psa = SafeArrayCreate(
VT_UNKNOWN,
1,
sabound
);
if (NULL != psa)
{
// save them in the safearray
for (long i = 0; i < nNumMediaTerminals; i ++)
{
SafeArrayPutElement(
psa,
&i,
ppMediaTerminals[i]
);
}
}
return psa;
}
/////////////////////////////////////////////////////////////////
// ReleaseMediaTerminals
//
/////////////////////////////////////////////////////////////////
void
ReleaseMediaTerminals(
ITMediaTerminal ** ppMediaTerminals,
LONG nNumMediaTerminals
)
{
for (long i = 0; i < nNumMediaTerminals; i ++)
{
if (ppMediaTerminals[i])
{
ppMediaTerminals[i]->Release();
}
}
}
/////////////////////////////////////////////////////////////////////
// Answer the call
/////////////////////////////////////////////////////////////////////
HRESULT
AnswerTheCall()
{
HRESULT hr;
ITCallInfo * pCallInfo;
ITAddress * pAddress;
ITMediaTerminal * ppMediaTerminals[MAXTERMINALS];
long nNumMediaTerminals = MAXTERMINALS;
SAFEARRAY * psa;
if (NULL == gpCall)
{
return E_UNEXPECTED;
}
// get the address object of this call
gpCall->QueryInterface( IID_ITCallInfo, (void**)&pCallInfo );
pCallInfo->get_Address( &pAddress );
pCallInfo->Release();
// create the media terminals for this call
hr = CreateMediaTerminals(
pAddress,
ppMediaTerminals,
&nNumMediaTerminals
);
// release the address
pAddress->Release();
if (S_OK != hr)
{
gpCall->Release();
gpCall = NULL;
return hr;
}
psa = CreateMediaTerminalSafeArray(ppMediaTerminals, nNumMediaTerminals);
if (S_OK != hr)
{
gpCall->Release();
gpCall = NULL;
ReleaseMediaTerminals(ppMediaTerminals, nNumMediaTerminals);
return hr;
}
// put the safearray in a variant
VARIANT var;
VariantInit(&var);
var.vt = VT_ARRAY;
var.parray = psa;
// call SelectMediaTerminals
hr = gpCall->SelectMediaTerminals(
var
);
ReleaseMediaTerminals(ppMediaTerminals, nNumMediaTerminals);
SafeArrayDestroy( psa );
if (S_OK != hr)
{
gpCall->Release();
gpCall = NULL;
return hr;
}
// 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 );
gpCall->Release();
gpCall = NULL;
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
);
}