www.pudn.com > CP_IVR.zip > TapiObj.cpp


/********************************************************************* 
 * TapiObj.cpp: 
 * implementation of the  
 *							CTapiObj,  
 *							CLine,  
 *							CPhone classes 
 * WARNINGS: 
 *			  - Just 1 and only 1 instance of these class must be 
 *				created. 
 * 
 * Auther: Hamed.M. 
 * eMail : HamedMosavi @ hotmail.com 
 *		   HamedMosavi @ gmail.com 
 ********************************************************************/ 
#include "stdafx.h" 
 
#include "TapiObj.h" 
#include "HSettings.h" 
#include "HDevices.h" 
#include "HErrLogger.h" 
 
#ifdef _DEBUG 
#undef THIS_FILE 
static char THIS_FILE[]=__FILE__; 
#define new DEBUG_NEW 
#endif 
 
/********************************************************************* 
 * Globals 
 ********************************************************************/ 
					   /* To post event messages to central manager */ 
extern unsigned long g_lMngrThread; 
				 /* To load some initialdata from a prefrences list */ 
extern CHSettings g_settings; 
							/* To store a list of telephony devices */ 
extern CHDevices g_devices; 
 
/********************************************************************* 
 * Construction: Parent class 
 ********************************************************************/ 
CTapiObj::CTapiObj() 
{ 
			 /* Our application name. CP stands for Code Project ;) */ 
	m_appName = _T("CP_IVR"); 
					   /* This application is going to use TAPI 2.1 */ 
			 /* For more information about versionning look at MSDN */ 
	m_version = 0x00020001; 
		 /* We are going to place a communication of the type voice */ 
	m_mediaMode = LINEMEDIAMODE_AUTOMATEDVOICE; 
} 
 
/********************************************************************* 
 * Destruction: Parent class 
 ********************************************************************/ 
CTapiObj::~CTapiObj() 
{ 
 
} 
 
/********************************************************************* 
 * Construction: Exception class, to be thrown! 
 ********************************************************************/ 
CTapiObj::TEx::TEx(int id, CString res, CString dt, CString tm) 
{ 
	code=id; 
	result=res; 
	date=dt; 
	time=tm; 
} 
 
/********************************************************************* 
 * CLine class 
 ********************************************************************* 
 * This class impelements  a line and defines main operations on it  * 
 * A line will be automatically answered (picked up), and app waits  * 
 * and detects codes that user presses at the other end of the line  * 
 * CallerID of the user will be receiver.							 * 
 * It informs the outside world of what's happenning at any time by  * 
 * posting a message to any hread, whoes handle is placed in		 * 
 * 'g_lMngrThread' global variable.									 * 
 ********************************************************************/ 
 
 
/********************************************************************* 
 *Constructor: CLine class (Up to line # 810) 
 ********************************************************************/ 
CLine::CLine() 
{ 
							/* To receive device capabilities info. */ 
	m_pLineDevCaps = NULL; 
    
	/*  To keep the ID of the current device, it can be used later to 
     *  to open other devices, in a multiline application. Line class 
	 *  Starts searching ID's greater than this ID,  So after Finding 
	 * the first line ID, you can give this 'that value +1' to search 
	 * for the next device.											*/ 
 
	m_lBossThreadID = 0; 
	m_ID = 0; 
	m_cid = _T(""); 
						   /* The line has not been initialized yet */ 
	m_bInitialized = FALSE; 
 
								/* The line has not been opened yet */ 
	m_bOpened = FALSE; 
						   /* Thread should not stop after starting */ 
	m_bContinueEventThread = TRUE; 
												  /* TAPI variables */ 
	m_hLineApp = NULL; 
	m_hLine = NULL; 
	m_hCall = NULL; 
	m_hLineEvent = NULL; 
	m_hLineMsgThread = NULL; 
	m_hWOut = NULL; 
	memset(&m_extID,NULL,sizeof(LINEEXTENSIONID)); 
	m_dwApiVersion = 0; 
	m_dwWaveOutID = 0; 
	m_dwRingCnt = 0; 
		  /* Will be filled by the number of available TAPI devices */ 
	m_nLineCnt = 0; 
} 
 
/********************************************************************* 
 * Destructor: CLine class 
 ********************************************************************/ 
CLine::~CLine() 
{ 
	ShutDown(); 
} 
 
/********************************************************************* 
 * Initialize the line, (~ 200 lines) 
 ********************************************************************/ 
