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; } */