www.pudn.com > tapi3.zip > TAPILINE.CPP


// tapiline.cpp : implementation file  for CTapiLine class 
// (c) Dialogic corp 1995, 1996 
 
 
#include "stdafx.h" 
#include  
#include "tapiapp.h" 
#include "tapiline.h" 
#include "tapicall.h" 
#include "wavstate.h"		// temporarily to see if there is a conflict 
 
#ifdef _DEBUG 
#undef THIS_FILE 
static char BASED_CODE THIS_FILE[] = __FILE__; 
#endif 
 
///////////////////////////////////////////////////////////////////////////// 
// CTapiLine 
 
IMPLEMENT_DYNCREATE(CTapiLine, CObject) 
//{{AFX_MSG_MAP(CTapiApp) 
	// NOTE - the ClassWizard will add and remove mapping macros here. 
	//    DO NOT EDIT what you see in these blocks of generated code! 
//}}AFX_MSG 
 
// Default constructor - just constructs the CTapiLine object 
CTapiLine::CTapiLine() 
{ 
// Do whatever initalization is needed	 
	m_hLine = NULL;								 
	m_uiWaveInID = 0xffff; 
	m_uiWaveOutID = 0xffff; 
	m_bRemote = FALSE; 
} 
 
CTapiLine::CTapiLine(DWORD dwLineID) 
{ 
// Do whatever initalization is needed	 
	m_hLine = NULL; 
	m_pApp = (CTapiApp *) AfxGetApp(); 
	m_dwLineID = dwLineID; 
	m_lpCallParms = NULL; 
	m_pActiveCall = NULL; 
	m_hCallSem = NULL; 
	m_uiWaveInID = 0xffff; 
	m_uiWaveOutID = 0xffff; 
	m_bRemote = FALSE; 
	//DWORD dwrc = ctlLineOpen(dwLineID); 
} 
 
CTapiLine::~CTapiLine() 
{ 
	TRACE("*** TALKER32 ***: Calling destructor for line %d\n", m_dwLineID); 
	ctlLineClose(); 
	CloseHandle(m_hCallSem); 
	m_hCallSem = NULL; 
	TRACE("*** TALKER32 ***: exiting destructor\n"); 
} 
 
DWORD CTapiLine::ctlLineOpen(DWORD dwLineID) 
{ 
	DWORD dwrc; 
	LINEEXTENSIONID eidExtID; 
	LPLINEDEVCAPS lpDevCaps; 
 
	m_bRemote = FALSE; 
	m_dwExtVersion = NULL; 
	m_dwApiVersion = NULL; 
	if(m_hLine != NULL) return 0L; 
    dwrc = (DWORD) lineNegotiateAPIVersion(m_pApp->m_hApp, dwLineID,  
    		0x1000, CURRENT_TAPI_VERSION, &m_dwApiVersion, &eidExtID); 
 //   		TAPI_VERSION1_3, TAPI_VERSION1_4, &m_dwApiVersion, &eidExtID); 
	if(dwrc) return dwrc;		// what to do with Ext ID??? 
 
	if(dwrc = lineNegotiateExtVersion(m_pApp->m_hApp, dwLineID,  
    m_dwApiVersion, 0, 0xfffffff, &m_dwExtVersion)) m_dwExtVersion = NULL; 
	m_dwExtVersion = NULL; 
	if(dwrc = (DWORD) lineOpen(m_pApp->m_hApp, dwLineID, &m_hLine,  
    	m_dwApiVersion, m_dwExtVersion, NULL, //DWORD dwCallbackInstance,  
    	//LINECALLPRIVILEGE_MONITOR | LINECALLPRIVILEGE_OWNER,  
    	LINECALLPRIVILEGE_OWNER,  
    	LINEMEDIAMODE_INTERACTIVEVOICE, NULL)) 
	{ 
		m_hLine = NULL; 
		return dwrc; 
	} 
	// Get the WAVE IDs 
	m_csName.Empty(); 
	ctlGetWaveDeviceID(WAVEIN);	 
	ctlGetWaveDeviceID(WAVEOUT);	 
	// Get CAPS to determine name & other things 
	if(!(dwrc = ctlLineGetCaps(&lpDevCaps))) 
	{ 
		m_csName += (LPCTSTR)((LPSTR)(lpDevCaps)+lpDevCaps->dwLineNameOffset); 
//		bRemote = ::IsLineRemote(lpDevCaps);	// find out if line is remote 
		delete lpDevCaps; 
	} 
 
	m_hCallSem = CreateMutex(NULL, FALSE, "TALKER32_LINE_CALLS"); 
	if(m_hCallSem == NULL)	return 0xffffffff; 
	return dwrc; 
} 
 