long CLine::Init() 
{ 
	PostThreadMessage(g_lMngrThread,WM_STATUS_CHNGD,0,ST_INIT); 
	long ret = -1; 
 
   /* To be able to control loops. It will keep track of the numbers 
	a loop continued, so after a maximum, we are in trouble=> break */ 
 
	int		patience = 0; 
 
						  /* Working with memory in TAPI is a hell! */ 
	BOOL	noMem; 
 
							 /* Allocate memory for line parameters */ 
	LINEINITIALIZEEXPARAMS *lineParams =  
		(LINEINITIALIZEEXPARAMS *) calloc(1, 
									sizeof(LINEINITIALIZEEXPARAMS)); 
 
	memset(lineParams,NULL,sizeof(LINEINITIALIZEEXPARAMS)); 
		 
	   /* We are going to receive TAPI events using an event thread */ 
	lineParams->dwOptions = LINEINITIALIZEEXOPTION_USEEVENT; 
	lineParams->dwTotalSize = sizeof(LINEINITIALIZEEXPARAMS); 
 
										   /* Try to initilize line */ 
				 /* We need a loop untill needed memory is alocated */ 
	do { 
								 /* We have not memory shortage yet */ 
		noMem = FALSE; 
 
								 /* First TAPI call in the sequence */ 
		ret = lineInitializeEx( 
			&m_hLineApp, 
			AfxGetInstanceHandle(), 
			NULL, 
			m_appName, 
			&m_nLineCnt, 
			&m_version, 
			lineParams ); 
 
				/* Didn't Itell you about memory hell? Here it starts 
								If more memory is needed, Realocate */ 
							 /* Re alocate memory if more is needed */ 
		if ( lineParams->dwNeededSize > lineParams->dwTotalSize ) { 
			int needed = lineParams->dwNeededSize; 
			noMem = TRUE; 
			free(lineParams); 
			lineParams = NULL; 
 
			lineParams = (LINEINITIALIZEEXPARAMS *) calloc(1,needed); 
			if (!lineParams) { 
				ThrowErr(0, 
				  _T("TapiObj.cpp, Ln 186, can't alocate memory")); 
			} 
 
			memset(lineParams,NULL,needed); 
			lineParams->dwOptions = LINEINITIALIZEEXOPTION_USEEVENT; 
			lineParams->dwTotalSize = needed; 
		} 
 
		  /* If this loop ran 1000 times!!, there must be a problem */ 
		if (++patience> 1000) { 
 
			if (lineParams) free(lineParams); 
			ThrowErr(1,_T( 
			 "TapiObj.cpp, Ln 199, Recovered from unlimited loop")); 
		}; 
 
				  /* Sleep 5 mili seconds and re run the loop again */ 
		Sleep(5); 
							  /* Repeat untill everythings go right */ 
	} while ( (ret==LINEERR_REINIT) || (noMem==TRUE) ); 
 
							   /* Get a handle for thread waking up */ 
	m_hLineEvent = (lineParams->Handles.hEvent); 
 
											  /* Free unused memory */ 
	if (lineParams) free(lineParams); 
	 
													/* Catch errors */ 
	if (ret != 0) { 
		CString str; 
		str.Format(_T("%d"),ret); 
		ThrowErr(ret,_T("TapiObj.cpp, Ln 217, ret=")+str); 
	} 
 
									  /* Create an array of devices */ 
	g_devices.Reset(); 
 
								/* Place to hold the true device id */ 
	int trueDeviceId = 0; 
 
							/* Time to negotiate! for a good device */ 
	for (int i=m_ID;(unsigned)idwTotalSize =  
						sizeof(LINECALLINFO)+1024; 
 
					lineGetCallInfo(m_hCall, lpCallInfo); 
					 
					if (lpCallInfo->dwTotalSize <  
						lpCallInfo->dwNeededSize) 
					{ 
						lpCallInfo = (LINECALLINFO *) 
							realloc( 
							lpCallInfo,  
							lpCallInfo->dwNeededSize); 
 
						lineGetCallInfo(m_hCall, lpCallInfo); 
					} 
 
												  /* Find Caller ID */ 
					m_cid.Format(_T("%s"),(LPCTSTR) 
						((LPBYTE)lpCallInfo +  
						lpCallInfo->dwCallerIDOffset)); 
 
								   /* Inform parent that we got CID */ 
					PostThreadMessage( 
						g_lMngrThread, 
						WM_CALLERID, 
						0, 
						0); 
					if (lpCallInfo) 
						free(lpCallInfo); 
				} 
 
			break; 
 
												  /* Close the line */ 
		case LINE_CLOSE: 
			ShutDown();			// UNDONE: should we shutdown? 
			break; 
 
		case LINE_LINEDEVSTATE :  
								 /* Ringing, Please answer the call */ 
			if (stLineMsg.dwParam1==LINEDEVSTATE_RINGING) { 
				 
										 /* Hey, dady! It's ringing */ 
				PostThreadMessage( 
					g_lMngrThread, 
					WM_STATUS_CHNGD, 
					0, 
					ST_RING); 
									 /* Should I pick the phone Up? */ 
				if ( stLineMsg.dwParam3 >= m_dwRingCnt ) { 
													/* Check errors */ 
					ret = lineAnswer(m_hCall,NULL,0); 
					if (!(ret>0)) { 
						ThrowErr(ret,_T("TapiObj.cpp, Ln 597,") 
							_T("Err: Unable to Answer the line.")); 
						return; 
					} 
				} 
			} 
			break; 
 
									   /* Touch tone digit arrived! */ 
		case LINE_MONITORDIGITS :  
												  /* Process digits */ 
			if ( stLineMsg.dwParam2 == LINEDIGITMODE_DTMF ) { 
				OnDetectDTMF( LOBYTE(stLineMsg.dwParam1) ); 
			} 
			break; 
 
				/* Tone arrived, any computer is trying to connect? */ 
		case LINE_MONITORTONE :  
			// ? 
			break; 
 
										/* Any detectable error(s)? */ 
		case LINE_REPLY :  
			// Possibly an error occured 
			// UNDONE: Handle errors, and stop later clls or so... 
			break; 
		default : 
			st.Format(L" [%x] ",stLineMsg.dwMessageID); 
			ThrowErr(0,st); 
			break; 
	} 
} 
 
