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