DWORD CTapiLine::ctlLineClose() 
{ 
	CString csTemp; 
 
	if(m_hLine == NULL) return NULL; 
	DWORD dwrc = (DWORD) lineClose(m_hLine); 
	if(dwrc)  
	{ 
		csTemp.Format("lineClose for line %d failed reason=%lx", m_dwLineID, dwrc); 
		AfxMessageBox(csTemp); 
		return dwrc; 
	} 
	ctlPurgeCalls(); 
	m_hLine = 0L; 
	m_dwLineID = 0L; 
	m_bRemote = FALSE; 
	return dwrc; 
} 
 
DWORD CTapiLine::ctlLineGetCaps(LPLINEDEVCAPS *lppDevCaps) 
{ 
	DWORD dwrc, i; 
	LPLINEDEVCAPS lpDevCaps; 
	DWORD dwInitSize = sizeof(LINEDEVCAPS) + 2048; 
 
	for(i = 0; i< 2; i++) 
	{ 
    	if (NULL == (lpDevCaps = (LPLINEDEVCAPS) new char[dwInitSize])) 
		{ 
        	MessageBox(NULL, "Memory alloc for devcaps failed", NULL, MB_ICONSTOP); 
			return 0xffffffff; 
		} 
    	lpDevCaps->dwTotalSize = dwInitSize; 
 
    	// get line capability info  
    	if(!(dwrc = (DWORD) lineGetDevCaps(m_pApp->m_hApp, m_dwLineID, 
	    		 m_dwApiVersion,0,lpDevCaps))) 
	    {		 				  
 			if(lpDevCaps->dwTotalSize >= lpDevCaps->dwNeededSize) break; // Done	 
	    	dwInitSize = lpDevCaps->dwNeededSize;		// More needed 
		} 
    	delete lpDevCaps;  
		if(dwrc) break; 
	}             
	if(!dwrc) *lppDevCaps = lpDevCaps;		// The app MUST delete the structure 
	return dwrc; 
} 
 
// lineMakeCall wrapper 
LONG CTapiLine::ctlLineMakeCall(LPCSTR lpDestAddress, DWORD dwCountryCode) 
{ 
	LONG lrc; 
 
	// need to protect against early return from MakeCall(possible case;pAsync is not yet in the que,REPLY from  
	// MakeCall already arrived. Set some semaphore to make REPLY wait?? 
	// Allocate new call object, set it up for MAKING before calling TAPI 
	lrc = AddCall(); 
	if(lrc || m_pActiveCall == NULL) 
	{ 
		TRACE("*** TALKER32 ***: AddCall failed\n"); 
		return lrc; 
	} 
	m_pActiveCall->UpdateCallState(MAKING, OUTBOUND); 
	//m_pActiveCall->SetLine(this);			 
	lrc = lineMakeCall(m_hLine, &(m_pActiveCall->m_hCall), lpDestAddress, dwCountryCode, m_lpCallParms); 
	if(lrc < 0)  
	{						// need to report an error 
		TRACE("*** TALKER32 ***: MakeCall failed on line %d\n", m_dwLineID); 
		RemoveCall(); 
		return lrc; 
	} 
	//m_pActiveCall->m_hCall = hCall; 
	TRACE("*** TALKER32 ***: MakeCall hCall=%lx asyncID=%d\n",m_pActiveCall->m_hCall, lrc); 
	m_pApp->SetAsyncID((DWORD) lrc, MAKING, FUNCTION_MAKECALL,  
						(LPVOID) this, (LPVOID) m_pActiveCall);  
	return lrc; 
} 
 