/********************************************************************* 
 * Tries to get the ID of the wave out device 
 ********************************************************************/ 
long CLine::GetWaveID() 
{ 
	VARSTRING	*pVs; 
	long		ret=0; 
	DWORD		dwWaveDev; 
	DWORD		dwSize; 
	int			patience = 0; 
 
								   /* allocate memory for structure */ 
	pVs = (VARSTRING *) calloc (1, sizeof(VARSTRING)); 
											  /* set structure size */ 
	pVs->dwTotalSize = sizeof(VARSTRING); 
 
	do { 
						/* We have not more patience, stop the loop */ 
		if (++patience > 1000) { 
			ThrowErr(2153,_T("TapiObj.cpp, Ln 649,") 
			_T("Err: Recovered from unlimited loop.")); 
			return -1; 
		} 
												 /* Get wave/out ID */ 
		ret = lineGetID( 
			m_hLine,  
			0L,  
			m_hCall,  
			LINECALLSELECT_CALL,  
			pVs,  
			_T("wave/out")); 
 
													/* check errors */ 
		if (ret)  { 
		  free (pVs); 
		  return -1; 
		} 
 
										/* reallocate and try again */ 
		if (pVs->dwTotalSize < pVs->dwNeededSize) { 
			dwSize = pVs->dwNeededSize; 
			free (pVs); 
			pVs = (VARSTRING *) calloc(1, dwSize); 
			pVs->dwTotalSize = dwSize; 
			continue; 
		}/* end if (need more space) */ 
		break; /* success  */ 
	} while (TRUE); 
 
													/* copy wave id */ 
	dwWaveDev = (DWORD) *((DWORD *)((LPSTR)pVs + pVs->dwStringOffset)); 
	free (pVs); 
 
	m_dwWaveOutID = dwWaveDev; 
	return dwWaveDev; 
} 
 
/********************************************************************* 
 * Restart the line 
 ********************************************************************/ 
void CLine::Restart() 
{ 
												 /* Stop the thread */ 
	m_bContinueEventThread = FALSE; 
	Sleep(500);							/* Sleep while thread exits */ 
 
								/* Notify parent of a restart event */ 
	PostThreadMessage(g_lMngrThread,WM_STATUS_CHNGD,0,ST_RESTART); 
 
												   /* Drop the line */ 
	lineDrop(m_hCall, NULL, 0); 
													/* TAPI cleanup */ 
	lineDeallocateCall (m_hCall); 
	m_hCall = NULL; 
 
	m_bInitialized = TRUE; 
	m_bOpened = FALSE; 
	 /* Sleep 100 more mili seconds, to make sure  hardware is ready*/ 
	Sleep(200); 
												/* Re open the line */ 
	Open(); 
} 
 
