OUTGOING.CPP

#define UNICODE 
#include <windows.h>
#include <tapi3.h>
#include "resource.h"

//////////////////////////////////////////////////////////
// T3OUT.EXE
//
// Example of making an outgoing call with TAPI 3.0
//
// This application will allow a user to make a call
// by using TAPI 3.0. The application will simply look
// for the first TAPI line that support Audio, and can
// dial a phone number. It will then use that line to
// make calls.
//
// This application does not handle incoming calls, and
// does not process incoming messages.
//
//////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////
// Constants
//////////////////////////////////////////////////////////

const DWORD ADDRESSLENGTH = 128;
const DWORD MAXTERMINALS = 5;

const WCHAR * const gszTapi30 = L"TAPI 3.0 Outgoing Call Sample";

const WCHAR * const gszConferenceName = L"Conference Name";
const WCHAR * const gszEmailName = L"Email Name";
const WCHAR * const gszMachineName = L"Machine Name";
const WCHAR * const gszPhoneNumber = L"Phone Number";
const WCHAR * const gszIPAddress = L"IP Address";

//////////////////////////////////////////////////////////
// GLOBALS
//////////////////////////////////////////////////////////
HINSTANCE ghInst;
HWND ghDlg = NULL;
ITTAPI * gpTapi;
ITAddress * gpAddress;
ITBasicCallControl * gpCall;

BSTR gbstrAudioIn;
BSTR gbstrAudioOut;
BSTR gbstrVideoIn;
BSTR gbstrVideoOut;


//////////////////////////////////////////////////////////
// PROTOTYPES
//////////////////////////////////////////////////////////
BOOL
CALLBACK
MainDialogProc(
HWND hDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);

HRESULT
FindAnAddress(
DWORD dwAddressType
);

HRESULT
GetMediaTerminal(
BSTR bstrMedia,
ITMediaTerminal ** ppMediaTerminal
);

HRESULT
GetVideoInMediaTerminal(
ITMediaTerminal ** ppMediaTerminal
);

HRESULT
MakeTheCall(
DWORD dwAddressType,
PWCHAR szAddressToCall
);

HRESULT
DisconnectTheCall();

void
DoMessage(
LPWSTR pszMessage
);

HRESULT
InitializeTapi();

void
ShutdownTapi();

void
EnableButton(
HWND hDlg,
int ID
);
void
DisableButton(
HWND hDlg,
int ID
);

//////////////////////////////////////////////////////////
// WinMain
//////////////////////////////////////////////////////////
int
WINAPI
WinMain(
HINSTANCE hInst,
HINSTANCE hPrevInst,
LPSTR lpCmdLine,
int nCmdShow
)
{
ghInst = hInst;


// need to coinit
if (!SUCCEEDED(CoInitialize(NULL)))
{
return 0;
}

if (S_OK != InitializeTapi())
{
return 0;
}

// everything is initialized, so
// start the main dialog box
DialogBox(
ghInst,
MAKEINTRESOURCE(IDD_MAINDLG),
NULL,
MainDialogProc
);


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

return S_OK;
}


///////////////////////////////////////////////////////////////
// ShutdownTapi
///////////////////////////////////////////////////////////////
void
ShutdownTapi()
{
// if there is still a call,
// release it
if (NULL != gpCall)
{
gpCall->Release();
gpCall = NULL;
}

// if we have an address object
// release it
if (NULL != gpAddress)
{
gpAddress->Release();
}

// release main object.
if (NULL != gpTapi)
{
gpTapi->Shutdown();
gpTapi->Release();
}

SysFreeString( gbstrAudioIn );
SysFreeString( gbstrAudioOut );
SysFreeString( gbstrVideoIn );
SysFreeString( gbstrVideoOut );
}

///////////////////////////////////////////////////////////////////////////
// InitAddressTypeComboBox
//
// Put address type string in the combo box
// and save the addresstype with the string
//
///////////////////////////////////////////////////////////////////////////
void
InitAddressTypeComboBox(
HWND hComboBox
)
{
int i;

i = SendMessage( hComboBox, CB_ADDSTRING, 0, (long)gszConferenceName );

SendMessage(
hComboBox,
CB_SETITEMDATA ,
i,
(long)T3_ADDRESSTYPE_CONFERENCENAME
);


i = SendMessage( hComboBox, CB_ADDSTRING, 0, (long)gszEmailName );

SendMessage(
hComboBox,
CB_SETITEMDATA ,
i,
(long)T3_ADDRESSTYPE_EMAILNAME
);


i = SendMessage( hComboBox, CB_ADDSTRING, 0, (long)gszMachineName );

SendMessage(
hComboBox,
CB_SETITEMDATA ,
i,
(long)T3_ADDRESSTYPE_DOMAINNAME
);


i = SendMessage( hComboBox, CB_ADDSTRING, 0, (long)gszPhoneNumber );

SendMessage(
hComboBox,
CB_SETITEMDATA ,
i,
(long)T3_ADDRESSTYPE_PHONENUMBER
);


SendMessage( hComboBox, CB_SETCURSEL, i, 0 );

i = SendMessage( hComboBox, CB_ADDSTRING, 0, (long)gszIPAddress );

SendMessage(
hComboBox,
CB_SETITEMDATA ,
i,
(long)T3_ADDRESSTYPE_IPADDRESS
);

}