// lineDial wrapper 
LONG CTapiLine::ctlLineDial(CTapiCall *pCall, LPCSTR lpAddr, DWORD dwCountryCode) 
{ 
	LONG lrc = -1; 
 
	if(pCall == NULL) pCall = m_pActiveCall; 
	if(pCall == NULL) return -1; 
 
	lrc = lineDial(pCall->m_hCall, lpAddr, dwCountryCode); 
 
	if(lrc < 0)  
	{						// need to report an error 
		TRACE("*** TALKER32 ***: lineDial failed on line %d\n", m_dwLineID); 
		return lrc; 
	} 
	m_pApp->SetAsyncID((DWORD) lrc, DIALING, FUNCTION_DIAL,  
						(LPVOID) this, (LPVOID) pCall);  
	return lrc; 
} 
 
// lineAnswer wrapper 
LONG CTapiLine::ctlLineAnswer(HCALL hCall) 
{ 
	LONG lrc = -1; 
 
	// Always assume active call. If hCall, check it against active call 
	//if(m_pActiveCall == NULL || hCall != m_pActiveCall->m_hCall) return -1L; 
	if(m_pActiveCall == NULL)  return -1; 
	if(hCall != NULL && hCall != m_pActiveCall->m_hCall) return -1L; 
	if(hCall == NULL ) hCall = m_pActiveCall->m_hCall; 
	m_pActiveCall->UpdateCallState(ANSWERING, INBOUND); 
   	lrc = lineAnswer(hCall, NULL, 0); 
	if(lrc < 0) 
	{						// need to report an error 
		TRACE("*** TALKER32 ***: Answer failed on line %d\n", m_dwLineID); 
//		RemoveCall();	// can't remove it here; must move to some list 
		return lrc; 
	} 
//	m_pActiveCall->m_hCall = hCall; 
	m_pApp->SetAsyncID((DWORD) lrc, MAKING, FUNCTION_ANSWER,  
						(LPVOID) this, (LPVOID) m_pActiveCall); 
	return lrc;						  
}	 
 
// lineDrop wrapper 
LONG CTapiLine::ctlLineDrop(CTapiCall *pCall) 
{ 
	LONG lrc = -1; 
 
	if(pCall == NULL) pCall = m_pActiveCall; 
	if(pCall == NULL) return -1; 
	// special case-drop while MakeCall has not returned 
	if(MAKING == pCall->GetCallState())  
	{ 
		pCall->UpdateCallState(STOP_MAKING); 
		return 0L; 
	} 
	pCall->StopWave();	//stop WAVE here  
	if(pCall->m_hCall) lrc = lineDrop(pCall->m_hCall, NULL, NULL); 
	if(lrc < 0) return lrc; 
	// set it up for DROPPING 
	m_pApp->SetAsyncID((DWORD) lrc, pCall->GetCallState(), FUNCTION_DROP, 
					  	(LPVOID) this, (LPVOID) m_pActiveCall); 
	pCall->UpdateCallState(DROPPING); 
	m_pApp->NotifyFrontEnd(m_dwLineID);	// do it from here or from onCallState? 
	return lrc; 
} 
 
// lineDeallocateCall wrapper 
LONG CTapiLine::ctlLineDeallocateCall(CTapiCall *pCall) 
{ 
	LONG lrc = -1; 
 
	TRACE(" *** enter ctlLineDeallocateCall pCall=%lx\n",pCall); 
	if(pCall == NULL) pCall = m_pActiveCall; 
	if(pCall == NULL) return -1; 
	TRACE(" *** calling lineDeallocateCall pCall=%lx\n",pCall); 
	lrc = lineDeallocateCall(pCall->m_hCall); 
	TRACE(" *** lineDeallocateCall rc=%lx\n",lrc); 
	if(lrc) 		// Failed, log the info 
	{ 
		// log the information here; can't just free it up 
		pCall->UpdateCallState(0xffff, 0xffff, 0xffff, DEALLOCATE_FAILED); 
		return lrc; 
	} 
	RemoveCall(pCall); 
	m_pApp->NotifyFrontEnd(m_dwLineID); 
	TRACE(" *** exit ctlLineDeallocateCall pCall=%lx\n",pCall); 
	return lrc; 
} 
 