/********************************************************************* 
 * Shutting down the line 
 ********************************************************************/ 
void CLine::ShutDown() 
{ 
							   /* Notify parent of a shutdown event */ 
	PostThreadMessage(g_lMngrThread,WM_STATUS_CHNGD,0,ST_SHUTDN); 
 
	if (m_bInitialized) { 
 
												 /* Stop the thread */ 
		m_bContinueEventThread = FALSE; 
										/* Sleep while thread exits */ 
		if ( WaitForSingleObject(m_hLineMsgThread,10000) ==  
			WAIT_TIMEOUT) { 
			TerminateThread(m_hLineMsgThread, 0); 
		} 
												   /* Drop the line */ 
		lineDrop(m_hCall, NULL, 0); 
												  /* Close the line */ 
		lineClose(m_hLine); 
		m_hLine = NULL; 
													/* TAPI cleanup */ 
		lineDeallocateCall (m_hCall); 
		m_hCall = NULL; 
												   /* Shutdown TAPI */ 
		lineShutdown(m_hLineApp); 
		m_hLineApp = NULL; 
 
		m_bInitialized = FALSE; 
	} 
	if (m_pLineDevCaps) { 
		free (m_pLineDevCaps); 
	} 
	 
	m_pLineDevCaps = NULL; 
} 
 
/********************************************************************* 
 * Thread to get tapi events 
 ********************************************************************/ 
DWORD WINAPI CLine::EventThread(LPVOID pParam) 
{ 
											 /* Get callers pointer */ 
	CLine* pLine = (CLine*) pParam; 
	if (!pLine) return 0; 
 
	/****************************************************************  
	 * Every 10 mili seconds check to make sure we have to continue, 
	 * if so, check to see weather we have any events or not, if so 
	 * process event otherwise goto first place!					 
	 ****************************************************************/ 
 
	while(pLine->m_bContinueEventThread) { 
		switch(WaitForSingleObject( 
			(HANDLE)(pLine->m_hLineEvent),  
			5)) 
		{ 
										 /* Have we got any events? */ 
		case WAIT_OBJECT_0: 
									 /* I did not catch errors here */ 
			try { 
				pLine->ProcessEvent(); 
 
			} catch(CTapiObj::TEx e) { 
				CString sz; 
				sz.Format(_T("Line thread Err: [code:%d],") 
					_T("[date:%s],") 
					_T("[time:%s],") 
					_T("[reason:%s]\n"),e.code,e.date,e.time,e.result); 
				CHErrLogger::LogError(sz); 
			} catch(...) { 
 
			} 
			break; 
		 
							 /* Sure we have nothing to process now */ 
		case WAIT_TIMEOUT: 
			break; 
 
									/* Go on waiting and processing */ 
		default: continue; 
		} 
	} 
 
	return 0; 
} 
 
/********************************************************************* 
 * Will be called to process a key pressed by the user 
 ********************************************************************/ 
void CLine::OnDetectDTMF(char tone) 
{ 
	switch (tone) { 
		case '1': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_1); 
			break; 
		case '2': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_2); 
			break; 
		case '3': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_3); 
			break; 
		case '4': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_4); 
			break; 
		case '5': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_5); 
			break; 
		case '6': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_6); 
			break; 
		case '7': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_7); 
			break; 
		case '8': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_8); 
			break; 
		case '9': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_9); 
			break; 
		case '0': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_0); 
			break; 
		case 'A': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_A); 
			break; 
		case 'B': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_B); 
			break; 
		case 'C': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_C); 
			break; 
		case 'D': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_D); 
			break; 
		case '*': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_STAR); 
			break; 
		case '#': PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_SHARP); 
			break; 
		default : PostThreadMessage(m_lBossThreadID,WM_DTMF,0,D_NONE); 
			break; 
	} 
} 
 
/********************************************************************* 
 * Starts processing a line 
 ********************************************************************/ 
BOOL CLine::Start() 
{ 
	try { 
		Init(); 
		Open(); 
	} catch(CTapiObj::TEx e) { 
		CString sz; 
		sz.Format(_T("Line Err: [code:%d],") 
			_T("[date:%s],") 
			_T("[time:%s],") 
			_T("[reason:%s]\n"),e.code,e.date,e.time,e.result); 
		CHErrLogger::LogError(sz); 
	} catch (...) { 
	} 
 
	return TRUE; 
} 
/********************************************************************* 
 * Returns caller ID 
 ********************************************************************/ 
