2025-11-28 00:35:46 +09:00

1098 lines
23 KiB
C++

//////////////////////////////////////////////////////////////////////
//
//
// Copyright (c) 1998-2001 Microsoft Corporation
//
//
// callnot.cpp
//
// Implementation of the ITTAPIEventNotification interface.
//
// This is an outgoing interface that is defined by TAPI 3.0. This
// is basically a callback function that TAPI 3.0 calls to inform
// the application of events related to calls (on a specific address)
//
// Please refer to COM documentation for information on outgoing
// interfaces.
//
// An application must implement and register this interface in order
// to receive calls and events related to calls
//
//////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <tapi3.h>
#include "callnot.h"
#include "FileTerm.h"
#include "resource.h"
extern ITBasicCallControl * g_pCall;
extern HWND g_hDlg;
extern ITTerminal *g_pPlayFileTerm;
extern ITTerminal *g_pRecordFileTerm;
//
//to keep track of number of messages
//
DWORD g_dwMessages = 0;
//////////////////////////////////////////////////////////////////
//
// GetTerminalFromStreamEvent - get the stream from the event and
// after this get the terminal from stream - if any
//
//////////////////////////////////////////////////////////////////
HRESULT GetTerminalFromStreamEvent(
IN ITCallMediaEvent * pCallMediaEvent,
OUT ITTerminal ** ppTerminal )
{
//
//do not return bogus value
//
*ppTerminal = NULL;
//
// Get the stream for this event.
//
ITStream * pStream = NULL;
HRESULT hr = pCallMediaEvent->get_Stream( &pStream );
if (FAILED(hr))
{
DoMessage( _T("GetTerminalFromStreamEvent: get_Stream failed"));
return E_FAIL;
}
//
// Enumerate the terminals on this stream.
//
IEnumTerminal * pEnumTerminal = NULL;
hr = pStream->EnumerateTerminals( &pEnumTerminal );
//
//not need this anymore
//
pStream->Release();
if ( FAILED(hr) )
{
DoMessage(_T("GetTerminalFromStreamEvent: EnumerateTerminals failed"));
return hr;
}
//
// Get the first terminal -- if there aren't any, return E_FAIL so that
// we skip this event (this happens when the terminal is on the other
// stream).
//
hr = pEnumTerminal->Next(1, ppTerminal, NULL);
//
//clean up
//
pEnumTerminal->Release();
//
//map anything but S_OK to failure - even S_FALSE
//
if ( hr != S_OK )
{
return E_FAIL;
}
return S_OK;
}
///////////////////////////////////////////////////////////////////
// CallEventNotification
//
// The only method in the ITCallEventNotification interface. This gets
// called by TAPI 3.0 when there is a call event to report. This just
// posts the message to our UI thread, so that we do as little as
// possible on TAPI's callback thread.
//
///////////////////////////////////////////////////////////////////
HRESULT
STDMETHODCALLTYPE
CTAPIEventNotification::Event(
IN TAPI_EVENT TapiEvent,
IN IDispatch * pEvent
)
{
//
//sanity check
//
if(NULL == pEvent)
{
return S_OK;
}
//
// Addref the event so it doesn't go away.
//
pEvent->AddRef();
//
// Post a message to our own UI thread.
//
PostMessage(
g_hDlg,
WM_PRIVATETAPIEVENT,
(WPARAM) TapiEvent,
(LPARAM) pEvent
);
return S_OK;
}
///////////////////////////////////////////////////////////////////
// OnTapiEvent
//
// This is the real event handler, called on our UI thread when
// the WM_PRIVATETAPIEVENT message is received
//
///////////////////////////////////////////////////////////////////
HRESULT
OnTapiEvent(
IN TAPI_EVENT TapiEvent,
IN IDispatch * pEvent
)
{
HRESULT hr = E_FAIL;
switch ( TapiEvent )
{
case TE_CALLNOTIFICATION:
{
// TE_CALLNOTIFICATION means that the application is being notified
// of a new call.
//
hr = DoCallNotification(pEvent);
break;
}//TE_CALLNOTIFICATION
case TE_CALLSTATE:
{
//
// TE_CALLSTATE is a call state event. pEvent is
// an ITCallStateEvent
//
hr = DoCallState(pEvent);
break;
}//TE_CALLSTATE
case TE_CALLMEDIA:
{
//
// TE_CALLMEDIA is a media event. pEvent is an ITCallMediaEvent
//
hr = DoCallMedia(pEvent);
break;
}//TE_CALLMEDIA
case TE_FILETERMINAL:
{
//
// TE_FILETERMINAL is a file terminal event. pEvent is an ITFileTerminalEvent
//
hr = DoFileEvent(pEvent);
break;
}//TE_FILETERMINAL
default:
break;
}
pEvent->Release(); // we addrefed CTAPIEventNotification::Event()
return S_OK;
}
///////////////////////////////////////////////////////////////////
// CheckStreamMT
//
// Helper method - check if pITStream has the requested media type
//
///////////////////////////////////////////////////////////////////
HRESULT CheckStreamMT(
IN ITStream* pITStream,
IN long mt)
{
long mtStream = TAPIMEDIATYPE_AUDIO;
HRESULT hr = E_FAIL;
if(FAILED(hr=pITStream->get_MediaType(&mtStream)))
{
return hr;
}
if(!(mt&mtStream))
{
return S_FALSE;
}
else
{
return S_OK;
}
}
///////////////////////////////////////////////////////////////////
// CheckStreamDir
//
// Helper method - check if pITStream has the requested direction
//
///////////////////////////////////////////////////////////////////
HRESULT CheckStreamDir(
IN ITStream* pITStream,
IN TERMINAL_DIRECTION td)
{
TERMINAL_DIRECTION tdStream = TD_CAPTURE ;
HRESULT hr =E_FAIL;
if(FAILED(hr=pITStream->get_Direction(&tdStream)))
{
return hr;
}
if(td!=tdStream)
{
return S_FALSE;
}
else
{
return S_OK;
}
}
///////////////////////////////////////////////////////////////////
// CreateAndSelectFileRecordTerminal
//
// Creates FileRecording terminal, add the audio track, put_FileName
// select track on the stream
//
///////////////////////////////////////////////////////////////////
HRESULT CreateAndSelectFileRecordTerminal()
{
//
// we should already have the call
//
if (NULL == g_pCall)
{
DoMessage( _T("CreateAndSelectFileRecordTerminal: we do not have a call"));
return E_UNEXPECTED;
}
//
// get the ITStreamControl interface for this call
//
ITStreamControl* pStreamControl = NULL;
HRESULT hr = g_pCall->QueryInterface( IID_ITStreamControl, (void**)&pStreamControl );
if (FAILED(hr))
{
DoMessage( _T("CreateAndSelectFileRecordTerminal: QI for ITStreamControl failed"));
return E_FAIL;
}
//
// make sure the record terminal was released
//
if(NULL != g_pRecordFileTerm)
{
g_pRecordFileTerm->Release();
g_pRecordFileTerm = NULL;
}
//
// QI for ITBasicCallControl2 - prepare to request the terminal
//
ITBasicCallControl2* pITBCC2 = NULL;
hr = g_pCall->QueryInterface( IID_ITBasicCallControl2, (void**)&pITBCC2 );
if(FAILED(hr))
{
pStreamControl->Release();
DoMessage( _T("CreateAndSelectFileRecordTerminal: QI for ITBasicCallControl2 failed"));
return hr;
}
//
// get the BSTR CLSID used for ReqestTerminal
//
BSTR bstrCLSID = NULL;
hr = StringFromCLSID(CLSID_FileRecordingTerminal, &bstrCLSID);
if(FAILED(hr))
{
pITBCC2->Release();
pStreamControl->Release();
DoMessage( _T("CreateAndSelectFileRecordTerminal: StringFromCLSID failed"));
return hr;
}
//
// request the record terminal - use the right direction and media type
//
hr = pITBCC2->RequestTerminal(bstrCLSID,
TAPIMEDIATYPE_AUDIO,
TD_RENDER,
&g_pRecordFileTerm);
//
//clean up
//
::CoTaskMemFree(bstrCLSID);
pITBCC2->Release();
if(FAILED(hr))
{
pStreamControl->Release();
DoMessage( _T("CreateAndSelectFileRecordTerminal: RequestTerminal failed"));
return hr;
}
//
// get to the ITMediaRecord interface so we can put_FileName
// we can NOT select before put_FileName
//
ITMediaRecord* pITMediaRec = NULL;
hr = g_pRecordFileTerm->QueryInterface( IID_ITMediaRecord, (void**)&pITMediaRec );
if(FAILED(hr))
{
pStreamControl->Release();
DoMessage( _T("CreateAndSelectFileRecordTerminal: QI ITMediaRecord failed"));
return hr;
}
//
// we will have a file for each message
//
WCHAR wchFileName[MAX_PATH+1] = {0};
_snwprintf_s(wchFileName, MAX_PATH,
_T("%s%ld%s"), REC_FILENAME,
g_dwMessages, REC_FILEEXT);
wchFileName[MAX_PATH] = 0;
BSTR bstrFileName = ::SysAllocString(wchFileName);
if(NULL == bstrFileName)
{
pStreamControl->Release();
DoMessage( _T("CreateAndSelectFileRecordTerminal: SysAllocString failed"));
return E_OUTOFMEMORY;
}
//
// put_FileName
//
hr = pITMediaRec->put_FileName(bstrFileName);
//
//clean up
//
::SysFreeString(bstrFileName);
pITMediaRec->Release();
if(FAILED(hr))
{
pStreamControl->Release();
DoMessage( _T("CreateAndSelectFileRecordTerminal: put_FileName failed"));
return hr;
}
//
// get to the ITMultiTrackTerminal interface so we can create tracks
//
ITMultiTrackTerminal* pMTRecTerminal = NULL;
hr = g_pRecordFileTerm->QueryInterface( IID_ITMultiTrackTerminal, (void**)&pMTRecTerminal );
if(FAILED(hr))
{
pStreamControl->Release();
DoMessage( _T("CreateAndSelectFileRecordTerminal: QI IID_ITMultiTrackTerminal failed"));
return hr;
}
//
// enumerate the streams on the call
//
IEnumStream* pEnumStreams = NULL;
hr = pStreamControl->EnumerateStreams(&pEnumStreams);
pStreamControl->Release();
if (FAILED(hr))
{
pMTRecTerminal->Release();
DoMessage( _T("CreateAndSelectFileRecordTerminal: EnumerateStreams failed"));
return hr;
}
//
// only do audio for now
//
long lMediaTypes = TAPIMEDIATYPE_AUDIO;
//
// walk through the list of streams on the call
// create what we need to
//
ITStream* pStream = NULL;
while (S_OK == pEnumStreams->Next(1, &pStream, NULL))
{
if ( (S_OK==CheckStreamMT(pStream,lMediaTypes))
&& (S_OK==CheckStreamDir(pStream,TD_RENDER)))
{
//
// if we have a recording terminal, attempt to create a track terminal
//
ITTerminal* pRecordingTrack = NULL;
//
//check stream for media type - when we check we have an &
//
long lStreamMediaType=0;
hr = pStream->get_MediaType(&lStreamMediaType);
if (FAILED(hr))
{
pStream->Release();
pStream=NULL;
continue;
}
//
// create track terminal
//
hr = pMTRecTerminal->CreateTrackTerminal(lStreamMediaType, TD_RENDER, &pRecordingTrack);
if (FAILED(hr))
{
pStream->Release();
pStream=NULL;
//
//will fail at the end anyway - lMediaTypes!=0 - so break here
//
break;
}
//
//select it on stream
//
hr = pStream->SelectTerminal(pRecordingTrack);
//
//clean up
//
pStream->Release();
pStream=NULL;
pRecordingTrack->Release();
pRecordingTrack=NULL;
if (FAILED(hr))
{
//
//will fail at the end anyway - lMediaTypes!=0 - so break here
//
break;
}
lMediaTypes^=lStreamMediaType;
if(lMediaTypes==0)
{
//
//we selected terminals for all media types we needed, we're done
//
break;
}
}
//
// clean up
//
if(pStream!=NULL)
{
pStream->Release();
pStream = NULL;
}
} // while (enumerating streams on the call)
//
//clean up
//
pMTRecTerminal->Release();
pEnumStreams->Release();
//
//check if we were able to select for all media types
//
if (lMediaTypes==0)
{
return S_OK;
}
else
{
DoMessage( _T("CreateAndSelectFileRecordTerminal: no streams available"));
return E_FAIL;
}
}
///////////////////////////////////////////////////////////////////
// SameCall
//
// check if same call object as g_pCall
//
///////////////////////////////////////////////////////////////////
bool SameCall(ITCallStateEvent* pCallStateEvent)
{
//
// sanity check
//
if(NULL == pCallStateEvent)
{
return false;
}
//
//get call object
//
ITCallInfo* pCallInfo = NULL;
HRESULT hr = pCallStateEvent->get_Call(&pCallInfo);
//
//check if we have a call and get_Call succeded
//
if(NULL != pCallInfo && ( SUCCEEDED(hr) && NULL != g_pCall) )
{
bool bIsEqual = true;
//
//get and compare IUnknown for both objects
//
IUnknown* pIUnkCallInfo = NULL;
IUnknown* pIUnkCallControl = NULL;
pCallInfo->QueryInterface(IID_IUnknown, (void**)&pIUnkCallInfo);
g_pCall->QueryInterface(IID_IUnknown, (void**)&pIUnkCallControl);
//
//compare
//
if(pIUnkCallInfo != pIUnkCallControl)
{
bIsEqual = false;
}
//
//clean up
//
pCallInfo->Release();
if(NULL != pIUnkCallInfo)
{
pIUnkCallInfo->Release();
}
if(NULL != pIUnkCallControl)
{
pIUnkCallControl->Release();
}
return bIsEqual;
}
else
{
return false;
}
}
///////////////////////////////////////////////////////////////////
// DoCallNotification
//
// TE_CALLNOTIFICATION means that the application is being notified
// of a new call.
//
// Note that we don't answer to call at this point. The application
// should wait for a CS_OFFERING CallState message before answering
// the call.
//
///////////////////////////////////////////////////////////////////
HRESULT DoCallNotification(
IN IDispatch * pEvent)
{
ITCallNotificationEvent * pNotify = NULL;
HRESULT hr = pEvent->QueryInterface( IID_ITCallNotificationEvent, (void **)&pNotify );
if (FAILED(hr))
{
DoMessage( _T("Incoming call, but failed to get the interface"));
return hr;
}
CALL_PRIVILEGE cp = CP_MONITOR;
ITCallInfo * pCall = NULL;
//
// get the call
//
hr = pNotify->get_Call( &pCall );
//
// release the event object
//
pNotify->Release();
if(FAILED(hr))
{
DoMessage( _T("Incoming call, but failed to get the call"));
return hr;
}
//
// check to see if we own the call
//
hr = pCall->get_Privilege( &cp );
if(FAILED(hr))
{
pCall->Release();
DoMessage( _T("Incoming call, but failed to get_Privilege"));
return hr;
}
if ( CP_OWNER != cp )
{
//
// just ignore it if we don't own it
//
pCall->Release();
return hr;
}
//
//check if we already have a call - if yes reject it
//
if(NULL != g_pCall)
{
ITBasicCallControl* pITBCC = NULL;
hr = pCall->QueryInterface( IID_ITBasicCallControl, (void**)&pITBCC );
pCall->Release();
//
//sanity check
//
if(SUCCEEDED(hr))
{
//
//disconnect - we'll handle the other events from this call later
//
pITBCC->Disconnect(DC_REJECTED);
pITBCC->Release();
}
return hr;
}
//
//get the call if do not have already one
//
hr = pCall->QueryInterface( IID_ITBasicCallControl, (void**)&g_pCall );
pCall->Release();
if(FAILED(hr))
{
DoMessage( _T("Incoming call, but failed to QI ITBasicCallControl"));
}
else
{
//
// update UI
//
SetStatusMessage(_T("Incoming Owner Call"));
}
//
//clean up
//
return hr;
}
///////////////////////////////////////////////////////////////////
// DoCallState
//
// TE_CALLSTATE is a call state event. pEvent is
// an ITCallStateEvent
//
///////////////////////////////////////////////////////////////////
HRESULT DoCallState(
IN IDispatch * pEvent)
{
CALL_STATE cs = CS_IDLE;
ITCallStateEvent * pCallStateEvent = NULL;
//
// Get the interface
//
HRESULT hr = pEvent->QueryInterface( IID_ITCallStateEvent, (void **)&pCallStateEvent );
if(FAILED(hr))
{
DoMessage( _T("CallStateEvent, but failed to get the interface"));
return hr;
}
//
//check if same call - if it is not do not process the event
//
if(false == SameCall(pCallStateEvent))
{
pCallStateEvent->Release();
return hr;
}
//
// get the CallState that we are being notified of.
//
hr = pCallStateEvent->get_State( &cs );
pCallStateEvent->Release();
if(FAILED(hr))
{
DoMessage( _T("ITCallStateEvent, but failed to get_State"));
return hr;
}
//
// send messages to main dialog procedure
//
if (CS_OFFERING == cs)
{
PostMessage(g_hDlg, WM_COMMAND, IDC_ANSWER, 0);
}
else if (CS_DISCONNECTED == cs)
{
PostMessage(g_hDlg, WM_COMMAND, IDC_DISCONNECTED, 0);
}
else if (CS_CONNECTED == cs)
{
PostMessage(g_hDlg, WM_COMMAND, IDC_CONNECTED, 0);
}
return hr;
}
///////////////////////////////////////////////////////////////////
// DoCallMedia
//
//
// TE_CALLMEDIA is a media event. pEvent is an ITCallMediaEvent
//
//
///////////////////////////////////////////////////////////////////
HRESULT DoCallMedia(
IN IDispatch * pEvent)
{
CALL_MEDIA_EVENT cme = CME_STREAM_INACTIVE;
ITCallMediaEvent * pCallMediaEvent = NULL;
//
// check if we still have a call
//
if(NULL == g_pCall)
{
return E_UNEXPECTED;
}
//
// Get the interface
//
HRESULT hr = pEvent->QueryInterface( IID_ITCallMediaEvent, (void **)&pCallMediaEvent );
if(FAILED(hr))
{
DoMessage( _T("ITCallMediaEvent, but failed to get the interface"));
return hr;
}
//
// get the CALL_MEDIA_EVENT that we are being notified of.
//
hr = pCallMediaEvent->get_Event( &cme );
if(FAILED(hr))
{
pCallMediaEvent->Release();
DoMessage( _T("ITCallMediaEvent, but failed to get_Event"));
return hr;
}
switch ( cme )
{
//
// the only event we process
//
case CME_STREAM_ACTIVE:
{
//
// Get the terminal that's now active.
//
ITTerminal * pTerminal = NULL;
hr = GetTerminalFromStreamEvent(pCallMediaEvent, &pTerminal);
if ( FAILED(hr) )
{
DoMessage(_T("ITCallMediaEvent: GetTerminalFromStreamEvent failed"));
g_pCall->Disconnect(DC_NORMAL);
break;
}
//
// Process this terminal based on the direction.
//
TERMINAL_DIRECTION td;
hr = pTerminal->get_Direction( &td);
//
//clean up
//
pTerminal->Release();
if ( FAILED(hr) )
{
DoMessage(_T("ITCallMediaEvent: get_Direction failed"));
g_pCall->Disconnect(DC_NORMAL);
break;
}
//
// if TD_CAPTURE and we have playback terminal start streaming
//
if ( TD_CAPTURE == td && NULL != g_pPlayFileTerm)
{
ITMediaControl* pITMC = NULL;
hr = g_pPlayFileTerm->QueryInterface(IID_ITMediaControl, (void**)&pITMC);
//
// get ITMediaControl so we can start streaming
//
if(FAILED(hr))
{
DoMessage(_T("ITCallMediaEvent: g_pPlayFileTerm QI for ITMediaControl failed"));
g_pCall->Disconnect(DC_NORMAL);
break;
}
//
// Start streaming
//
hr = pITMC->Start();
pITMC->Release();
if(SUCCEEDED(hr))
{
SetStatusMessage(_T("File Playback Terminal started "));
}
else
{
DoMessage(_T("ITCallMediaEvent: ITMediaControl::Start() failed"));
g_pCall->Disconnect(DC_NORMAL);
}
}
break;
}
default:
break;
}
//
// clean up
//
pCallMediaEvent->Release();
return hr;
}
///////////////////////////////////////////////////////////////////
// DoFileEvent
//
//
// TE_FILETERMINAL is a file terminal event. pEvent is an ITFileTerminalEvent
//
//
///////////////////////////////////////////////////////////////////
HRESULT DoFileEvent(
IN IDispatch * pEvent)
{
//
// check if we still have a call
//
if(NULL == g_pCall)
{
return E_UNEXPECTED;
}
ITFileTerminalEvent* pITFTEvent = NULL;
HRESULT hr = pEvent->QueryInterface( IID_ITFileTerminalEvent,
reinterpret_cast<void **>(&pITFTEvent) );
if (FAILED(hr))
{
//
// fatal error - can not continue - disconnect the call
//
DoMessage( _T("ITFileTerminalEvent, but failed to get the interface"));
g_pCall->Disconnect(DC_NORMAL);
return hr;
}
//
// get the state - we'll make some decision based on this
//
TERMINAL_MEDIA_STATE ftState=TMS_ACTIVE;
hr = pITFTEvent->get_State(&ftState);
if(FAILED(hr))
{
//
// fatal error - can not continue - disconnect the call
//
pITFTEvent->Release();
DoMessage( _T("ITFileTerminalEvent, but failed to get_State"));
g_pCall->Disconnect(DC_NORMAL);
return hr;
}
//
// we are interesred in TMS_IDLE because we will unselect playback and
// select recording
//
if(ftState != TMS_IDLE)
{
pITFTEvent->Release();
return hr;
}
//
// get the terminal
//
ITTerminal *pTerminal = NULL;
hr = pITFTEvent->get_Terminal(&pTerminal);
//
// do not need this anymore
//
pITFTEvent->Release();
if(FAILED(hr))
{
//
// fatal error - can not continue - disconnect the call
//
DoMessage( _T("ITFileTerminalEvent, but failed to get_Terminal"));
g_pCall->Disconnect(DC_NORMAL);
return hr;
}
TERMINAL_DIRECTION td;
hr = pTerminal->get_Direction( &td);
pTerminal->Release();
if(FAILED(hr))
{
//
// fatal error - can not continue - disconnect the call
//
DoMessage( _T("ITFileTerminalEvent, but failed to get_Direction"));
g_pCall->Disconnect(DC_NORMAL);
return hr;
}
if((td == TD_CAPTURE) && (NULL != g_pPlayFileTerm))
{
//
// unselect playback - it is done - we reached the end or an error
//
ITBasicCallControl2 *pITBCC2 = NULL;
hr = g_pCall->QueryInterface(IID_ITBasicCallControl2, (void**)&pITBCC2);
if(FAILED(hr))
{
//
// fatal error - can not continue - disconnect the call
//
DoMessage( _T("ITFileTerminalEvent, but failed to QI for ITBasicCallControl2"));
g_pCall->Disconnect(DC_NORMAL);
return hr;
}
//
// use ITBasicCallControl2 methods - much easier than
// enumerate stream, terminals, etc
//
hr = pITBCC2->UnselectTerminalOnCall(g_pPlayFileTerm);
g_pPlayFileTerm->Release();
g_pPlayFileTerm = NULL;
pITBCC2->Release();
if(FAILED(hr))
{
//
// fatal error - can not continue - disconnect the call
//
DoMessage( _T("ITFileTerminalEvent, but failed to ITBasicCallControl2::UnselectTerminalOnCall"));
g_pCall->Disconnect(DC_NORMAL);
return hr;
}
//
// select record - do not use automatic selection
// we'll see how to use ITMultiTrackTerminal methods
//
hr = CreateAndSelectFileRecordTerminal();
if(FAILED(hr))
{
//
// fatal error - can not continue - disconnect the call
//
g_pCall->Disconnect(DC_NORMAL);
SetStatusMessage(_T("CreateAndSelectFileRecordTerminal failed"));
return hr;
}
//
// get ITMediaControl interface - we need to call Start
//
ITMediaControl* pITMC = NULL;
hr = g_pRecordFileTerm->QueryInterface(IID_ITMediaControl, (void**)&pITMC);
if(FAILED(hr))
{
//
// fatal error - can not continue - disconnect the call
//
DoMessage( _T("ITFileTerminalEvent, but failed to QI ITMediaControl"));
g_pCall->Disconnect(DC_NORMAL);
return hr;
}
hr = pITMC->Start();
pITMC->Release();
if(FAILED(hr))
{
//
// fatal error - can not continue - disconnect the call
//
DoMessage( _T("ITFileTerminalEvent, but ITMediaControl::Start"));
g_pCall->Disconnect(DC_NORMAL);
return hr;
}
SetStatusMessage(_T("File Record Terminal started "));
//
// will stop recording after one minute
//
SetTimer(g_hDlg, TIMER_ID, MAX_REC_TIME, NULL);
g_dwMessages++;
}
return hr;
}