// Create a call as a result of offering 
LONG CTapiLine::ctlOfferCall(HCALL hCall) 
{ 
	if(hCall == NULL) return -1L; 
	if(m_pActiveCall)  
	{ 
		TRACE("*** TALKER32 ***: Offering call while other call is active on line %d\n", m_dwLineID);  
		return -1L; 
	} 
	LONG lrc = AddCall(hCall); 
	if(lrc || m_pActiveCall == NULL) 
	{ 
		TRACE("*** TALKER32 ***: AddCall failed\n"); 
		return lrc; 
	} 
	m_pActiveCall->UpdateCallState(OFFERING, INBOUND); 
	m_pActiveCall->SetLine(this);			 
	m_pApp->NotifyFrontEnd(m_dwLineID); 
	return 0L; 
} 
 
// Add a call on this line 
LONG CTapiLine::AddCall(HCALL hCall) 
{ 
 
	TRACE("*** TALKER32 ***: AddCall #%lx entering wait for sem\n", hCall); 
	DWORD dwrc = WaitForSingleObject(m_hCallSem, 15000); 
	if(dwrc != WAIT_OBJECT_0)  
	{ 
		TRACE("*** TALKER32 ***: AddCall wait for sem failed rc=%lx\n", dwrc); 
		return -1L; 
	} 
	m_pActiveCall = new CTapiCall(hCall, this); 
	if(m_pActiveCall && m_uiWaveInID != 0xffff && m_uiWaveOutID != 0xffff) m_pActiveCall->InitWave(); 
	TRACE("*** TALKER32 ***: AddCall #%lx releasing mutex\n", hCall); 
	ReleaseMutex(m_hCallSem); 
	if(m_pActiveCall == NULL) return -1L; 
	if(NULL == m_pActiveCall->GetStateSemHandle()) return -1L;	// failed to alloc mutex 
	return 0L;	// why 0?.... 
} 
 
// Remove active or any other call 
void CTapiLine::RemoveCall(CTapiCall *pCall) 
{ 
	// Protect this against async returns that may try to do something w/pCall!!!  
	// first claim the call list mutex 
	HANDLE hStateSem; 
	DWORD dwWS; 
 
	TRACE("*** TALKER32 ***: RemoveCall entering wait for sem\n"); 
	DWORD dwrc = WaitForSingleObject(m_hCallSem, 15000); 
	if(dwrc != WAIT_OBJECT_0)  
	{ 
		TRACE("*** TALKER32 ***: RemoveCall #%lx wait for sem failed rc=%lx\n", pCall, dwrc); 
		return; 
	} 
	if(pCall == NULL) pCall = m_pActiveCall; 
	if(pCall == NULL) goto FinishRemoveCall;		// nothing to do 
 
	hStateSem = pCall->GetStateSemHandle(); 
	if(hStateSem)		// in case somebody else tries to call UpdateCallState... 
	{ 
		WaitForSingleObject(hStateSem, 5000); 
		CloseHandle(hStateSem); 
 	} 
	// cleanup async requests associated with this call here !!! 
	dwWS = pCall->GetWaveStatus(); 
	if(dwWS != WAVE_IDLE) 	// Defer removing call ???... 
		TRACE("*** TALKER32 ***: removing call while WAVE state=%d\n", dwWS); 
	delete pCall; 
	if(pCall == m_pActiveCall) m_pActiveCall = NULL; 
FinishRemoveCall: 
	TRACE("*** TALKER32 ***: RemoveCall %lx releasing mutex\n", pCall); 
	ReleaseMutex(m_hCallSem); 
	return; 
} 
 
// purge (all) calls on the line, no questions asked 
void CTapiLine::ctlPurgeCalls() 
{ 
 
	TRACE("*** TALKER32 ***: Enter PurgeCalls for line %d\n", m_dwLineID); 
// for now only delete the active call. When the calls storage is impl, add logic 
	if(NULL == m_pActiveCall) return; 
// Do for all calls 
	RemoveCall(); 
	TRACE("*** TALKER32 ***: Exit PurgeCalls for line %d\n", m_dwLineID); 
} 
 