CString CLine::GetCallerID() 
{ 
	return m_cid; 
} 
 
 
/********************************************************************* 
 * Returns wave device ID 
 ********************************************************************/ 
DWORD CLine::GetDevWavID() 
{ 
	return m_dwWaveOutID; 
} 
 
/********************************************************************* 
 * Set a thread to receive DTMF messages 
 ********************************************************************/ 
void CLine::SetBossMessenger(unsigned long pThreadID) 
{ 
	m_lBossThreadID = pThreadID; 
} 
 
/********************************************************************* 
 * Set number of rings to pickup the line after 
 ********************************************************************/ 
void CLine::SetRingCount(UINT nRingCnt) 
{ 
	m_dwRingCnt = nRingCnt; 
											/* Pickup after ? rings */ 
	lineSetNumRings (m_hLine, 0, m_dwRingCnt); 
} 
 
/********************************************************************* 
 * End of CLine impelementation 
 ********************************************************************/ 
 
/********************************************************************* 
 * CPhone class 
 ********************************************************************* 
 * This class impelements  a phone and defines main operations on it * 
 * phone devices are to control mic/spk jacks behind your modem or   * 
 * through the sound card. They also make it possible to work with   * 
 * the phone socket (socket simmilar to RJ-45) of the modem.		 * 
 *																	 * 
 * This impelementation covers Speaker/phone capability of modems.	 * 
 *																	 * 
 ********************************************************************/ 
 
/********************************************************************* 
 * Constructor CPhone class 
 ********************************************************************/ 
CPhone::CPhone() 
{ 
	m_ID = 0; 
	m_bInitialized = FALSE; 
	m_bOpened = FALSE; 
	m_bContinueEventThread = TRUE; 
 
	m_hPhoneApp = NULL; 
	m_hPhone = NULL; 
	m_hPhoneEvent = NULL; 
	m_hPhoneMsgThread = NULL; 
	m_pPhoneDevCaps = NULL; 
	m_dwApiVersion = 0; 
} 
 
/********************************************************************* 
 * Distructor CPhone class 
 ********************************************************************/ 
CPhone::~CPhone() 
{ 
	ShutDown(); 
} 
 
/********************************************************************* 
 * Initialize the phone (~ 120 lines) 
 *  
 * very similar to line initialization above 
 ********************************************************************/ 
long CPhone::Init() 
{ 
	int ret = -1; 
 
	// Varibles 
	int		patience = 0; 
	DWORD	nPhoneCnt=0; 
	BOOL	noMem; 
 
	PHONEINITIALIZEEXPARAMS *phoneParams =  
		(PHONEINITIALIZEEXPARAMS *)  
		calloc(1,sizeof(PHONEINITIALIZEEXPARAMS)); 
 
	memset(phoneParams,NULL,sizeof(PHONEINITIALIZEEXPARAMS)); 
	phoneParams->dwOptions = PHONEINITIALIZEEXOPTION_USEEVENT; 
	phoneParams->dwTotalSize = sizeof(PHONEINITIALIZEEXPARAMS); 
 
										  /* Try to initilize phone */ 
	do { 
									 /* We have not memory shortage */ 
		noMem = FALSE; 
 
		ret = phoneInitializeEx( &m_hPhoneApp, 
								 AfxGetInstanceHandle(), 
								 NULL, 
								 m_appName, 
								 &nPhoneCnt, 
								 &m_version, 
								 phoneParams ); 
	 
							 /* If more memory is needed, Realocate */ 
		if ( phoneParams->dwNeededSize > phoneParams->dwTotalSize ) { 
			int needed = phoneParams->dwNeededSize; 
			noMem = TRUE; 
			free(phoneParams); 
			phoneParams = NULL; 
 
			phoneParams = (PHONEINITIALIZEEXPARAMS *)  
				calloc(1,needed); 
 
			if (!phoneParams) { 
				ThrowErr(0,_T("TapiObj.cpp, Ln 987,") 
					_T("can't alocate memory")); 
			} 
 
			memset(phoneParams,NULL,needed); 
			phoneParams->dwOptions =  
				PHONEINITIALIZEEXOPTION_USEEVENT; 
			phoneParams->dwTotalSize = needed; 
		} 
 
 
												/* looping too much */ 
		if (++patience> 1000) { 
 
			if (phoneParams) free(phoneParams); 
			ThrowErr(1,_T("TapiObj.cpp, Ln 1002,") 
				_T("Recovered from unlimited loop")); 
		}; 
 
											/* Sleep 5 mili seconds */ 
		Sleep(5); 
	} while ( (ret==PHONEERR_REINIT) || (noMem==TRUE) ); 
 
 
							   /* Get a handle for thread waking up */ 
	m_hPhoneEvent = (phoneParams->Handles.hEvent); 
 
											  /* Free unused memory */ 
	if (phoneParams) free(phoneParams); 
 
													/* Check errors */ 
	if (ret != 0) { 
		CString sr; 
 
		sr.Format(_T("%d"),ret); 
		ThrowErr(ret,_T("TapiObj.cpp, Ln 1022, ret=")+sr); 
	} 
 
	memset(&(m_extID),NULL,sizeof(PHONEEXTENSIONID)); 
 
							/* Time to negotiate! for a good device */ 
	for (UINT i=m_ID;im_bContinueEventThread) { 
		switch(WaitForSingleObject( 
			(HANDLE)(phone->m_hPhoneEvent), 10)) { 
		 
										 /* Have we got any events? */ 
		case WAIT_OBJECT_0: 
			try { 
				phone->ProcessEvent(); 
			} catch(...) { 
				CString sz; 
				sz.Format(_T("Err: PhoneEventThread ,Line 1233\n")); 
				CHErrLogger::LogError(sz); 
			} 
			break; 
		 
							 /* Sure we have nothing to process now */ 
		case WAIT_TIMEOUT: 
			break; 
 
									/* Go on waiting and processing */ 
		default: continue; 
		} 
	} 
 
	return 0; 
} 
 