///////////////////////////////////////////////////////////////////////////
// MainDlgProc
///////////////////////////////////////////////////////////////////////////
BOOL
CALLBACK
MainDialogProc(
HWND hDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
switch (uMsg)
{
case WM_INITDIALOG:
{
HWND hComboBox;


// set up dialog
ghDlg = hDlg;

EnableButton( hDlg, IDOK );
DisableButton( hDlg, IDC_DISCONNECT );

hComboBox = GetDlgItem( hDlg, IDC_ADDRESSTYPE );

InitAddressTypeComboBox(hComboBox);

SetFocus( hComboBox );

return 0;
}

case WM_COMMAND:
{
if ( LOWORD(wParam) == IDCANCEL )
{
// quit
EndDialog( hDlg, 0 );

return 1;
}

// dial request
if ( LOWORD(wParam) == IDOK )
{
HWND hComboBox;
DWORD dwIndex;
DWORD dwAddressType;
WCHAR szAddressToCall[ADDRESSLENGTH];


// get the address type the user selected.
hComboBox = GetDlgItem( hDlg, IDC_ADDRESSTYPE );
dwIndex = SendMessage( hComboBox, CB_GETCURSEL, 0, 0 );

dwAddressType = SendMessage(
hComboBox,
CB_GETITEMDATA,
dwIndex,
0
);

// get the address the user wants to call
GetDlgItemText(
hDlg,
IDC_ADDRESS,
szAddressToCall,
ADDRESSLENGTH
);

// make the call
if ( S_OK == MakeTheCall(dwAddressType, szAddressToCall) )
{
EnableButton( hDlg, IDC_DISCONNECT );
DisableButton( hDlg, IDOK );
}
else
{
DoMessage(L"The call failed to connect");
}

return 1;
}

// disconnect requestion
if ( LOWORD( wParam ) == IDC_DISCONNECT )
{
// disconnect
if (S_OK == DisconnectTheCall())
{
EnableButton( hDlg, IDOK );
DisableButton( hDlg, IDC_DISCONNECT );
}
else
{
DoMessage(L"The call failed to disconnect");
}

return 1;
}

return 0;
}
default:

return 0;
}
}


////////////////////////////////////////////////////////////////////////
// FindAnAddress
//
// Finds an address object that this application will use to make calls on.
//
// This function finds an address that supports the addresstype passed
// in, as well as the audioin and audioout media types.
//
// Return Value
// S_OK if it finds an address
// S_FALSE if it does not find an address
////////////////////////////////////////////////////////////////////////
HRESULT
FindAnAddress(
DWORD dwAddressType
)
{
HRESULT hr = S_OK;
BOOL bFoundAddress = FALSE;
IEnumAddress * pEnumAddress;
ITAddress * pAddress;
IEnumAddressType * pEnumAddressTypes;
DWORD dwType;

// if we have an address object
// release it
if (NULL != gpAddress)
{
gpAddress->Release();
gpAddress = NULL;
}

// enumerate the addresses
hr = gpTapi->_EnumerateAddresses( &pEnumAddress );

if (S_OK != hr)
{
return hr;
}

while ( !bFoundAddress )
{
// get the next address
hr = pEnumAddress->Next( 1, &pAddress, NULL );

if (S_OK != hr)
{
break;
}

// enumerate the address types that
// this address supports
hr = pAddress->_EnumerateAddressType( &pEnumAddressTypes );

if (S_OK != hr)
{
pAddress->Release();

continue;
}

while (TRUE)
{
ITMediaSupport * pMediaSupport;
VARIANT_BOOL bSupport;

// get the next address type
hr = pEnumAddressTypes->Next( 1, &dwType, NULL );

if (S_OK != hr)
{
break;
}

// is the type we are looking for?
if ( dwAddressType == dwType )
{
// yes
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)
{
// save
pMediaSupport->Release();

gpAddress = pAddress;
gpAddress->AddRef();

bFoundAddress = TRUE;
break;
}
}

pMediaSupport->Release();
}
}

pAddress->Release();
pEnumAddressTypes->Release();
}

pEnumAddress->Release();

if (!bFoundAddress)
{
return S_FALSE;
}

return S_OK;
}