// React to changes in call status; needs to be protected  
// Call NotifyFrontEnd after this function to update indicators 
void CTapiLine::ctlUpdateCallStatus(WORD wFunction, DWORD dwStatus, CTapiCall *pCall) 
{ 
 
	switch(wFunction) 
	{ 
	 	case 0:			// status change is NOT a result of async return 
			pCall->UpdateCallState((WORD) dwStatus); 
		break; 
 
		case FUNCTION_MAKECALL: 
//		case FUNCTION_DIAL: 
		if(dwStatus < 0)  
		{ 
			if(pCall) 	// do not try to drop; can only drop calls that were made  
			{ 
				pCall->UpdateCallState(IDLE, IDLE, LINECALLSTATE_UNKNOWN, MAKE_FAILED); 
				ctlLineDeallocateCall(pCall); 
			}	 
		}	 
		else pCall->UpdateCallState(PROGRESS); 
		break; 
 
		case FUNCTION_DROP:		// if failed, find out the reason 
			if(m_pActiveCall==NULL || !IsBadReadPtr(pCall, 32)) 
			{ 
				TRACE("*** TALKER32 ***: Trying to update call state for invalid pCall, pCall=%lx, m_pActive=%lx\n", 
					pCall, m_pActiveCall); 
				break;	  
			} 
			if(dwStatus < 0) 	// do NOT try to deallocate or change state 
			{ 
				// mark it as tried to drop & failed 
				pCall->UpdateCallState(0xffff, 0xffff, 0xffff, DROP_FAILED); 
			} 
			else pCall->UpdateCallState(DROPPING); 
		break;	 
 
		case FUNCTION_ANSWER: 
		// does not mean we have an active connection yet 
			if(dwStatus < 0) 	// don't know what to do, try to drop... 
			{ 
				pCall->UpdateCallState(DROPPING, 0xffff, 0xffff, ANSWER_FAILED); 
				ctlLineDrop(m_pActiveCall); 
			} 
		break;	 
 
		case FUNCTION_DEVSPEC: 
			if(dwStatus & 0x80000000) 
				ctlUpdateDevSpecStatus(DEVSPEC_STARTFAILED); 
			else ctlUpdateDevSpecStatus(dwStatus ? DEVSPEC_RESULTFAILED : DEVSPEC_IDLE); 
			//ctlUpdateDevSpecStatus(DEVSPEC_PROGRESS); 
				 
		break; 
 
		case FUNCTION_FORWARD: 
			TRACE("TALKER32: lineForward reply status=%lx\n", dwStatus); 
		break; 
 
		default: 
		break;	 
 
		m_pApp->NotifyFrontEnd(m_dwLineID);		// always update front end 
	} // end of switch(wFunction) 
 
} 
 
// Update call info based on HCALL 
BOOL CTapiLine::ctlUpdateCallInfo(HCALL hCall, LPLINECALLINFO lpCallInfo) 
{ 
	// find the call, delete the current CALLINFO if any, set new CALLINFO 
 
	if(m_pActiveCall->m_hCall != hCall) return FALSE;	// for now 
	// m_pActiveCall->UpdateCallInfo(lpCallInfo);	// must be protected 
	return FALSE;	// always for now, so the caller deletes CALLINFO 
} 
 