/********************************************************************* 
 * Switches a line to Mic/Spk phone device 
 ********************************************************************/ 
LONG CPhone::SwichToMicSpk() 
{ 
	long ret = 0; 
	ret = phoneSetHookSwitch(  
		m_hPhone,  
		PHONEHOOKSWITCHDEV_SPEAKER, 
		PHONEHOOKSWITCHMODE_MICSPEAKER 
		); 
 
	if (ret<0) { 
	} 
	return ret; 
} 
 
/********************************************************************* 
 * Resets a phone device 
 ********************************************************************/ 
void CPhone::Reset() 
{ 
	phoneClose( m_hPhone ); 
 
	Start(); 
} 
 
/********************************************************************* 
 * Starts a phone device operation 
 ********************************************************************/ 
BOOL CPhone::Start() 
{ 
	try { 
		Init(); 
		Open(); 
	} catch(CTapiObj::TEx e) { 
		CString sz; 
		sz.Format(_T("Phone Err: [code:%d],") 
			_T("[date:%s],") 
			_T("[time:%s],") 
			_T("[reason:%s]\n"),e.code,e.date,e.time,e.result); 
		CHErrLogger::LogError(sz); 
	} catch (...) { 
	} 
 
	return TRUE; 
} 
 
/********************************************************************* 
 * Misplaced function! 
 ********************************************************************/ 
void CTapiObj::ThrowErr(int id, CString res) 
{ 
	CTime	tm; 
	tm=tm.GetCurrentTime(); 
	CString stdt,sttm; 
 
	stdt.Format(_T("%d/%d/%d"),tm.GetYear(),tm.GetMonth(),tm.GetDay()); 
	sttm.Format(_T("%d:%d:%d"),tm.GetHour(),tm.GetMinute(),tm.GetSecond()); 
 
	throw(CTapiObj::TEx(id,res,stdt,sttm)); 
} 
 
/********************************************************************* 
 * Shutdown & Clenup phone device 
 ********************************************************************/ 
void CPhone::ShutDown() 
{ 
	if (m_bInitialized) { 
 
												 /* Stop the thread */ 
		m_bContinueEventThread = FALSE; 
										/* Sleep while thread exits */ 
		if ( WaitForSingleObject(m_hPhoneMsgThread,10000) ==  
			WAIT_TIMEOUT) { 
			TerminateThread(m_hPhoneMsgThread, 0); 
		} 
 
												   /* Drop the line */ 
		phoneClose(m_hPhone); 
												  /* Close the line */ 
		m_hPhone = NULL; 
												   /* Shutdown TAPI */ 
		phoneShutdown(m_hPhoneApp); 
		m_hPhoneApp = NULL; 
 
		m_bInitialized = FALSE; 
	} 
	if (m_pPhoneDevCaps) { 
		free (m_pPhoneDevCaps); 
	} 
	 
	m_pPhoneDevCaps = NULL; 
}