/////////////////////////////////////////////////////////////////
// CreateMediaTerminals
//
// Create audioin and audioout terminals.
//
// If the address also supports video, create
// Videoin and videoout terminals, too.
//
// This function assumes that ppMediaTerminals has a size no less than four.
/////////////////////////////////////////////////////////////////
HRESULT
CreateMediaTerminals(
ITMediaTerminal ** ppMediaTerminals,
PLONG pNumMediaTerminals
)
{
int count = 0;
HRESULT hr;

// get the mediaterminal for audioin
hr = GetMediaTerminal(
gbstrAudioIn,
&ppMediaTerminals[count]
);

if (S_OK != hr)
{
return hr;
}

count ++;

// get the mediaterminal for audioout
hr = GetMediaTerminal(
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;

gpAddress->QueryInterface( IID_ITMediaSupport, (void **)&pMediaSupport );

// does it support VideoIn?
pMediaSupport->QueryMediaType(
gbstrVideoIn,
&bSupport
);

if (bSupport)
{
// get the mediaterminal for VideoIn
hr = GetVideoInMediaTerminal(
&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(
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();
}
}
}

/////////////////////////////////////////////////////////////////
// MakeTheCall
//
// Sets up and makes a call
/////////////////////////////////////////////////////////////////
HRESULT
MakeTheCall(
DWORD dwAddressType,
PWCHAR szAddressToCall
)
{
HRESULT hr = S_OK;
ITMediaTerminal * ppMediaTerminals[MAXTERMINALS];
long nNumMediaTerminals = MAXTERMINALS;
SAFEARRAY * psa;
VARIANT var;
BSTR bstrAddressToCall;

// find an address object that
// we will use to make calls on
hr = FindAnAddress(dwAddressType);

if (S_OK != hr)
{
DoMessage(L"Could not find an address to make a phone call on");

return hr;
}

bstrAddressToCall = SysAllocString( szAddressToCall );

hr = gpAddress->CreateCall( bstrAddressToCall, &gpCall );

SysFreeString ( bstrAddressToCall );

if (S_OK != hr)
{
return hr;
}

hr = CreateMediaTerminals(ppMediaTerminals, &nNumMediaTerminals);

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
VariantInit(&var);
var.vt = VT_ARRAY;
var.parray = psa;

// call SelectMediaTerminals
hr = gpCall->SelectMediaTerminals( var );

ReleaseMediaTerminals(ppMediaTerminals, nNumMediaTerminals);

SafeArrayDestroy( psa );

if (S_OK != hr)
{
return hr;
}

// We're now ready to call connect.
//
// the TRUE parameter indicates that this
// call is sychronous - that is, it won't
// return until the call is in the connected
// state (or fails to connect)
// Since this is called in the UI thread,
// this means that the app will appear
// to hang until this function returns.
// Some TAPI service providers may take a long
// time for a call to reach the connected state.
hr = gpCall->Connect( TRUE );

if (S_OK != hr)
{
gpCall->Release();
gpCall = NULL;
return hr;
}

return hr;
}


/////////////////////////////////////////////////////////
// GetMediaTerminal
//
// Creates a MediaTerminal for the bstrMediaType passed
// in, using the default terminal for the bstrMediaType
//
/////////////////////////////////////////////////////////
HRESULT
GetMediaTerminal(
BSTR bstrMediaType,
ITMediaTerminal ** ppMediaTerminal
)
{
HRESULT hr = S_OK;
ITTerminalSupport * pTerminalSupport;
ITTerminal * pTerminal;

// get the terminal support interface
gpAddress->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(
ITMediaTerminal ** ppMediaTerminal
)
{
HRESULT hr = S_OK;
ITTerminalSupport * pTerminalSupport;
ITTerminal * pTerminal;

// get the terminal support interface
gpAddress->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;

}


//////////////////////////////////////////////////////////////////////
// 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;
}



///////////////////////////////////////////////////////////////////
//
// HELPER FUNCTIONS
//
///////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////
// DoMessage
///////////////////////////////////////////////////////////////////
void
DoMessage(
LPWSTR pszMessage
)
{
MessageBox(
ghDlg,
pszMessage,
gszTapi30,
MB_OK
);
}

///////////////////////////////////////////////////////////////
// EnableButton
//
// Enable, make default, and setfocus to a button
///////////////////////////////////////////////////////////////
void
EnableButton(
HWND hDlg,
int ID
)
{
SendDlgItemMessage(
hDlg,
ID,
BM_SETSTYLE,
BS_DEFPUSHBUTTON,
0
);
EnableWindow(
GetDlgItem( hDlg, ID ),
TRUE
);
SetFocus(
GetDlgItem( hDlg, ID )
);
}

//////////////////////////////////////////////////////////////
// DisableButton
//
// Disable a button
//////////////////////////////////////////////////////////////
void
DisableButton(
HWND hDlg,
int ID
)
{
SendDlgItemMessage(
hDlg,
ID,
BM_SETSTYLE,
BS_PUSHBUTTON,
0
);
EnableWindow(
GetDlgItem( hDlg, ID ),
FALSE
);
}