//    get the id of the wave device corresponding to the line 
//    COMMENTS: wave devices use an unsigned integer id.  we must allocate enough 
//               space for this id to be returned from the driver. 
void CTapiLine::ctlGetWaveDeviceID (WORD wType) 
{ 
	LONG lrc; 
	DWORD dwID;  
	VARSTRING *pvsVarStr = (VARSTRING *) new char[sizeof(VARSTRING) + 64]; 
// allocate 4 extra bytes for wave id.  
                  
	if(pvsVarStr == NULL) goto GetIDFail;                  
	pvsVarStr->dwTotalSize =  sizeof (VARSTRING) + 64; 
	if ((lrc = lineGetID (m_hLine, 0, 0, LINECALLSELECT_LINE,  
		pvsVarStr, wType == WAVEIN ? "wave/in" : wType == WAVEOUT ? "wave/out" : "xxx")) < 0) 
		goto GetIDFail; 
	// if the returned id was not binary, something is wrong  
	if (!m_bRemote && pvsVarStr->dwStringFormat != STRINGFORMAT_BINARY)  
	{ 	 
		#ifdef TAPI20 
			if(pvsVarStr->dwStringFormat == STRINGFORMAT_ASCII || pvsVarStr->dwStringFormat == STRINGFORMAT_UNICODE) 
			{ 
				m_bRemote = TRUE;		// the line is running remotely 
				m_csName = ((BYTE *)pvsVarStr+sizeof(VARSTRING)); 
				m_csName += "\\"; 
			} 
		#endif 
		//goto GetIDFail; 
	} 
	dwID = (DWORD) *((BYTE *)pvsVarStr+sizeof(VARSTRING)); // get id  
	delete pvsVarStr; 
	if(wType == WAVEIN) m_uiWaveInID = (UINT)(WORD) dwID;  
	else if(wType == WAVEOUT) m_uiWaveOutID = (UINT)(WORD) dwID;  
	return; 
GetIDFail: 
	if(pvsVarStr != NULL) delete pvsVarStr; 
} 
 
//call lineGetLineDevStatus here 
LPLINEDEVSTATUS CTapiLine::ctlGetLineDevStatus() 
{ 
    LPLINEDEVSTATUS lpDevStatus = NULL; 
    LONG lrc = 0; 
    DWORD dwSize = sizeof(LINEDEVSTATUS)+1000; 
    DWORD i; 
     
    // keep trying until buffer is big enough  
    for (i=0; i<2; i++) 
	{ 
    	lpDevStatus = (LPLINEDEVSTATUS) new char[dwSize]; 
    	if (lpDevStatus == NULL) 
        	return NULL; 
	    lpDevStatus->dwTotalSize = dwSize; 
 
        lrc = lineGetLineDevStatus(m_hLine,lpDevStatus); 
		if(lrc < 0) goto FinishLineGetDevStatus; 
 
        dwSize = lpDevStatus->dwNeededSize;  
		if(dwSize <= lpDevStatus->dwTotalSize) break;   // success 
		          
		delete (char *) lpDevStatus; 
	}  
 
    return lpDevStatus; 
 
FinishLineGetDevStatus:  
    if (lpDevStatus != NULL) 
        delete (LPBYTE) lpDevStatus; 
    return NULL; 
}  
 
// Configuration dialog 
void CTapiLine::ctlConfigDialog(HWND hWnd, LPSTR lpClass) 
{ 
	lineConfigDialog(m_dwLineID, hWnd, lpClass); 
} 
 
LONG CTapiLine::ctlLineDevSpecific(DWORD dwFunc, DWORD dwAddressID, HCALL hCall, 
						 LPVOID lParams, DWORD dwSize) 
{ 
	LONG lrc; 
	TRACE("*** TALKER32: calling DevSpec dwSize=%d\n", dwSize); 
	lrc = lineDevSpecific(m_hLine, dwAddressID, hCall, lParams, dwSize); 
	if(lrc < 0)  
	{						// need to report an error 
		TRACE("*** TALKER32: DevSpec failed on line %d\n", m_dwLineID); 
		return lrc; 
	} 
	TRACE("*** TALKER32: DevSpec asyncID=%d\n",lrc); 
	ctlUpdateDevSpecStatus(DEVSPEC_STARTING); 
	// update the function in the beginning of execution ONLY 
	ctlUpdateDevSpecFunction(dwFunc); 
	m_pApp->SetAsyncID((DWORD) lrc, MAKING, FUNCTION_DEVSPEC,  
						(LPVOID) this, (LPVOID) m_pActiveCall);  
	return lrc; 
} 
 
