2025-04-27 07:49:33 -04:00

1059 lines
26 KiB
C++

/*++
Copyright (c) 1999 Microsoft Corporation. All Rights Reserved.
--*/
#define UNICODE
#include <windows.h>
#include <tapi3.h>
#include <strmif.h>
#include <control.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 = NULL;
ITBasicCallControl * gpCall;
//////////////////////////////////////////////////////////
// PROTOTYPES
//////////////////////////////////////////////////////////
INT_PTR
CALLBACK
MainDialogProc(
HWND hDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);
HRESULT
FindAnAddress(
DWORD dwAddressType,
BSTR * ppName
);
HRESULT
GetTerminal(
ITStream * pStream,
ITTerminal ** ppTerminal
);
HRESULT
GetVideoRenderTerminal(
ITTerminal ** ppTerminal
);
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
);
BOOL
AddressSupportsMediaType(
ITAddress * pAddress,
long lMediaType
);
//////////////////////////////////////////////////////////
// WinMain
//////////////////////////////////////////////////////////
int
WINAPI
WinMain(
HINSTANCE hInst,
HINSTANCE hPrevInst,
LPSTR lpCmdLine,
int nCmdShow
)
{
ghInst = hInst;
// need to coinit
if ( FAILED( CoInitialize(NULL) ) )
{
return 0;
}
if ( FAILED( 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;
// 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 (S_OK != hr)
{
DoMessage(L"TAPI failed to initialize");
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;
}
// if we have an address object
// release it
if (NULL != gpAddress)
{
gpAddress->Release();
gpAddress = NULL;
}
// release main object.
if (NULL != gpTapi)
{
gpTapi->Shutdown();
gpTapi->Release();
gpTapi = NULL;
}
}
///////////////////////////////////////////////////////////////////////////
// InitAddressTypeComboBox
//
// Put address type string in the combo box
// and save the addresstype with the string
//
///////////////////////////////////////////////////////////////////////////
void
InitAddressTypeComboBox(
HWND hComboBox
)
{
int i;
i = (int)SendMessage( hComboBox, CB_ADDSTRING, 0, (LONG_PTR)gszConferenceName );
SendMessage(
hComboBox,
CB_SETITEMDATA ,
i,
(LONG_PTR)LINEADDRESSTYPE_SDP
);
i = (int)SendMessage( hComboBox, CB_ADDSTRING, 0, (LONG_PTR)gszEmailName );
SendMessage(
hComboBox,
CB_SETITEMDATA ,
i,
(LONG_PTR)LINEADDRESSTYPE_EMAILNAME
);
i = (int)SendMessage( hComboBox, CB_ADDSTRING, 0, (LONG_PTR)gszMachineName );
SendMessage(
hComboBox,
CB_SETITEMDATA ,
i,
(LONG_PTR)LINEADDRESSTYPE_DOMAINNAME
);
i = (int)SendMessage( hComboBox, CB_ADDSTRING, 0, (LONG_PTR)gszPhoneNumber );
SendMessage(
hComboBox,
CB_SETITEMDATA ,
i,
(LONG_PTR)LINEADDRESSTYPE_PHONENUMBER
);
SendMessage( hComboBox, CB_SETCURSEL, i, 0 );
i = (int)SendMessage( hComboBox, CB_ADDSTRING, 0, (LONG_PTR)gszIPAddress );
SendMessage(
hComboBox,
CB_SETITEMDATA ,
i,
(LONG_PTR)LINEADDRESSTYPE_IPADDRESS
);
}
///////////////////////////////////////////////////////////////////////////
// MainDlgProc
///////////////////////////////////////////////////////////////////////////
INT_PTR
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 = (DWORD)SendMessage( hComboBox, CB_GETCURSEL, 0, 0 );
dwAddressType = (DWORD)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 request
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
// E_FAIL if it does not find an address
////////////////////////////////////////////////////////////////////////
HRESULT
FindAnAddress(
DWORD dwAddressType,
BSTR * ppName
)
{
HRESULT hr = S_OK;
BOOL bFoundAddress = FALSE;
IEnumAddress * pEnumAddress;
ITAddress * pAddress;
ITAddressCapabilities * pAddressCaps;
long lType = 0;
// if we have an address object
// release it
if (NULL != gpAddress)
{
gpAddress->Release();
gpAddress = NULL;
}
// enumerate the addresses
hr = gpTapi->EnumerateAddresses( &pEnumAddress );
if ( FAILED(hr) )
{
return hr;
}
while ( !bFoundAddress )
{
// get the next address
hr = pEnumAddress->Next( 1, &pAddress, NULL );
if (S_OK != hr)
{
break;
}
hr = pAddress->QueryInterface(IID_ITAddressCapabilities, (void**)&pAddressCaps);
if ( SUCCEEDED(hr) )
{
hr = pAddressCaps->get_AddressCapability( AC_ADDRESSTYPES, &lType );
pAddressCaps->Release();
if ( SUCCEEDED(hr) )
{
// is the type we are looking for?
if ( dwAddressType & lType )
{
// does it support audio?
if ( AddressSupportsMediaType(pAddress, TAPIMEDIATYPE_AUDIO) )
{
// does it have a name?
if ( SUCCEEDED( pAddress->get_AddressName(ppName) ) )
{
// save it in the global variable
// since we break out of the loop, this one won't
// get released
gpAddress = pAddress;
bFoundAddress = TRUE;
break;
}
}
}
}
}
pAddress->Release();
} // end while loop
pEnumAddress->Release();
if (!bFoundAddress)
{
return E_FAIL;
}
return S_OK;
}
/////////////////////////////////////////////////////////////////
// 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);
}
/////////////////////////////////////////////////////////////////
// EnablePreview
//
// Selects a video render terminal on a video capture stream,
// thereby enabling video preview.
/////////////////////////////////////////////////////////////////
HRESULT
EnablePreview(
ITStream * pStream
)
{
ITTerminal * pTerminal;
HRESULT hr = GetVideoRenderTerminal(&pTerminal);
if ( SUCCEEDED(hr) )
{
hr = pStream->SelectTerminal(pTerminal);
pTerminal->Release();
}
return hr;
}
/////////////////////////////////////////////////////////////////
// SelectTerminalsOnCall
//
// Creates and selects terminals for all streams on the given
// call.
/////////////////////////////////////////////////////////////////
HRESULT
SelectTerminalsOnCall(
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(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( pStream );
}
}
pTerminal->Release();
}
pStream->Release();
}
pEnumStreams->Release();
}
}
return hr;
}
/////////////////////////////////////////////////////////////////
// MakeTheCall
//
// Sets up and makes a call
/////////////////////////////////////////////////////////////////
HRESULT
MakeTheCall(
DWORD dwAddressType,
PWCHAR szAddressToCall
)
{
HRESULT hr = S_OK;
BSTR bstrAddressToCall;
BSTR pAddressName;
// find an address object that
// we will use to make calls on
hr = FindAnAddress(dwAddressType, &pAddressName);
if ( FAILED(hr) )
{
DoMessage(L"Could not find a TAPI address for making calls.");
return hr;
}
WCHAR pText[200];
wsprintf(pText, L"Using address: %s", pAddressName);
SysFreeString(pAddressName);
DoMessage(pText);
//
// find out which media types this address supports
//
long lMediaTypes = 0;
if ( AddressSupportsMediaType(gpAddress, TAPIMEDIATYPE_AUDIO) )
{
lMediaTypes |= TAPIMEDIATYPE_AUDIO; // we will use audio
}
if ( AddressSupportsMediaType(gpAddress, TAPIMEDIATYPE_VIDEO) )
{
lMediaTypes |= TAPIMEDIATYPE_VIDEO; // we will use video
}
//
// Create the call.
//
bstrAddressToCall = SysAllocString( szAddressToCall );
hr = gpAddress->CreateCall( bstrAddressToCall,
dwAddressType,
lMediaTypes,
&gpCall);
SysFreeString ( bstrAddressToCall );
if ( FAILED(hr) )
{
DoMessage(L"Could not create a call.");
return hr;
}
//
// Select our terminals on the call; if any of the selections fail we
// proceed without that terminal.
//
hr = SelectTerminalsOnCall( gpCall );
//
// We're now ready to call connect.
//
// the VARIANT_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( VARIANT_TRUE );
if ( FAILED(hr) )
{
gpCall->Release();
gpCall = NULL;
DoMessage(L"Could not connect the call.");
return hr;
}
return S_OK;
}
/////////////////////////////////////////////////////////
// GetTerminal
//
// Creates the default terminal for the passed-in stream.
//
/////////////////////////////////////////////////////////
HRESULT
GetTerminal(
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(ppTerminal);
}
//
// For all other terminals we use GetDefaultStaticTerminal.
// First, get the terminal support interface.
//
ITTerminalSupport * pTerminalSupport;
hr = gpAddress->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;
}
/////////////////////////////////////////////////////////
// GetVideoRenderTerminal
//
// Creates a dynamic terminal for the Video Render mediatype / direction
//
/////////////////////////////////////////////////////////
HRESULT
GetVideoRenderTerminal(
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 = gpAddress->QueryInterface(IID_ITTerminalSupport,
(void **)&pTerminalSupport);
if ( SUCCEEDED(hr) )
{
//
// Create the video render terminal.
//
hr = pTerminalSupport->CreateTerminal(bstrTerminalClass,
TAPIMEDIATYPE_VIDEO,
TD_RENDER,
ppTerminal);
pTerminalSupport->Release();
if ( SUCCEEDED(hr) )
{
// Get the video window interface for the terminal
IVideoWindow *pVideoWindow = NULL;
hr = (*ppTerminal)->QueryInterface(IID_IVideoWindow,
(void**)&pVideoWindow);
if ( SUCCEEDED(hr) )
{
//
// Set the AutoShow member to true
//
// Note that the AutoShow property is the only one
// we can use on this terminal's IVideoWindow and
// IBasicVideo interfaces before the CME_STREAM_ACTIVE
// event is received for the stream. All other methods
// will fail until CME_STREAM_ACTIVE has been sent.
// Applications that need to control more about a video
// window than just its visibility must listen for the
// CME_STREAM_ACTIVE event. See the "t3in.exe" sample
// for how to do this.
//
hr = pVideoWindow->put_AutoShow( VARIANT_TRUE );
pVideoWindow->Release();
}
}
}
SysFreeString( bstrTerminalClass );
}
}
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;
}
///////////////////////////////////////////////////////////////////
//
// 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
);
}
//////////////////////////////////////////////////////////////
// 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);
}