/*++ Copyright (c) 1995-1996 Microsoft Corporation Module Name: atsp.c Notes: --*/ #include "atsp.h" BOOL WINAPI DllMain( HANDLE hDLL, DWORD dwReason, LPVOID lpReserved ) { if (dwReason == DLL_PROCESS_ATTACH) { ghInst = hDLL; #if DBG { HKEY hKey; DWORD dwDataSize, dwDataType; char szAtsp32DebugLevel[] = "Atsp32DebugLevel"; RegOpenKeyExA( HKEY_LOCAL_MACHINE, gszAtspKey, 0, KEY_ALL_ACCESS, &hKey ); dwDataSize = sizeof (DWORD); gdwDebugLevel=0; RegQueryValueEx( hKey, szAtsp32DebugLevel, 0, &dwDataType, (LPBYTE) &gdwDebugLevel, &dwDataSize ); RegCloseKey (hKey); } #endif } return TRUE; } void CommThread( PDRVLINE pLine ) { char buf[4]; DWORD dwThreadID = GetCurrentThreadId(), dwNumBytes; HANDLE hComm = pLine->hComm, hEvent; LPOVERLAPPED pOverlapped = &pLine->Overlapped; DBGOUT(( 3, "CommThread (id=%d): enter, port=%s", dwThreadID, pLine->szComm )); hEvent = pOverlapped->hEvent; buf[0] = buf[1] = '.'; // // Loop waiting for i/o to complete (either the Write done in // TSPI_lineMakeCall or the Reads done to retrieve status info). // Note that TSPI_lineDrop or TSPI_lineCloseCall may set the // event to alert us that they're tearing down the call, in // which case we just exit. // for (;;) { if (WaitForSingleObject (hEvent, ATSP_TIMEOUT) == WAIT_OBJECT_0) { if (pLine->bDropInProgress == TRUE) { DBGOUT((2, "CommThread (id=%d): drop in progress")); goto CommThread_exit; } GetOverlappedResult (hComm, pOverlapped, &dwNumBytes, FALSE); ResetEvent (hEvent); } else { DBGOUT((2, "CommThread (id=%d): wait timeout")); SetCallState (pLine, LINECALLSTATE_IDLE, 0); goto CommThread_exit; } buf[1] &= 0x7f; // nuke the parity bit DBGOUT(( 3, "CommThread (id=%d): read '%c'", dwThreadID, (buf[1] == '\r' ? '.' : buf[1]) )); switch ((buf[0] << 8) + buf[1]) { case 'CT': // "CONNECT" case 'OK': // "OK" SetCallState (pLine, LINECALLSTATE_CONNECTED, 0); goto CommThread_exit; case 'SY': // "BUSY" case 'OR': // "ERROR" case 'NO': // "NO ANSWER", "NO DIALTONE", "NO CARRIER" SetCallState (pLine, LINECALLSTATE_IDLE, 0); goto CommThread_exit; default: break; } buf[0] = buf[1]; ZeroMemory (pOverlapped, sizeof (OVERLAPPED) - sizeof (HANDLE)); if ( 0 == ReadFile (hComm, &buf[1], 1, &dwNumBytes, pOverlapped)) { DBGOUT((2, "CommThread (id=%d): fail to read from line")); goto CommThread_exit; } } CommThread_exit: CloseHandle (hEvent); DBGOUT((3, "CommThread (id=%d): exit", dwThreadID)); ExitThread (0); } // // We get a slough of C4047 (different levels of indrection) warnings down // below in the initialization of FUNC_PARAM structs as a result of the // real func prototypes having params that are types other than DWORDs, // so since these are known non-interesting warnings just turn them off // #pragma warning (disable:4047) // // --------------------------- TAPI_lineXxx funcs ----------------------------- // LONG TSPIAPI TSPI_lineClose( HDRVLINE hdLine ) { LONG lResult = 0; #if DBG FUNC_PARAM params[] = { { gszhdLine, hdLine } }; FUNC_INFO info = { "TSPI_lineClose", 1, params, }; #endif Prolog (&info); DrvFree ((PDRVLINE) hdLine); return (Epilog (&info, lResult)); } LONG TSPIAPI TSPI_lineCloseCall( HDRVCALL hdCall ) { PDRVLINE pLine = (PDRVLINE) hdCall; #if DBG FUNC_PARAM params[] = { { gszhdCall, hdCall } }; FUNC_INFO info = { "TSPI_lineCloseCall", 1, params }; #endif // // Note that in TAPI 2.0 TSPI_lineCloseCall can get called // without TSPI_lineDrop ever being called, so we need to // be prepared for either case. // Prolog (&info); DropActiveCall (pLine); pLine->htCall = NULL; return (Epilog (&info, 0)); } LONG TSPIAPI TSPI_lineConditionalMediaDetection( HDRVLINE hdLine, DWORD dwMediaModes, LPLINECALLPARAMS const lpCallParams ) { #if DBG FUNC_PARAM params[] = { { gszhdLine, hdLine }, { "dwMediaModes", dwMediaModes }, { gszlpCallParams, lpCallParams } }; FUNC_INFO info = { "TSPI_lineConditionalMediaDetection", 3, params }; #endif // // This func is really a no-op for us, since we don't look // for incoming calls (though we do say we support them to // make apps happy) // Prolog (&info); return (Epilog (&info, 0)); } LONG TSPIAPI TSPI_lineDrop( DRV_REQUESTID dwRequestID, HDRVCALL hdCall, LPCSTR lpsUserUserInfo, DWORD dwSize ) { PDRVLINE pLine = (PDRVLINE) hdCall; #if DBG FUNC_PARAM params[] = { { gszdwRequestID, dwRequestID }, { gszhdCall, hdCall }, { "lpsUserUserInfo", lpsUserUserInfo }, { gszdwSize, dwSize } }; FUNC_INFO info = { "TSPI_lineDrop", 4, params }; #endif Prolog (&info); DropActiveCall (pLine); SetCallState (pLine, LINECALLSTATE_IDLE, 0); (*gpfnCompletionProc)(dwRequestID, 0); return (Epilog (&info, dwRequestID)); } LONG TSPIAPI TSPI_lineGetAddressCaps( DWORD dwDeviceID, DWORD dwAddressID, DWORD dwTSPIVersion, DWORD dwExtVersion, LPLINEADDRESSCAPS lpAddressCaps ) { #if DBG FUNC_PARAM params[] = { { gszdwDeviceID, dwDeviceID }, { "dwAddressID", dwAddressID }, { "dwTSPIVersion", dwTSPIVersion }, { "dwExtVersion", dwExtVersion }, { "lpAddressCaps", lpAddressCaps } }; FUNC_INFO info = { "TSPI_lineGetAddressCaps", 5, params }; #endif LONG lResult = 0; Prolog (&info); if (dwAddressID != 0) { lResult = LINEERR_INVALADDRESSID; } lpAddressCaps->dwNeededSize = lpAddressCaps->dwUsedSize = sizeof(LINEADDRESSCAPS); lpAddressCaps->dwLineDeviceID = dwDeviceID; lpAddressCaps->dwAddressSharing = LINEADDRESSSHARING_PRIVATE; lpAddressCaps->dwCallInfoStates = LINECALLINFOSTATE_MEDIAMODE | LINECALLINFOSTATE_APPSPECIFIC; lpAddressCaps->dwCallerIDFlags = lpAddressCaps->dwCalledIDFlags = lpAddressCaps->dwRedirectionIDFlags = lpAddressCaps->dwRedirectingIDFlags = LINECALLPARTYID_UNAVAIL; lpAddressCaps->dwCallStates = LINECALLSTATE_IDLE | LINECALLSTATE_OFFERING | LINECALLSTATE_ACCEPTED | LINECALLSTATE_DIALTONE | LINECALLSTATE_DIALING | LINECALLSTATE_CONNECTED | LINECALLSTATE_PROCEEDING | LINECALLSTATE_DISCONNECTED | LINECALLSTATE_UNKNOWN; lpAddressCaps->dwDialToneModes = LINEDIALTONEMODE_UNAVAIL; lpAddressCaps->dwBusyModes = LINEBUSYMODE_UNAVAIL; lpAddressCaps->dwSpecialInfo = LINESPECIALINFO_UNAVAIL; lpAddressCaps->dwDisconnectModes = LINEDISCONNECTMODE_NORMAL | LINEDISCONNECTMODE_BUSY | LINEDISCONNECTMODE_NOANSWER | LINEDISCONNECTMODE_UNAVAIL | LINEDISCONNECTMODE_NODIALTONE; lpAddressCaps->dwMaxNumActiveCalls = 1; lpAddressCaps->dwAddrCapFlags = LINEADDRCAPFLAGS_DIALED; lpAddressCaps->dwCallFeatures = LINECALLFEATURE_ACCEPT | LINECALLFEATURE_ANSWER | LINECALLFEATURE_DROP | LINECALLFEATURE_SETCALLPARAMS; lpAddressCaps->dwAddressFeatures = LINEADDRFEATURE_MAKECALL; return (Epilog (&info, lResult)); } LONG TSPIAPI TSPI_lineGetAddressStatus( HDRVLINE hdLine, DWORD dwAddressID, LPLINEADDRESSSTATUS lpAddressStatus ) { #if DBG FUNC_PARAM params[] = { { gszhdLine, hdLine }, { "dwAddressID", dwAddressID }, { "lpAddressStatus", lpAddressStatus } }; FUNC_INFO info = { "TSPI_lineGetAddressStatus", 3, params }; #endif LONG lResult = 0; PDRVLINE pLine = (PDRVLINE) hdLine; Prolog (&info); lpAddressStatus->dwNeededSize = lpAddressStatus->dwUsedSize = sizeof(LINEADDRESSSTATUS); lpAddressStatus->dwNumActiveCalls = (pLine->htCall ? 1 : 0); lpAddressStatus->dwAddressFeatures = LINEADDRFEATURE_MAKECALL; return (Epilog (&info, lResult)); } LONG TSPIAPI TSPI_lineGetCallAddressID( HDRVCALL hdCall, LPDWORD lpdwAddressID ) { #if DBG FUNC_PARAM params[] = { { gszhdCall, hdCall }, { "lpdwAddressID", lpdwAddressID } }; FUNC_INFO info = { "TSPI_lineGetCallAddressID", 2, params }; #endif // // We only support 1 address (id=0) // Prolog (&info); *lpdwAddressID = 0; return (Epilog (&info, 0)); } LONG TSPIAPI TSPI_lineGetCallInfo( HDRVCALL hdCall, LPLINECALLINFO lpLineInfo ) { #if DBG FUNC_PARAM params[] = { { gszhdCall, hdCall }, { "lpLineInfo", lpLineInfo } }; FUNC_INFO info = { "TSPI_lineGetCallInfo", 2, params }; #endif LONG lResult = 0; PDRVLINE pLine = (PDRVLINE) hdCall; Prolog (&info); lpLineInfo->dwNeededSize = lpLineInfo->dwUsedSize = sizeof(LINECALLINFO); lpLineInfo->dwBearerMode = LINEBEARERMODE_VOICE; lpLineInfo->dwMediaMode = pLine->dwMediaMode; lpLineInfo->dwCallStates = LINECALLSTATE_IDLE | LINECALLSTATE_DIALTONE | LINECALLSTATE_DIALING | LINECALLSTATE_CONNECTED | LINECALLSTATE_PROCEEDING | LINECALLSTATE_DISCONNECTED | LINECALLSTATE_UNKNOWN; lpLineInfo->dwOrigin = LINECALLORIGIN_OUTBOUND; lpLineInfo->dwReason = LINECALLREASON_DIRECT; lpLineInfo->dwCallerIDFlags = lpLineInfo->dwCalledIDFlags = lpLineInfo->dwConnectedIDFlags = lpLineInfo->dwRedirectionIDFlags = lpLineInfo->dwRedirectingIDFlags = LINECALLPARTYID_UNAVAIL; return (Epilog (&info, lResult)); } LONG TSPIAPI TSPI_lineGetCallStatus( HDRVCALL hdCall, LPLINECALLSTATUS lpLineStatus ) { #if DBG FUNC_PARAM params[] = { { gszhdCall, hdCall }, { "lpLineStatus", lpLineStatus } }; FUNC_INFO info = { "TSPI_lineGetCallStatus", 2, params }; #endif LONG lResult = 0; PDRVLINE pLine = (PDRVLINE) hdCall; Prolog (&info); lpLineStatus->dwNeededSize = lpLineStatus->dwUsedSize = sizeof(LINECALLSTATUS); lpLineStatus->dwCallState = pLine->dwCallState; if (pLine->dwCallState != LINECALLSTATE_IDLE) { lpLineStatus->dwCallFeatures = LINECALLFEATURE_DROP; } return (Epilog (&info, lResult)); } LONG TSPIAPI TSPI_lineGetDevCaps( DWORD dwDeviceID, DWORD dwTSPIVersion, DWORD dwExtVersion, LPLINEDEVCAPS lpLineDevCaps ) { #if DBG FUNC_PARAM params[] = { { gszdwDeviceID, dwDeviceID }, { "dwTSPIVersion", dwTSPIVersion }, { "dwExtVersion", dwExtVersion }, { "lpLineDevCaps", lpLineDevCaps } }; FUNC_INFO info = { "TSPI_lineGetDevCaps", 4, params }; #endif LONG lResult = 0; static WCHAR szProviderInfo[] = L"AT-compatible modem service provider"; #define PROVIDER_INFO_SIZE (37 * sizeof (WCHAR)) Prolog (&info); lpLineDevCaps->dwNeededSize = sizeof (LINEDEVCAPS) + PROVIDER_INFO_SIZE + (MAX_DEV_NAME_LENGTH + 1) * sizeof (WCHAR); if (lpLineDevCaps->dwTotalSize >= lpLineDevCaps->dwNeededSize) { #define LINECONFIG_SIZE (2 * (MAX_DEV_NAME_LENGTH + 1) + 40) char szLineConfig[LINECONFIG_SIZE], szLineN[16], *p; HKEY hKey; DWORD dwDataSize, dwDataType; lpLineDevCaps->dwUsedSize = lpLineDevCaps->dwNeededSize; lpLineDevCaps->dwProviderInfoSize = PROVIDER_INFO_SIZE; lpLineDevCaps->dwProviderInfoOffset = sizeof(LINEDEVCAPS); My_lstrcpyW ((WCHAR *)(lpLineDevCaps + 1), szProviderInfo); RegOpenKeyEx( HKEY_LOCAL_MACHINE, gszAtspKey, 0, KEY_ALL_ACCESS, &hKey ); dwDataSize = LINECONFIG_SIZE; wsprintf (szLineN, "Line%d", dwDeviceID - gdwLineDeviceIDBase); lstrcpy (szLineConfig, gszDefLineConfigParams); RegQueryValueEx( hKey, szLineN, 0, &dwDataType, (LPBYTE) szLineConfig, &dwDataSize ); RegCloseKey (hKey); for (p = szLineConfig; *p != ','; p++); *p = 0; lpLineDevCaps->dwLineNameSize = (lstrlen (szLineConfig) + 1) * sizeof (WCHAR); lpLineDevCaps->dwLineNameOffset = sizeof(LINEDEVCAPS) + PROVIDER_INFO_SIZE; MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, szLineConfig, -1, (WCHAR *) ((LPBYTE) (lpLineDevCaps + 1) + PROVIDER_INFO_SIZE), lpLineDevCaps->dwLineNameSize ); } else { lpLineDevCaps->dwUsedSize = sizeof(LINEDEVCAPS); } lpLineDevCaps->dwStringFormat = STRINGFORMAT_ASCII; lpLineDevCaps->dwAddressModes = LINEADDRESSMODE_ADDRESSID; lpLineDevCaps->dwNumAddresses = 1; lpLineDevCaps->dwBearerModes = LINEBEARERMODE_VOICE; lpLineDevCaps->dwMaxRate = 9600; lpLineDevCaps->dwMediaModes = LINEMEDIAMODE_INTERACTIVEVOICE | LINEMEDIAMODE_DATAMODEM; lpLineDevCaps->dwDevCapFlags = LINEDEVCAPFLAGS_CLOSEDROP | LINEDEVCAPFLAGS_DIALBILLING | LINEDEVCAPFLAGS_DIALQUIET | LINEDEVCAPFLAGS_DIALDIALTONE; lpLineDevCaps->dwMaxNumActiveCalls = 1; lpLineDevCaps->dwRingModes = 1; lpLineDevCaps->dwLineFeatures = LINEFEATURE_MAKECALL; return (Epilog (&info, lResult)); } LONG TSPIAPI TSPI_lineGetID( HDRVLINE hdLine, DWORD dwAddressID, HDRVCALL hdCall, DWORD dwSelect, LPVARSTRING lpDeviceID, LPCWSTR lpszDeviceClass, HANDLE hTargetProcess ) { #if DBG FUNC_PARAM params[] = { { gszhdLine, hdLine }, { "dwAddressID", dwAddressID }, { gszhdCall, hdCall }, { "dwSelect", dwSelect }, { "lpDeviceID", lpDeviceID }, { "lpszDeviceClass", lpszDeviceClass }, { "hTargetProcess", hTargetProcess } }; FUNC_INFO info = { "TSPI_lineGetID", 7, params }; #endif DWORD dwNeededSize = sizeof(VARSTRING) + sizeof (DWORD); LONG lResult = 0; PDRVLINE pLine = (dwSelect == LINECALLSELECT_CALL ? (PDRVLINE) hdCall : (PDRVLINE) hdLine); Prolog (&info); if (lstrcmpiW (lpszDeviceClass, L"tapi/line") == 0) { if (lpDeviceID->dwTotalSize < dwNeededSize) { lpDeviceID->dwUsedSize = 3*sizeof(DWORD); } else { lpDeviceID->dwUsedSize = dwNeededSize; lpDeviceID->dwStringFormat = STRINGFORMAT_BINARY; lpDeviceID->dwStringSize = sizeof(DWORD); lpDeviceID->dwStringOffset = sizeof(VARSTRING); *((LPDWORD)(lpDeviceID + 1)) = pLine->dwDeviceID; } lpDeviceID->dwNeededSize = dwNeededSize; } else if (lstrcmpiW (lpszDeviceClass, L"comm/datamodem") == 0) { dwNeededSize += (strlen (pLine->szComm) + 1) * sizeof (WCHAR); if (lpDeviceID->dwTotalSize < dwNeededSize) { lpDeviceID->dwUsedSize = 3 * sizeof(DWORD); } else { HANDLE hCommDup = NULL; if (!pLine->htCall) { DBGOUT((1, "TSPI_lineGetID32: error, no active call")); lResult = LINEERR_OPERATIONFAILED; goto TSPI_lineGetID_epilog; } if (!DuplicateHandle( GetCurrentProcess(), pLine->hComm, hTargetProcess, &hCommDup, 0, TRUE, DUPLICATE_SAME_ACCESS )) { DBGOUT(( 1, "TSPI_lineGetID: DupHandle failed, err=%ld", GetLastError() )); lResult = LINEERR_OPERATIONFAILED; goto TSPI_lineGetID_epilog; } lpDeviceID->dwUsedSize = dwNeededSize; lpDeviceID->dwStringFormat = STRINGFORMAT_BINARY; lpDeviceID->dwStringSize = dwNeededSize - sizeof(VARSTRING); lpDeviceID->dwStringOffset = sizeof(VARSTRING); *((HANDLE *)(lpDeviceID + 1)) = hCommDup; lstrcpy( ((char *)(lpDeviceID + 1)) + sizeof (HANDLE), pLine->szComm ); MultiByteToWideChar( CP_ACP, 0, pLine->szComm, -1, ((WCHAR *)(lpDeviceID + 1)) + sizeof (HANDLE), 256 ); } lpDeviceID->dwNeededSize = dwNeededSize; } else { lResult = LINEERR_NODEVICE; } TSPI_lineGetID_epilog: return (Epilog (&info, lResult)); } LONG TSPIAPI TSPI_lineGetLineDevStatus( HDRVLINE hdLine, LPLINEDEVSTATUS lpLineDevStatus ) { #if DBG FUNC_PARAM params[] = { { gszhdLine, hdLine }, { "lpLineDevStatus", lpLineDevStatus } }; FUNC_INFO info = { "TSPI_lineGetLineDevStatus", 2, params }; #endif LONG lResult = 0; PDRVLINE pLine = (PDRVLINE) hdLine; Prolog (&info); lpLineDevStatus->dwUsedSize = lpLineDevStatus->dwNeededSize = sizeof (LINEDEVSTATUS); lpLineDevStatus->dwNumActiveCalls = (pLine->htCall ? 1 : 0); //lpLineDevStatus->dwLineFeatures = lpLineDevStatus->dwDevStatusFlags = LINEDEVSTATUSFLAGS_CONNECTED | LINEDEVSTATUSFLAGS_INSERVICE; return (Epilog (&info, lResult)); } LONG TSPIAPI TSPI_lineGetNumAddressIDs( HDRVLINE hdLine, LPDWORD lpdwNumAddressIDs ) { #if DBG FUNC_PARAM params[] = { { gszhdLine, hdLine }, { "lpdwNumAddressIDs", lpdwNumAddressIDs } }; FUNC_INFO info = { "TSPI_lineGetNumAddressIDs", 2, params }; #endif LONG lResult = 0; PDRVLINE pLine = (PDRVLINE) hdLine; // // We only support 1 address (id=0) // Prolog (&info); *lpdwNumAddressIDs = 1; return (Epilog (&info, lResult)); } LONG TSPIAPI TSPI_lineMakeCall( DRV_REQUESTID dwRequestID, HDRVLINE hdLine, HTAPICALL htCall, LPHDRVCALL lphdCall, LPCWSTR lpszDestAddress, DWORD dwCountryCode, LPLINECALLPARAMS const lpCallParams ) { char szCommands[64], szCommand[64], szDestAddress[128]; DWORD dwThreadID, dwNumBytes, dwError; PDRVLINE pLine = (PDRVLINE) hdLine; #if DBG FUNC_PARAM params[] = { { gszdwRequestID, dwRequestID }, { gszhdLine, hdLine }, { "htCall", htCall }, { "lphdCall", lphdCall }, { "lpszDestAddress", szDestAddress }, { "dwCountryCode", dwCountryCode }, { gszlpCallParams, lpCallParams } }; FUNC_INFO info = { "TSPI_lineMakeCall", 7, params }; #endif if (lpszDestAddress) { WideCharToMultiByte( CP_ACP, 0, lpszDestAddress, -1, (LPSTR) szDestAddress, 128, NULL, NULL ); } Prolog (&info); // // Check to see if there's already another call // if (pLine->htCall) { (*gpfnCompletionProc)(dwRequestID, LINEERR_CALLUNAVAIL); goto TSPI_lineMakeCall_return; } // // Since we don't support TSPI_lineDial, fail if app tries // to pass a NULL lpszDestAddress (implying that app just // wants to go offhook) // if (lpszDestAddress == NULL) { (*gpfnCompletionProc)(dwRequestID, LINEERR_INVALADDRESS); goto TSPI_lineMakeCall_return; } // // Get the line's config info // { HKEY hKey; DWORD dwDataSize, dwDataType; char szLineN[8], *pszConfig, *p, *p2; wsprintf( szLineN, "Line%d", ((PDRVLINE) hdLine)->dwDeviceID - gdwLineDeviceIDBase ); dwDataSize = 256; pszConfig = DrvAlloc (dwDataSize); RegOpenKeyEx( HKEY_LOCAL_MACHINE, gszAtspKey, 0, KEY_ALL_ACCESS, &hKey ); RegQueryValueEx( hKey, szLineN, 0, &dwDataType, (LPBYTE) pszConfig, &dwDataSize ); pszConfig[dwDataSize] = '\0'; // *pszConfig = "MyLine,COM1,L0" RegCloseKey (hKey); // // szComm // for (p = pszConfig; *p != ','; p++); p++; // *p = "COM1,L0" for (p2 = p; *p2 != ','; p2++); *p2 = 0; // *p = "COM1" lstrcpy (pLine->szComm, p); // // szCommands // p2++; // *p2 = "L0" lstrcpy (szCommands, p2); DrvFree (pszConfig); } // // Open the port // if ((pLine->hComm = CreateFile( pLine->szComm, GENERIC_READ | GENERIC_WRITE, 0, //FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, // no security attrs OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL // no template file )) == INVALID_HANDLE_VALUE) { DBGOUT(( 3, "TSPI_lineMakeCall: CreateFile(%s) failed, err=%ld", pLine->szComm, GetLastError() )); (*gpfnCompletionProc)(dwRequestID, LINEERR_RESOURCEUNAVAIL); goto TSPI_lineMakeCall_return; } // // Setup up the modem command string. If there's an initial 'T' // or 'P' (for Tone or Pulse) in the dest address then disregard // it. Also if it's a voice call add the semi colon so we return // to cmd mode. // { char *p = (char *) szDestAddress; if (*p == 'T' || *p == 'P') { p++; } if (lpCallParams && lpCallParams->dwMediaMode != LINEMEDIAMODE_INTERACTIVEVOICE) { wsprintf (szCommand, "AT%sDT%s\r", szCommands, p); } else { wsprintf (szCommand, "AT%sDT%s;\r", szCommands, p); } } // // Init the data structure & tell tapi our handle to the call // pLine->htCall = htCall; pLine->bDropInProgress = FALSE; pLine->dwMediaMode = (lpCallParams ? lpCallParams->dwMediaMode : LINEMEDIAMODE_INTERACTIVEVOICE); *lphdCall = (HDRVCALL) pLine; // // Do an overlapped write, the comm thread will deal with the results // pLine->Overlapped.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); if (!WriteFile( pLine->hComm, szCommand, lstrlen (szCommand), &dwNumBytes, &pLine->Overlapped ) && (dwError = GetLastError()) != ERROR_IO_PENDING) { DBGOUT(( 1, "TSPI_lineMakeCall: WriteFile(%s) failed, error=%d", pLine->szComm, dwError )); pLine->htCall = NULL; CloseHandle (pLine->hComm); CloseHandle (pLine->Overlapped.hEvent); (*gpfnCompletionProc)(dwRequestID, LINEERR_OPERATIONFAILED); goto TSPI_lineMakeCall_return; } // // Complete the requests & set the initial call state // (*gpfnCompletionProc)(dwRequestID, 0); SetCallState (pLine, LINECALLSTATE_DIALING, 0); // // Spin the comm thread to handle the results of the Write above // { HANDLE hCommThread; if (!(hCommThread = CreateThread( (LPSECURITY_ATTRIBUTES) NULL, 0, (LPTHREAD_START_ROUTINE) CommThread, pLine, 0, &dwThreadID ))) { DBGOUT(( 1, "TSPI_lineMakeCall: CreateThread failed, err=%ld", GetLastError() )); GetOverlappedResult( pLine->hComm, &pLine->Overlapped, &dwNumBytes, TRUE ); SetCallState (pLine, LINECALLSTATE_IDLE, 0); CloseHandle (pLine->hComm); CloseHandle (pLine->Overlapped.hEvent); goto TSPI_lineMakeCall_return; } CloseHandle (hCommThread); } TSPI_lineMakeCall_return: return (Epilog (&info, dwRequestID)); } LONG TSPIAPI TSPI_lineNegotiateTSPIVersion( DWORD dwDeviceID, DWORD dwLowVersion, DWORD dwHighVersion, LPDWORD lpdwTSPIVersion ) { LONG lResult = 0; #if DBG FUNC_PARAM params[] = { { gszdwDeviceID, dwDeviceID }, { "dwLowVersion", dwLowVersion }, { "dwHighVersion", dwHighVersion }, { "lpdwTSPIVersion", lpdwTSPIVersion } }; FUNC_INFO info = { "TSPI_lineNegotiateTSPIVersion", 4, params }; #endif Prolog (&info); *lpdwTSPIVersion = 0x00020000; return (Epilog (&info, lResult)); } LONG TSPIAPI TSPI_lineOpen( DWORD dwDeviceID, HTAPILINE htLine, LPHDRVLINE lphdLine, DWORD dwTSPIVersion, LINEEVENT lpfnEventProc ) { LONG lResult; PDRVLINE pLine; #if DBG FUNC_PARAM params[] = { { gszdwDeviceID, dwDeviceID }, { "htLine", htLine }, { "lphdLine", lphdLine }, { "dwTSPIVersion", dwTSPIVersion }, { "lpfnEventProc", lpfnEventProc } }; FUNC_INFO info = { "TSPI_lineOpen", 5, params }; #endif Prolog (&info); if ((pLine = DrvAlloc (sizeof (DRVLINE)))) { pLine->htLine = htLine; pLine->pfnEventProc = lpfnEventProc; pLine->dwDeviceID = dwDeviceID; *lphdLine = (HDRVLINE) pLine; lResult = 0; } else { lResult = LINEERR_NOMEM; } return (Epilog (&info, lResult)); } LONG TSPIAPI TSPI_lineSetDefaultMediaDetection( HDRVLINE hdLine, DWORD dwMediaModes ) { #if DBG FUNC_PARAM params[] = { { gszhdLine, hdLine }, { "dwMediaModes", dwMediaModes } }; FUNC_INFO info = { "TSPI_lineSetDefaultMediaDetection", 2, params }; #endif // // This func is really a no-op for us, since we don't look // for incoming calls (though we do say we support them to // make apps happy) // Prolog (&info); return (Epilog (&info, 0)); } // // ------------------------- TSPI_providerXxx funcs --------------------------- // LONG TSPIAPI TSPI_providerConfig( HWND hwndOwner, DWORD dwPermanentProviderID ) { // // Although this func is never called by TAPI v2.0, we export // it so that the Telephony Control Panel Applet knows that it // can configure this provider via lineConfigProvider(), // otherwise Telephon.cpl will not consider it configurable // return 0; } LONG TSPIAPI TSPI_providerGenericDialogData( ULONG_PTR dwObjectID, DWORD dwObjectType, LPVOID lpParams, DWORD dwSize ) { LONG lResult = 0; #if DBG FUNC_PARAM params[] = { { "dwObjectID", dwObjectID }, { "dwObjectType", dwObjectType }, { "lpParams", lpParams }, { "dwSize", dwSize } }; FUNC_INFO info = { "TSPI_providerGenericDialogData", 4, params }; #endif Prolog (&info); return (Epilog (&info, lResult)); } LONG TSPIAPI TSPI_providerInit( DWORD dwTSPIVersion, DWORD dwPermanentProviderID, DWORD dwLineDeviceIDBase, DWORD dwPhoneDeviceIDBase, DWORD_PTR dwNumLines, DWORD_PTR dwNumPhones, ASYNC_COMPLETION lpfnCompletionProc, LPDWORD lpdwTSPIOptions ) { LONG lResult = 0; #if DBG FUNC_PARAM params[] = { { "dwTSPIVersion", dwTSPIVersion }, { gszdwPermanentProviderID, dwPermanentProviderID }, { "dwLineDeviceIDBase", dwLineDeviceIDBase }, { "dwPhoneDeviceIDBase", dwPhoneDeviceIDBase }, { "dwNumLines", dwNumLines }, { "dwNumPhones", dwNumPhones }, { "lpfnCompletionProc", lpfnCompletionProc } }; FUNC_INFO info = { "TSPI_providerInit", 7, params }; #endif Prolog (&info); gdwLineDeviceIDBase = dwLineDeviceIDBase; gpfnCompletionProc = lpfnCompletionProc; *lpdwTSPIOptions = LINETSPIOPTION_NONREENTRANT; return (Epilog (&info, lResult)); } LONG TSPIAPI TSPI_providerInstall( HWND hwndOwner, DWORD dwPermanentProviderID ) { // // Although this func is never called by TAPI v2.0, we export // it so that the Telephony Control Panel Applet knows that it // can add this provider via lineAddProvider(), otherwise // Telephon.cpl will not consider it installable // // return 0; } LONG TSPIAPI TSPI_providerRemove( HWND hwndOwner, DWORD dwPermanentProviderID ) { // // Although this func is never called by TAPI v2.0, we export // it so that the Telephony Control Panel Applet knows that it // can remove this provider via lineRemoveProvider(), otherwise // Telephon.cpl will not consider it removable // return 0; } LONG TSPIAPI TSPI_providerShutdown( DWORD dwTSPIVersion, DWORD dwPermanentProviderID ) { LONG lResult = 0; #if DBG FUNC_PARAM params[] = { { "dwTSPIVersion", dwTSPIVersion }, { gszdwPermanentProviderID, dwPermanentProviderID } }; FUNC_INFO info = { "TSPI_providerShutdown", 2, params }; #endif Prolog (&info); return (Epilog (&info, lResult)); } LONG TSPIAPI TSPI_providerEnumDevices( DWORD dwPermanentProviderID, LPDWORD lpdwNumLines, LPDWORD lpdwNumPhones, HPROVIDER hProvider, LINEEVENT lpfnLineCreateProc, PHONEEVENT lpfnPhoneCreateProc ) { HKEY hKey; DWORD dwNumLines, dwDataType, dwDataSize; // // Retrieve the number of devices we're // configured for from our registry section // if (ERROR_SUCCESS != RegOpenKeyEx( HKEY_LOCAL_MACHINE, gszAtspKey, 0, KEY_ALL_ACCESS, &hKey )) { return LINEERR_OPERATIONFAILED; } dwDataSize = sizeof(dwNumLines); dwNumLines = 0; RegQueryValueEx( hKey, gszNumLines, 0, &dwDataType, (LPBYTE) &dwNumLines, &dwDataSize ); RegCloseKey (hKey); *lpdwNumLines = dwNumLines; *lpdwNumPhones = 0; return 0; } LONG TSPIAPI TSPI_providerUIIdentify( LPWSTR lpszUIDLLName ) { LONG lResult = 0; #if DBG FUNC_PARAM params[] = { { "lpsUIDLLName", lpszUIDLLName } }; FUNC_INFO info = { "TSPI_providerUIIdentify", 1, params }; #endif Prolog (&info); My_lstrcpyW(lpszUIDLLName, L"atsp32.tsp"); return (Epilog (&info, lResult)); } // // ---------------------------- TUISPI_xxx funcs ------------------------------ // LONG TSPIAPI TUISPI_lineConfigDialog( TUISPIDLLCALLBACK lpfnUIDLLCallback, DWORD dwDeviceID, HWND hwndOwner, LPCWSTR lpszDeviceClass ) { char szDeviceClass[128]; LONG lResult = 0; #if DBG FUNC_PARAM params[] = { { "lpfnUIDLLCallback", lpfnUIDLLCallback }, { gszdwDeviceID, dwDeviceID }, { gszhwndOwner, hwndOwner }, { "lpszDeviceClass", szDeviceClass } }; FUNC_INFO info = { "TUISPI_lineConfigDialog", 4, params }; #endif if (lpszDeviceClass) { WideCharToMultiByte( CP_ACP, 0, lpszDeviceClass, -1, (LPSTR) szDeviceClass, 128, NULL, NULL ); } Prolog (&info); DialogBoxParam( ghInst, MAKEINTRESOURCE(IDD_DIALOG1), hwndOwner, (DLGPROC) ConfigDlgProc, 0 ); return (Epilog (&info, lResult)); } LONG TSPIAPI TUISPI_providerConfig( TUISPIDLLCALLBACK lpfnUIDLLCallback, HWND hwndOwner, DWORD dwPermanentProviderID ) { LONG lResult = 0; #if DBG FUNC_PARAM params[] = { { "lpfnUIDLLCallback", lpfnUIDLLCallback }, { gszhwndOwner, hwndOwner }, { gszdwPermanentProviderID, dwPermanentProviderID } }; FUNC_INFO info = { "TUISPI_providerConfig", 3, params }; #endif Prolog (&info); DialogBoxParam( ghInst, MAKEINTRESOURCE(IDD_DIALOG1), hwndOwner, (DLGPROC) ConfigDlgProc, 0 ); return (Epilog (&info, lResult)); } LONG TSPIAPI TUISPI_providerInstall( TUISPIDLLCALLBACK lpfnUIDLLCallback, HWND hwndOwner, DWORD dwPermanentProviderID ) { LONG lResult; if ((lResult = ProviderInstall ("atsp32.tsp", TRUE)) == 0) { DialogBoxParam( ghInst, MAKEINTRESOURCE(IDD_DIALOG1), hwndOwner, (DLGPROC) ConfigDlgProc, 0 ); } return lResult; } LONG TSPIAPI TUISPI_providerRemove( TUISPIDLLCALLBACK lpfnUIDLLCallback, HWND hwndOwner, DWORD dwPermanentProviderID ) { HKEY hKey; char szSoftwareMsft[] = "Software\\Microsoft", szATSP[] = "ATSP"; LONG lResult; // // Clean up our registry section // lResult = RegOpenKeyExA( HKEY_LOCAL_MACHINE, szSoftwareMsft, 0, KEY_ALL_ACCESS, &hKey ); if (ERROR_SUCCESS != lResult) return 0; RegDeleteKeyA (hKey, szATSP); RegCloseKey (hKey); return 0; } #pragma warning (default:4047) // // ---------------------- Misc private support routines ----------------------- // LPWSTR PASCAL My_lstrcpyW( WCHAR *pString1, WCHAR *pString2 ) { WCHAR *p = pString1; for (; (*p = *pString2); p++, pString2++); return pString1; } void PASCAL EnableChildren( HWND hwnd, BOOL bEnable ) { int i; static int aiControlIDs[] = { IDC_DEVICES, IDC_NAME, IDC_PORT, IDC_COMMANDS, IDC_REMOVE, 0 }; for (i = 0; aiControlIDs[i]; i++) { EnableWindow (GetDlgItem (hwnd, aiControlIDs[i]), bEnable); } } void PASCAL SelectDevice( HWND hwnd, LRESULT lDevice ) { SendDlgItemMessage (hwnd, IDC_DEVICES, LB_SETCURSEL, lDevice, 0); PostMessage(hwnd, WM_COMMAND, IDC_DEVICES | (LBN_SELCHANGE << 16), 0); } BOOL CALLBACK ConfigDlgProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) { static HKEY hAtspKey; DWORD dwDataSize; DWORD dwDataType; switch (msg) { case WM_INITDIALOG: { char *pBuf; DWORD i, iNumLines; // // Create or open our configuration key in the registry. If the // create fails it may well be that the current user does not // have write access to this portion of the registry, so we'll // just show a "read only" dialog and not allow user to make any // changes // { LONG lResult; DWORD dwDisposition; if ((lResult = RegCreateKeyEx( HKEY_LOCAL_MACHINE, gszAtspKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, (LPSECURITY_ATTRIBUTES) NULL, &hAtspKey, &dwDisposition )) != ERROR_SUCCESS) { DBGOUT(( 3, "RegCreateKeyEx(%s,ALL_ACCESS) failed, err=%d", gszAtspKey, lResult )); if ((lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, gszAtspKey, 0, KEY_QUERY_VALUE, &hAtspKey )) != ERROR_SUCCESS) { DBGOUT(( 3, "RegOpenKeyEx(%s,ALL_ACCESS) failed, err=%d", gszAtspKey, lResult )); EndDialog (hwnd, 0); return FALSE; } { int i; static int aiControlIDs[] = { IDC_NAME, IDC_PORT, IDC_COMMANDS, IDC_ADD, IDC_REMOVE, IDOK, 0 }; for (i = 0; aiControlIDs[i]; i++) { EnableWindow( GetDlgItem (hwnd, aiControlIDs[i]), FALSE ); } } } } // // Retrieve our configuration info from the registry // dwDataSize = sizeof(iNumLines); iNumLines = 0; RegQueryValueEx( hAtspKey, gszNumLines, 0, &dwDataType, (LPBYTE) &iNumLines, &dwDataSize ); SendDlgItemMessage( hwnd, IDC_NAME, EM_LIMITTEXT, MAX_DEV_NAME_LENGTH, 0 ); SendDlgItemMessage( hwnd, IDC_COMMANDS, EM_LIMITTEXT, MAX_DEV_NAME_LENGTH, 0 ); pBuf = DrvAlloc (256); for (i = 0; i < iNumLines; i++) { char *p, *p2, szLineN[8]; PDRVLINECONFIG pLineConfig = DrvAlloc (sizeof(DRVLINECONFIG)); LONG lResult; wsprintf (szLineN, "Line%d", i); dwDataSize = 256; lResult = RegQueryValueEx( hAtspKey, szLineN, 0, &dwDataType, (LPBYTE) pBuf, &dwDataSize ); // // If there was a problem, use the default config // if (0 != lResult) { lstrcpy (pBuf, gszDefLineConfigParams); } for (p = pBuf; *p != ','; p++); *p = 0; SendDlgItemMessage( hwnd, IDC_DEVICES, LB_ADDSTRING, 0, (LPARAM) pBuf ); SendDlgItemMessage( hwnd, IDC_DEVICES, LB_SETITEMDATA, i, (LPARAM) pLineConfig ); p++; for (p2 = p; *p2 != ','; p2++); *p2 = 0; lstrcpy (pLineConfig->szPort, p); p = p2 + 1; lstrcpy (pLineConfig->szCommands, p); } DrvFree (pBuf); // // Fill up the various controls with configuration options // { static char *aszPorts[] = { "COM1","COM2","COM3",NULL }; for (i = 0; aszPorts[i]; i++) { SendDlgItemMessage( hwnd, IDC_PORT, LB_ADDSTRING, 0, (LPARAM) aszPorts[i] ); } } if (iNumLines == 0) { EnableChildren (hwnd, FALSE); } else { SelectDevice (hwnd, 0); } break; } case WM_COMMAND: { LRESULT lSelection; PDRVLINECONFIG pLineConfig; lSelection = SendDlgItemMessage( hwnd, IDC_DEVICES, LB_GETCURSEL, 0, 0 ); pLineConfig = (PDRVLINECONFIG) SendDlgItemMessage( hwnd, IDC_DEVICES, LB_GETITEMDATA, (WPARAM) lSelection, 0 ); switch (LOWORD((DWORD)wParam)) { case IDC_DEVICES: if (HIWORD(wParam) == LBN_SELCHANGE) { char buf[MAX_DEV_NAME_LENGTH + 1]; SendDlgItemMessage( hwnd, IDC_DEVICES, LB_GETTEXT, lSelection, (LPARAM) buf ); SetDlgItemText (hwnd, IDC_NAME, buf); SendDlgItemMessage( hwnd, IDC_PORT, LB_SELECTSTRING, (WPARAM) -1, (LPARAM) pLineConfig->szPort ); SetDlgItemText (hwnd, IDC_COMMANDS, pLineConfig->szCommands); } break; case IDC_NAME: if ((HIWORD(wParam) == EN_CHANGE) && (lSelection != LB_ERR)) { char buf[MAX_DEV_NAME_LENGTH + 1]; GetDlgItemText (hwnd, IDC_NAME, buf, MAX_DEV_NAME_LENGTH); SendDlgItemMessage( hwnd, IDC_DEVICES, LB_DELETESTRING, lSelection, 0 ); SendDlgItemMessage( hwnd, IDC_DEVICES, LB_INSERTSTRING, lSelection, (LPARAM) buf ); SendDlgItemMessage( hwnd, IDC_DEVICES, LB_SETCURSEL, lSelection, 0 ); SendDlgItemMessage( hwnd, IDC_DEVICES, LB_SETITEMDATA, lSelection, (LPARAM) pLineConfig ); } break; case IDC_PORT: if (HIWORD(wParam) == LBN_SELCHANGE) { lSelection = SendDlgItemMessage( hwnd, IDC_PORT, LB_GETCURSEL, 0, 0 ); SendDlgItemMessage( hwnd, IDC_PORT, LB_GETTEXT, lSelection, (LPARAM) pLineConfig->szPort ); } break; case IDC_COMMANDS: if ((HIWORD(wParam) == EN_CHANGE) && (lSelection != LB_ERR)) { GetDlgItemText( hwnd, IDC_COMMANDS, pLineConfig->szCommands, 63 ); } break; case IDC_ADD: { LRESULT lNumLines, l = 2; char szLineName[32]; PDRVLINECONFIG pLineConfig = DrvAlloc (sizeof(DRVLINECONFIG)); lNumLines = SendDlgItemMessage( hwnd, IDC_DEVICES, LB_GETCOUNT, 0, 0 ); lstrcpy (pLineConfig->szPort, "COM1"); lstrcpy (szLineName, "my new line"); find_unique_line_name: if (SendDlgItemMessage( hwnd, IDC_DEVICES, LB_FINDSTRING, (WPARAM) -1, (LPARAM) szLineName ) != LB_ERR) { wsprintf (szLineName, "my new line%d", l++); goto find_unique_line_name; } SendDlgItemMessage( hwnd, IDC_DEVICES, LB_ADDSTRING, 0, (LPARAM) szLineName ); SendDlgItemMessage( hwnd, IDC_DEVICES, LB_SETITEMDATA, lNumLines, (LPARAM) pLineConfig ); EnableChildren (hwnd, TRUE); SelectDevice (hwnd, lNumLines); SetFocus (GetDlgItem (hwnd, IDC_NAME)); SendDlgItemMessage( hwnd, IDC_NAME, EM_SETSEL, 0, (LPARAM) -1 ); break; } case IDC_REMOVE: { LRESULT lNumLines; DrvFree (pLineConfig); lNumLines = SendDlgItemMessage( hwnd, IDC_DEVICES, LB_DELETESTRING, lSelection, 0 ); if (lNumLines == 0) { SetDlgItemText (hwnd, IDC_NAME, ""); SetDlgItemText (hwnd, IDC_COMMANDS, ""); EnableChildren (hwnd, FALSE); } else { SelectDevice (hwnd, 0); } break; } case IDOK: { char *pBuf; LRESULT l, lNumLines; // // Update the num lines & num phones values // pBuf = DrvAlloc (256); lNumLines = SendDlgItemMessage( hwnd, IDC_DEVICES, LB_GETCOUNT, 0, 0 ); RegSetValueEx( hAtspKey, gszNumLines, 0, REG_DWORD, (LPBYTE) &lNumLines, sizeof(DWORD) ); // // For each installed device save it's config info // for (l = 0; l < lNumLines; l++) { char szLineN[8]; PDRVLINECONFIG pLineConfig; SendDlgItemMessage( hwnd, IDC_DEVICES, LB_GETTEXT, l, (LPARAM) pBuf ); pLineConfig = (PDRVLINECONFIG) SendDlgItemMessage( hwnd, IDC_DEVICES, LB_GETITEMDATA, l, 0 ); wsprintf( pBuf + strlen (pBuf), ",%s,%s", pLineConfig->szPort, pLineConfig->szCommands ); wsprintf (szLineN, "Line%d", l); RegSetValueEx( hAtspKey, szLineN, 0, REG_SZ, (LPBYTE) pBuf, lstrlen (pBuf) + 1 ); DrvFree (pLineConfig); } DrvFree (pBuf); // fall thru to EndDialog... } case IDCANCEL: RegCloseKey (hAtspKey); EndDialog (hwnd, 0); break; } // switch (LOWORD((DWORD)wParam)) break; } } // switch (msg) return FALSE; } LPVOID PASCAL DrvAlloc( DWORD dwSize ) { return (LocalAlloc (LPTR, dwSize)); } VOID PASCAL DrvFree( LPVOID lp ) { LocalFree (lp); } void PASCAL SetCallState( PDRVLINE pLine, DWORD dwCallState, DWORD dwCallStateMode ) { if (dwCallState != pLine->dwCallState) { pLine->dwCallState = dwCallState; pLine->dwCallStateMode = dwCallStateMode; (*pLine->pfnEventProc)( pLine->htLine, pLine->htCall, LINE_CALLSTATE, dwCallState, dwCallStateMode, pLine->dwMediaMode ); } } #if DBG void PASCAL Prolog( PFUNC_INFO pInfo ) { DWORD i; DBGOUT((3, "%s: enter", pInfo->lpszFuncName)); for (i = 0; i < pInfo->dwNumParams; i++) { if (pInfo->aParams[i].dwVal && pInfo->aParams[i].lpszVal[3] == 'z') // lpszVal = "lpsz..." { DBGOUT(( 3, "%s%s=x%lx, '%s'", gszTab, pInfo->aParams[i].lpszVal, pInfo->aParams[i].dwVal, pInfo->aParams[i].dwVal )); } else { DBGOUT(( 3, "%s%s=x%lx", gszTab, pInfo->aParams[i].lpszVal, pInfo->aParams[i].dwVal )); } } } LONG PASCAL Epilog( PFUNC_INFO pInfo, LONG lResult ) { DBGOUT((3, "%s: returning x%x", pInfo->lpszFuncName, lResult)); return lResult; } void CDECL DebugOutput( DWORD dwDbgLevel, LPCSTR lpszFormat, ... ) { if (dwDbgLevel <= gdwDebugLevel) { char buf[128] = "ATSP32: "; va_list ap; va_start(ap, lpszFormat); wvsprintf (&buf[8], lpszFormat, ap); lstrcat (buf, "\n"); OutputDebugString (buf); va_end(ap); } } #endif LONG PASCAL ProviderInstall( char *pszProviderName, BOOL bNoMultipleInstance ) { LONG lResult; // // If only one installation instance of this provider is // allowed then we want to check the provider list to see // if the provider is already installed // if (bNoMultipleInstance) { LONG (WINAPI *pfnGetProviderList)(); DWORD dwTotalSize, i; HINSTANCE hTapi32; LPLINEPROVIDERLIST pProviderList; LPLINEPROVIDERENTRY pProviderEntry; // // Load Tapi32.dll & get a pointer to the lineGetProviderList // func. We don't want to statically link because this module // plays the part of both core SP & UI DLL, and we don't want // to incur the performance hit of automatically loading // Tapi32.dll when running as a core SP within Tapisrv.exe's // context. // if (!(hTapi32 = LoadLibrary ("tapi32.dll"))) { DBGOUT(( 1, "LoadLibrary(tapi32.dll) failed, err=%d", GetLastError() )); lResult = LINEERR_OPERATIONFAILED; goto ProviderInstall_return; } if (!((FARPROC) pfnGetProviderList = GetProcAddress( hTapi32, (LPCSTR) "lineGetProviderList" ))) { DBGOUT(( 1, "GetProcAddr(lineGetProviderList) failed, err=%d", GetLastError() )); lResult = LINEERR_OPERATIONFAILED; goto ProviderInstall_unloadTapi32; } // // Loop until we get the full provider list // dwTotalSize = sizeof (LINEPROVIDERLIST); goto ProviderInstall_allocProviderList; ProviderInstall_getProviderList: if ((lResult = (*pfnGetProviderList)(0x00020000, pProviderList)) != 0) { goto ProviderInstall_freeProviderList; } if (pProviderList->dwNeededSize > pProviderList->dwTotalSize) { dwTotalSize = pProviderList->dwNeededSize; LocalFree (pProviderList); ProviderInstall_allocProviderList: if (!(pProviderList = LocalAlloc (LPTR, dwTotalSize))) { lResult = LINEERR_NOMEM; goto ProviderInstall_unloadTapi32; } pProviderList->dwTotalSize = dwTotalSize; goto ProviderInstall_getProviderList; } // // Inspect the provider list entries to see if this provider // is already installed // pProviderEntry = (LPLINEPROVIDERENTRY) (((LPBYTE) pProviderList) + pProviderList->dwProviderListOffset); for (i = 0; i < pProviderList->dwNumProviders; i++) { char *pszInstalledProviderName = ((char *) pProviderList) + pProviderEntry->dwProviderFilenameOffset, *p; // // Make sure pszInstalledProviderName points at // and not \filename by walking backeards thru the // string searching for last '\\' // p = pszInstalledProviderName + lstrlen (pszInstalledProviderName) - 1; for (; *p != '\\' && p != pszInstalledProviderName; p--); pszInstalledProviderName = (p == pszInstalledProviderName ? p : p + 1); if (lstrcmpiA (pszInstalledProviderName, pszProviderName) == 0) { lResult = LINEERR_NOMULTIPLEINSTANCE; goto ProviderInstall_freeProviderList; } pProviderEntry++; } // // If here then the provider isn't currently installed, // so do whatever configuration stuff is necessary and // indicate SUCCESS // lResult = 0; ProviderInstall_freeProviderList: LocalFree (pProviderList); ProviderInstall_unloadTapi32: FreeLibrary (hTapi32); } else { // // Do whatever configuration stuff is necessary and return SUCCESS // lResult = 0; } ProviderInstall_return: return lResult; } void PASCAL DropActiveCall( PDRVLINE pLine ) { if (pLine->hComm) { DWORD dwNumBytes, dwError; OVERLAPPED overlapped; pLine->bDropInProgress = TRUE; SetEvent (pLine->Overlapped.hEvent); ZeroMemory (&overlapped, sizeof (OVERLAPPED)); overlapped.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); if (pLine->dwMediaMode != LINEMEDIAMODE_INTERACTIVEVOICE) { if (!WriteFile( pLine->hComm, "+++\r", 4, &dwNumBytes, &overlapped )) { if ((dwError = GetLastError()) == ERROR_IO_PENDING) { GetOverlappedResult( pLine->hComm, &overlapped, &dwNumBytes, TRUE ); ResetEvent (overlapped.hEvent); } else { } } } if (!WriteFile (pLine->hComm, "ATH\r", 4, &dwNumBytes, &overlapped)) { if ((dwError = GetLastError()) == ERROR_IO_PENDING) { GetOverlappedResult( pLine->hComm, &overlapped, &dwNumBytes, TRUE ); } else { } } CloseHandle (overlapped.hEvent); CloseHandle (pLine->hComm); pLine->hComm = NULL; } }