LONG CTapiLine::ctlLineForward(LPSTR lpDest) 
{ 
	if(!lpDest) return -1; 
	LPLINEFORWARDLIST lpLFL = (LPLINEFORWARDLIST) new char[sizeof(LINEFORWARDLIST) + lstrlen(lpDest)+8]; 
	if(!lpLFL) return -1; 
	BOOL bAllAddr = TRUE; 
	DWORD dwAddrID = 0L; 
	HCALL hCC; 
	LONG lrc; 
 
	lpLFL->dwNumEntries = 1; 
	lpLFL->dwTotalSize = sizeof(LINEFORWARDLIST) + lstrlen(lpDest)+8; 
	lpLFL->ForwardList[0].dwForwardMode = LINEFORWARDMODE_UNCOND; 
	lpLFL->ForwardList[0].dwCallerAddressSize = 0; 
	lpLFL->ForwardList[0].dwCallerAddressOffset = 0; 
	lpLFL->ForwardList[0].dwDestCountryCode = 0; 
	lpLFL->ForwardList[0].dwDestAddressSize = (DWORD)lstrlen(lpDest); 
	lpLFL->ForwardList[0].dwDestAddressOffset = sizeof(LINEFORWARDLIST); 
	memcpy((LPBYTE)lpLFL+sizeof(LINEFORWARDLIST), lpDest, lstrlen(lpDest)+1); 
 
	lrc = lineForward(m_hLine, bAllAddr, dwAddrID, lpLFL, 4, &hCC, NULL); 
	if(lrc < 0)  
		TRACE("*** TALKER32: lineForward failed on line %d rc=%lx\n", m_dwLineID, lrc); 
	else m_pApp->SetAsyncID((DWORD) lrc, MAKING, FUNCTION_FORWARD,  
						(LPVOID) this, NULL);  
	delete (LPBYTE) lpLFL; 
	return lrc; 
} 
 
// Determine if the line is running on the remote computer 
// Since MS doesn't provide a standard way, use whatever mechanism is available for the SP 
// Override this function to implement a better way 
BOOL IsLineRemote(LPLINEDEVCAPS lpDC) 
{ 
	int nInd; 
	CString csTemp; 
 
	if(!lpDC->dwLineNameOffset) return FALSE; 
	csTemp.Format("%s", (LPSTR)((LPBYTE)lpDC + lpDC->dwLineNameOffset)); 
	csTemp.MakeUpper(); 
	if(-1 == csTemp.Find("DXXXB")) return FALSE;		// non-Dialogic TSP 
	if(-1 == (nInd = csTemp.Find("\\"))) return FALSE;	// computer name not a part of board name 
	csTemp = csTemp.Left(nInd); 
	char szName[MAX_COMPUTERNAME_LENGTH+1]; 
	DWORD dwSize = MAX_COMPUTERNAME_LENGTH+1; 
	if(!GetComputerName(szName, &dwSize)) return FALSE;	// Name unavailable for some reason 
	if(!strcmpi(szName, (LPCTSTR)csTemp)) return FALSE;	// Same name 
	return TRUE;					 // Finally, the names ARE different 
} 
 
/* call lineGetLineDevCaps here 
LPLINEDEVSTATUS CTapiLine::ctlGetLineDevCaps() 
{ 
    LPLINEDEVCAPS lpDevCaps = NULL; 
    LONG lrc = 0; 
    DWORD dwSize = sizeof(LINEDEVCAPS)+1000; 
    DWORD i; 
     
    // keep trying until buffer is big enough  
    for (i=0; i<2; i++) 
	{ 
    	lpDevStatus = (LPLINEDEVSTATUS) new char[dwSize]; 
    	if (lpDevStatus == NULL) 
        	return NULL; 
	    lpDevStatus->dwTotalSize = dwSize; 
 
        lrc = lineGetLineDevStatus(m_hLine,lpDevStatus); 
		if(lrc < 0) goto FinishLineGetDevStatus; 
 
        dwSize = lpDevStatus->dwNeededSize;  
		if(dwSize <= lpDevStatus->dwTotalSize) break;   // success 
		          
		delete (char *) lpDevStatus; 
	}  
 
    return lpDevStatus; 
 
FinishLineGetDevStatus:  
    if (lpDevStatus != NULL) 
        delete (LPBYTE) lpDevStatus; 
    return NULL; 
} */