INCOMING.CPP

#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
);
}