/*========================================================================== * * Copyright (C) 1996-1997 Microsoft Corporation. All Rights Reserved. * * File: dial.c * Content: Wrappers for TAPI routines *@@BEGIN_MSINTERNAL * History: * Date By Reason * ==== == ====== * 6/10/96 kipo created it * 6/22/96 kipo close com port when disconnected; allow checking for * valid TAPI lines during NewComPort(). * 7/08/96 kipo added support for new dialogs * 8/10/96 kipo added support for dialing location * 1/06/97 kipo updated for objects * 1/24/97 kipo bug #5400: Compaq Presario was overwriting the dev caps * buffer, causing a crash. Fixed to allocated a larger * buffer with some slop as a workaround. * 3/04/97 kipo close com port handle when deallocating call; use string * table for modem strings; updated debug output. * 3/24/97 kipo added support for specifying which modem to use * 4/08/97 kipo added support for separate modem and serial baud rates * 5/07/97 kipo added support for modem choice list * 5/23/97 kipo added support return status codes * 4/21/98 a-peterz #22920 Handle LINE_CLOSE message * 5/07/98 a-peterz #15251 Track call errors in DPDIAL * 10/13/99 johnkan #413516 - Mismatch between modem dialog selection and TAPI device ID *@@END_MSINTERNAL ***************************************************************************/ #include #include #include "dputils.h" #include "macros.h" #include "dial.h" void FAR PASCAL LineCallBackProc(DWORD hDevice, DWORD dwMessage, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2, DWORD_PTR dwParam3); void ProcessConnectedState(LPDPDIAL globals, HCALL hCall, DWORD dwCallStateDetail, DWORD dwCallPrivilege); void ProcessDisconnectedState(LPDPDIAL globals, HCALL hCall, DWORD dwCallStateDetail, DWORD dwCallPrivilege); void ProcessIdleState(LPDPDIAL globals, HCALL hCall, DWORD dwCallStateDetail, DWORD dwCallPrivilege); void ProcessOfferingState(LPDPDIAL globals, HCALL hCall, DWORD dwCallPrivilege); void ProcessReplyMessage(LPDPDIAL globals, DWORD asyncID, LINERESULT lResult); LINERESULT dialGetDevCaps(LPDPDIAL globals, DWORD dwLine, DWORD dwAPIVersion, LPLINEDEVCAPS *lpDevCapsRet); LINERESULT dialGetCommHandle(LPDPDIAL globals); LINERESULT dialCloseCommHandle(LPDPDIAL globals); LINERESULT dialTranslateAddress(LPDPDIAL globals, DWORD dwDeviceID, DWORD dwAPIVersion, LPCSTR lpszDialAddress, LPLINETRANSLATEOUTPUT *lpLineTranslateOutputRet); LPSTR GetLineErrStr(LONG err); LPSTR GetCallStateStr(DWORD callState); LPSTR GetLineMsgStr(DWORD msg); #ifdef DEBUG extern LONG lineError(LONG err, LPSTR modName, DWORD lineNum); #define LINEERROR(err) (lineError(err, DPF_MODNAME, __LINE__)) #else #define LINEERROR(err) (err) #endif /* dial initialize */ #undef DPF_MODNAME #define DPF_MODNAME "dialInitialize" LINERESULT dialInitialize(HINSTANCE hInst, LPTSTR szAppName, LPDPCOMPORT lpComPort, LPDPDIAL *storage) { LPDPDIAL globals; LINERESULT lResult; /* Stores return code from TAPI calls */ // create globals globals =(LPDPDIAL) GlobalAllocPtr(GHND, sizeof(DPDIAL)); FAILWITHACTION(globals == NULL, lResult = LINEERR_NOMEM, Failure); DPF(3, "lineInitialize"); DPF(3, "> hInstance: %08X", hInst); DPF(3, "> szAppName: %s", szAppName); // init the line lResult = lineInitialize(&globals->hLineApp, hInst, LineCallBackProc, szAppName, &globals->dwNumLines); FAILIF(LINEERROR(lResult), Failure); DPF(3, "< hLineApp: %08X", globals->hLineApp); DPF(3, "< dwNumLines: %d", globals->dwNumLines); // no lines available FAILWITHACTION(globals->dwNumLines == 0, lResult = LINEERR_NODEVICE, Failure); // store pointer to com port object globals->lpComPort = lpComPort; *storage = globals; return (SUCCESS); Failure: dialShutdown(globals); return (lResult); } /* dial shutdown */ #undef DPF_MODNAME #define DPF_MODNAME "dialShutdown" LINERESULT dialShutdown(LPDPDIAL globals) { LINERESULT lResult; if (globals == NULL) return (SUCCESS); if (globals->hLineApp) { dialDropCall(globals); dialDeallocCall(globals); dialLineClose(globals); DPF(3, "lineShutdown"); DPF(3, "> hLineApp: %08X", globals->hLineApp); lResult = lineShutdown(globals->hLineApp); LINEERROR(lResult); } GlobalFreePtr(globals); return (SUCCESS); } /* dialLineOpen - wrapper for lineOpen */ #undef DPF_MODNAME #define DPF_MODNAME "dialLineOpen" LINERESULT dialLineOpen(LPDPDIAL globals, DWORD dwLine) { LINEEXTENSIONID lineExtensionID; // Will be set to 0 to indicate no known extensions LPLINEDEVCAPS lpLineDevCaps = NULL; LINERESULT lResult; // fail if line is already open FAILWITHACTION(globals->hLine != 0, lResult = LINEERR_INVALLINEHANDLE, Failure); /* negotiate API version for each line */ lResult = lineNegotiateAPIVersion(globals->hLineApp, dwLine, TAPIVERSION, TAPIVERSION, &globals->dwAPIVersion, &lineExtensionID); FAILIF(LINEERROR(lResult), Failure); lResult = dialGetDevCaps(globals, dwLine, globals->dwAPIVersion, &lpLineDevCaps); FAILIF(LINEERROR(lResult), Failure); /* check for supported media mode. If not datamodem, continue to next line */ FAILWITHACTION(!(lpLineDevCaps->dwMediaModes & LINEMEDIAMODE_DATAMODEM), lResult = LINEERR_NODEVICE, Failure); DPF(3, "lineOpen"); DPF(3, "> hLineApp: %08X", globals->hLineApp); DPF(3, "> dwDeviceID: %d", dwLine); // reset error tracking globals->dwCallError = CALL_OK; /* open the line that supports data modems */ lResult = lineOpen( globals->hLineApp, dwLine, &globals->hLine, globals->dwAPIVersion, 0L, (DWORD_PTR) globals, LINECALLPRIVILEGE_OWNER, LINEMEDIAMODE_DATAMODEM, NULL); FAILIF(LINEERROR(lResult), Failure); DPF(3, "< hLine: %08X", globals->hLine); /* if we are here then we found a compatible line */ globals->dwLineID = dwLine; globals->dwCallState = LINECALLSTATE_IDLE; // line is now idle and ready to make/receive calls lResult = SUCCESS; Failure: if (lpLineDevCaps) GlobalFreePtr(lpLineDevCaps); return (lResult); } /* dialLineClose - wrapper for lineClose */ #undef DPF_MODNAME #define DPF_MODNAME "dialLineClose" LINERESULT dialLineClose(LPDPDIAL globals) { LINERESULT lResult; // fail if line is already closed FAILWITHACTION(globals->hLine == 0, lResult = LINEERR_INVALLINEHANDLE, Failure); DPF(3, "lineClose"); DPF(3, "> hLine: %08X", globals->hLine); lResult = lineClose(globals->hLine); LINEERROR(lResult); globals->hLine = 0; Failure: return (lResult); } /* dialMakeCall - wrapper for lineMakeCall */ #undef DPF_MODNAME #define DPF_MODNAME "dialMakeCall" LINERESULT dialMakeCall(LPDPDIAL globals, LPTSTR szDestination) { LINECALLPARAMS callparams; LINERESULT lResult; // fail if line not open or if call is already open FAILWITHACTION(globals->hLine == 0, lResult = LINEERR_INVALLINEHANDLE, Failure); FAILWITHACTION(globals->hCall != 0, lResult = LINEERR_INVALCALLHANDLE, Failure); // set call parameters ZeroMemory(&callparams, sizeof(LINECALLPARAMS)); callparams.dwBearerMode = LINEBEARERMODE_VOICE; callparams.dwMediaMode = LINEMEDIAMODE_DATAMODEM; callparams.dwTotalSize = sizeof(LINECALLPARAMS); DPF(3, "lineMakeCall"); DPF(3, "> hLine: %08X", globals->hLine); DPF(3, "> szDestAddr: \"%s\"", szDestination); lResult = lineMakeCall(globals->hLine, &globals->hCall, szDestination, 0, &callparams); // lResult will be > 0 if call is asynchronous FAILWITHACTION(lResult < 0, LINEERROR(lResult), Failure); FAILMSG(lResult == 0); DPF(3, "< hCall: %08X", globals->hCall); DPF(3, "< dwAsyncID: %d", lResult); globals->dwAsyncID = lResult; // store async ID lResult = SUCCESS; Failure: return (lResult); } /* dialDropCall - wrapper for lineDrop */ #undef DPF_MODNAME #define DPF_MODNAME "dialDropCall" LINERESULT dialDropCall(LPDPDIAL globals) { MSG msg; DWORD dwStopTicks; LINERESULT lResult; // fail if line not open or if call not open FAILWITHACTION(globals->hLine == 0, lResult = LINEERR_INVALLINEHANDLE, Failure); FAILWITHACTION(globals->hCall == 0, lResult = LINEERR_INVALCALLHANDLE, Failure); DPF(3, "lineDrop"); DPF(3, "> hCall: %08X", globals->hCall); lResult = lineDrop(globals->hCall, NULL, 0); // lResult will be > 0 if call is asynchronous FAILWITHACTION(lResult < 0, LINEERROR(lResult), Failure); FAILMSG(lResult == 0); DPF(3, "< dwAsyncID: %d", lResult); globals->dwAsyncID = lResult; // store async ID // wait for call to get dropped dwStopTicks = GetTickCount() + LINEDROPTIMEOUT; while (GetTickCount() < dwStopTicks) { // see if reply has occured and we are idle if ((globals->dwAsyncID == 0) && (globals->dwCallState == LINECALLSTATE_IDLE)) { break; } // give TAPI a chance to call our callback if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } } lResult = SUCCESS; Failure: return (lResult); } /* dialDeallocCall - wrapper for lineDeallocCall */ #undef DPF_MODNAME #define DPF_MODNAME "dialDeallocCall" LINERESULT dialDeallocCall(LPDPDIAL globals) { LINERESULT lResult; // fail if line not open or if call not open FAILWITHACTION(globals->hLine == 0, lResult = LINEERR_INVALLINEHANDLE, Failure); FAILWITHACTION(globals->hCall == 0, lResult = LINEERR_INVALCALLHANDLE, Failure); // close the com port dialCloseCommHandle(globals); DPF(3, "lineDeallocateCall"); DPF(3, "> hCall: %08X", globals->hCall); lResult = lineDeallocateCall(globals->hCall); LINEERROR(lResult); globals->hCall = 0; Failure: return (lResult); } /* dialIsConnected- returns TRUE if call is connected */ #undef DPF_MODNAME #define DPF_MODNAME "dialIsConnected" BOOL dialIsConnected(LPDPDIAL globals) { // connected if we have a call handle and the state is connected if ((globals->hCall) && (globals->dwCallState == LINECALLSTATE_CONNECTED)) return (TRUE); else return (FALSE); } /* callback function */ #undef DPF_MODNAME #define DPF_MODNAME "LineCallBackProc" void FAR PASCAL LineCallBackProc(DWORD hDevice, DWORD dwMessage, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2, DWORD_PTR dwParam3) { LPDPDIAL globals = (LPDPDIAL) dwInstance; DPF(3, "Line message: %s", GetLineMsgStr(dwMessage)); switch (dwMessage) { case LINE_LINEDEVSTATE: break; case LINE_CALLSTATE: globals->dwCallState = dwParam1; DPF(3, " call state: %s", GetCallStateStr((DWORD)globals->dwCallState)); switch (globals->dwCallState) { case LINECALLSTATE_OFFERING: ProcessOfferingState(globals, (HCALL) hDevice, (DWORD)dwParam3); break; case LINECALLSTATE_CONNECTED: ProcessConnectedState(globals, (HCALL) hDevice, (DWORD)dwParam2, (DWORD)dwParam3); break; case LINECALLSTATE_DISCONNECTED: ProcessDisconnectedState(globals, (HCALL) hDevice, (DWORD)dwParam2, (DWORD)dwParam3); break; case LINECALLSTATE_IDLE: ProcessIdleState(globals, (HCALL) hDevice, (DWORD)dwParam2, (DWORD)dwParam3); break; case LINECALLSTATE_BUSY: break; } break; case LINE_REPLY: ProcessReplyMessage(globals, (DWORD)dwParam1, (LINERESULT) dwParam2); break; /* other messages that can be processed */ case LINE_CLOSE: // the line has shut itself down globals->hLine = 0; globals->dwCallError = CALL_CLOSED; break; case LINE_ADDRESSSTATE: break; case LINE_CALLINFO: break; case LINE_DEVSPECIFIC: break; case LINE_DEVSPECIFICFEATURE: break; case LINE_GATHERDIGITS: break; case LINE_GENERATE: break; case LINE_MONITORDIGITS: break; case LINE_MONITORMEDIA: break; case LINE_MONITORTONE: break; } /* switch */ } /* LineCallBackProc */ /* ProcessOfferingState - handler for LINECALLSTATE_OFFERING state */ #undef DPF_MODNAME #define DPF_MODNAME "ProcessOfferingState" void ProcessOfferingState(LPDPDIAL globals, HCALL hCall, DWORD dwCallPrivilege) { LINERESULT lResult; DDASSERT(hCall); DDASSERT(globals->hCall == 0); DDASSERT(globals->dwAsyncID == 0); DPF(3, " hCall: %08X", hCall); DPF(3, " privilege: %08X", (DWORD)dwCallPrivilege); // fail if we don't own the call FAILIF(dwCallPrivilege != LINECALLPRIVILEGE_OWNER, Failure); // answer the call lResult = lineAnswer(hCall, NULL, 0); // lResult will be > 0 if call is asynchronous FAILWITHACTION(lResult < 0, LINEERROR(lResult), Failure); FAILMSG(lResult == 0); globals->hCall = hCall; // store call handle globals->dwAsyncID = lResult; // store async ID Failure: return; } /* ProcessConnectedState - handler for LINECALLSTATE_CONNECTED state */ #undef DPF_MODNAME #define DPF_MODNAME "ProcessConnectedState" void ProcessConnectedState(LPDPDIAL globals, HCALL hCall, DWORD dwCallStateDetail, DWORD dwCallPrivilege) { LINERESULT lResult; HRESULT hr; DDASSERT(hCall); DDASSERT(globals->hCall); DDASSERT(globals->hCall == hCall); DPF(3, " hCall: %08X", hCall); DPF(3, " privilege: %08X", dwCallPrivilege); DPF(3, " detail: %08X", dwCallStateDetail); // get the id of the COM device connected to the modem // NOTE: once we get the handle, it is our responsibility to close it lResult = dialGetCommHandle(globals); FAILIF(LINEERROR(lResult), Failure); DPF(3, " hComPort: %08X", globals->hComm); // setup com port hr = globals->lpComPort->Setup(globals->lpComPort, globals->hComm); FAILIF(FAILED(hr), Failure); { DWORD dwBaudRate; lResult = dialGetBaudRate(globals, &dwBaudRate); } Failure: return; } /* ProcessDisconnectedState - handler for LINECALLSTATE_DISCONNECTED state */ #undef DPF_MODNAME #define DPF_MODNAME "ProcessDisconnectedState" void ProcessDisconnectedState(LPDPDIAL globals, HCALL hCall, DWORD dwCallStateDetail, DWORD dwCallPrivilege) { LINERESULT lResult; DDASSERT(hCall); DDASSERT(globals->hCall); DDASSERT(globals->hCall == hCall); DPF(3, " hCall: %08X", hCall); DPF(3, " privilege: %08X", dwCallPrivilege); DPF(3, " detail: %08X", dwCallStateDetail); // record error globals->dwCallError = CALL_DISCONNECTED; // shutdown com port and deallocate call handle lResult = dialDeallocCall(globals); FAILMSG(LINEERROR(lResult)); } /* ProcessIdleState - handler for LINECALLSTATE_IDLE state */ #undef DPF_MODNAME #define DPF_MODNAME "ProcessIdleState" void ProcessIdleState(LPDPDIAL globals, HCALL hCall, DWORD dwCallStateDetail, DWORD dwCallPrivilege) { DDASSERT(hCall); DPF(3, " hCall: %08X", hCall); DPF(3, " privilege: %08X", dwCallPrivilege); DPF(3, " detail: %08X", dwCallStateDetail); } /* ProcessReplyMessage - handler for LINE_REPLY message */ #undef DPF_MODNAME #define DPF_MODNAME "ProcessReplyMessage" void ProcessReplyMessage(LPDPDIAL globals, DWORD dwAsyncID, LINERESULT lResult) { DDASSERT(dwAsyncID); DDASSERT(globals->dwAsyncID); DDASSERT(globals->dwAsyncID == dwAsyncID); DPF(3, " dwAsyncID: %d", dwAsyncID); DPF(3, " error: %d", lResult); // check for an error if (LINEERROR(lResult)) globals->dwCallError = CALL_LINEERROR; // reset field so we know reply happened globals->dwAsyncID = 0; } /* dialGetDevCaps - wrapper for lineGetDevCaps */ /* Bug #5400 - My trusty Compaq Presario returns two line devices. The second device says it needs 555 bytes for dev caps, but when you give it a pointer to a 555-byte block it actually writes 559 (!) bytes into the buffer! Whoah, Bessy! This makes Windows very unhappy in strange and magical ways. The fix is to start with a very large buffer (1024 bytes?) like all the samples do and then leave some slop in subsequent reallocs, which should hopefully clean up after these messy critters. */ #define DEVCAPSINITIALSIZE 1024 // size of first alloc #define DEVCAPSSLOP 100 // extra space that loser service providers can party on #undef DPF_MODNAME #define DPF_MODNAME "dialGetDevCaps" LINERESULT dialGetDevCaps(LPDPDIAL globals, DWORD dwLine, DWORD dwAPIVersion, LPLINEDEVCAPS *lpDevCapsRet) { LPLINEDEVCAPS lpDevCaps; LINERESULT lResult; LPVOID lpTemp; // create a buffer for dev caps lpDevCaps = (LPLINEDEVCAPS) GlobalAllocPtr(GHND, DEVCAPSINITIALSIZE + DEVCAPSSLOP); FAILWITHACTION(lpDevCaps == NULL, lResult = LINEERROR(LINEERR_NOMEM), Failure); lpDevCaps->dwTotalSize = DEVCAPSINITIALSIZE; while (TRUE) { // get device caps lResult = lineGetDevCaps(globals->hLineApp, dwLine, dwAPIVersion, 0, lpDevCaps); if (lResult == SUCCESS) { // make sure there is enough space if (lpDevCaps->dwNeededSize <= lpDevCaps->dwTotalSize) break; // there is enough space, so exit } else if (lResult != LINEERR_STRUCTURETOOSMALL) { LINEERROR(lResult); goto Failure; } // reallocate buffer if not big enough */ lpTemp = GlobalReAllocPtr(lpDevCaps, lpDevCaps->dwNeededSize + DEVCAPSSLOP, 0); FAILWITHACTION(lpTemp == NULL, lResult = LINEERROR(LINEERR_NOMEM), Failure); lpDevCaps = lpTemp; lpDevCaps->dwTotalSize = lpDevCaps->dwNeededSize; } *lpDevCapsRet = lpDevCaps; return (SUCCESS); Failure: if (lpDevCaps) GlobalFreePtr(lpDevCaps); return (lResult); } /* dialGetCallInfo - wrapper for lineGetCallInfo */ #undef DPF_MODNAME #define DPF_MODNAME "dialGetCallInfo" LINERESULT dialGetCallInfo(LPDPDIAL globals, LPLINECALLINFO *lpCallInfoRet) { LPLINECALLINFO lpCallInfo; LINERESULT lResult; LPVOID lpTemp; // create a buffer for call info lpCallInfo = (LPLINECALLINFO) GlobalAllocPtr(GHND, sizeof(LINECALLINFO)); FAILWITHACTION(lpCallInfo == NULL, lResult = LINEERROR(LINEERR_NOMEM), Failure); lpCallInfo->dwTotalSize = sizeof(LINECALLINFO); while (TRUE) { // get device info lResult = lineGetCallInfo(globals->hCall, lpCallInfo); if (lResult == SUCCESS) { // make sure there is enough space if (lpCallInfo->dwNeededSize <= lpCallInfo->dwTotalSize) break; // there is enough space, so exit } else if (lResult != LINEERR_STRUCTURETOOSMALL) { LINEERROR(lResult); goto Failure; } // reallocate buffer if not big enough */ lpTemp = GlobalReAllocPtr(lpCallInfo, lpCallInfo->dwNeededSize, 0); FAILWITHACTION(lpTemp == NULL, lResult = LINEERROR(LINEERR_NOMEM), Failure); lpCallInfo = lpTemp; lpCallInfo->dwTotalSize = lpCallInfo->dwNeededSize; } *lpCallInfoRet = lpCallInfo; return (SUCCESS); Failure: if (lpCallInfo) GlobalFreePtr(lpCallInfo); return (lResult); } /* dialGetBaudRate - get baud rate of current connecton */ #undef DPF_MODNAME #define DPF_MODNAME "dialGetBaudRate" LINERESULT dialGetBaudRate(LPDPDIAL globals, LPDWORD lpdwBaudRate) { LPLINECALLINFO lpCallInfo; LINERESULT lResult; lResult = dialGetCallInfo(globals, &lpCallInfo); if LINEERROR(lResult) return (lResult); *lpdwBaudRate = lpCallInfo->dwRate; GlobalFreePtr(lpCallInfo); return (SUCCESS); } /* dialGetTranslateCaps - wrapper for lineGetTranslateCaps */ #undef DPF_MODNAME #define DPF_MODNAME "dialGetTranslateCaps" LINERESULT dialGetTranslateCaps(LPDPDIAL globals, DWORD dwAPIVersion, LPLINETRANSLATECAPS *lpTranslateCapsRet) { LPLINETRANSLATECAPS lpTranslateCaps; LPVOID lpTemp; LINERESULT lResult; // create a buffer for translate caps lpTranslateCaps = (LPLINETRANSLATECAPS) GlobalAllocPtr(GHND, sizeof(LINETRANSLATECAPS)); FAILWITHACTION(lpTranslateCaps == NULL, lResult = LINEERROR(LINEERR_NOMEM), Failure); lpTranslateCaps->dwTotalSize = sizeof(LINETRANSLATECAPS); while (TRUE) { // get translate caps lResult = lineGetTranslateCaps(globals->hLineApp, dwAPIVersion, lpTranslateCaps); if (lResult == SUCCESS) { // make sure there is enough space if (lpTranslateCaps->dwNeededSize <= lpTranslateCaps->dwTotalSize) break; // there is enough space, so exit } else if (lResult != LINEERR_STRUCTURETOOSMALL) { LINEERROR(lResult); goto Failure; } // reallocate buffer if not big enough */ lpTemp = GlobalReAllocPtr(lpTranslateCaps, lpTranslateCaps->dwNeededSize, 0); FAILWITHACTION(lpTemp == NULL, lResult = LINEERROR(LINEERR_NOMEM), Failure); lpTranslateCaps = lpTemp; lpTranslateCaps->dwTotalSize = lpTranslateCaps->dwNeededSize; } *lpTranslateCapsRet = lpTranslateCaps; return (SUCCESS); Failure: if (lpTranslateCaps) GlobalFreePtr(lpTranslateCaps); return (lResult); } /* dialGetCommHandle - wrapper for lineGetID */ #undef DPF_MODNAME #define DPF_MODNAME "dialGetCommHandle" /* structure returned by Unimodem which contains device handle and name */ typedef struct { HANDLE hComm; CHAR szDeviceName[1]; } COMMID, *LPCOMMID; LINERESULT dialGetCommHandle(LPDPDIAL globals) { LPCOMMID lpCommID; VARSTRING *vs, *temp; LINERESULT lResult; vs = (VARSTRING *) GlobalAllocPtr(GHND, sizeof(VARSTRING)); FAILWITHACTION(vs == NULL, lResult = LINEERR_NOMEM, Failure); vs->dwTotalSize = sizeof(VARSTRING); vs->dwStringFormat = STRINGFORMAT_BINARY; while (TRUE) { // get line ID lResult = lineGetID(0, 0L, globals->hCall, LINECALLSELECT_CALL, vs, "comm/datamodem"); if (lResult == SUCCESS) { // make sure there is enough space if (vs->dwNeededSize <= vs->dwTotalSize) break; // there is enough space, so exit } else if (lResult != LINEERR_STRUCTURETOOSMALL) { LINEERROR(lResult); goto Failure; } // reallocate buffer if not big enough */ temp = GlobalReAllocPtr(vs, vs->dwNeededSize, 0); FAILWITHACTION(temp == NULL, lResult = LINEERROR(LINEERR_NOMEM), Failure); vs = temp; vs->dwTotalSize = vs->dwNeededSize; } lpCommID = (LPCOMMID) ((LPSTR)vs + vs->dwStringOffset); // lstrcpy(globals->szDeviceName, cid->szDeviceName); globals->hComm = lpCommID->hComm; Failure: if (vs) GlobalFreePtr(vs); return (lResult); } /* dialCloseCommHandle - make sure com port is closed */ /* NOTE: As per the docs for the "comm/datamodem" device class, the handle to the com port returned by lineGetID() MUST be explictly closed using CloseHandle() or you will not be able to to open this line again! */ #undef DPF_MODNAME #define DPF_MODNAME "dialCloseCommHandle" LINERESULT dialCloseCommHandle(LPDPDIAL globals) { HANDLE hCom; // make sure the com port globals are available if (globals->lpComPort) { // get handle to com port hCom = globals->lpComPort->GetHandle(globals->lpComPort); // make sure its closed down if (hCom) { globals->lpComPort->Shutdown(globals->lpComPort); CloseHandle(hCom); } } return (SUCCESS); } /* dialTranslateAddress - wrapper for lineTranslateAddress */ #undef DPF_MODNAME #define DPF_MODNAME "dialTranslateAddress" LINERESULT dialTranslateAddress(LPDPDIAL globals, DWORD dwDeviceID, DWORD dwAPIVersion, LPCSTR lpszDialAddress, LPLINETRANSLATEOUTPUT *lpLineTranslateOutputRet) { LPLINETRANSLATEOUTPUT lpLineTranslateOutput; LPVOID lpTemp; LINERESULT lResult; // create a buffer for translate caps lpLineTranslateOutput = (LPLINETRANSLATEOUTPUT) GlobalAllocPtr(GHND, sizeof(LINETRANSLATEOUTPUT)); FAILWITHACTION(lpLineTranslateOutput == NULL, lResult = LINEERROR(LINEERR_NOMEM), Failure); lpLineTranslateOutput->dwTotalSize = sizeof(LINETRANSLATEOUTPUT); while (TRUE) { // translate address lResult = lineTranslateAddress(globals->hLineApp, dwDeviceID, dwAPIVersion, lpszDialAddress, 0, LINETRANSLATEOPTION_CANCELCALLWAITING, lpLineTranslateOutput); if (lResult == SUCCESS) { // make sure there is enough space if (lpLineTranslateOutput->dwNeededSize <= lpLineTranslateOutput->dwTotalSize) break; // there is enough space, so exit } else if (lResult != LINEERR_STRUCTURETOOSMALL) { LINEERROR(lResult); goto Failure; } // reallocate buffer if not big enough */ lpTemp = GlobalReAllocPtr(lpLineTranslateOutput, lpLineTranslateOutput->dwNeededSize, 0); FAILWITHACTION(lpTemp == NULL, lResult = LINEERROR(LINEERR_NOMEM), Failure); lpLineTranslateOutput = lpTemp; lpLineTranslateOutput->dwTotalSize = lpLineTranslateOutput->dwNeededSize; } *lpLineTranslateOutputRet = lpLineTranslateOutput; return (SUCCESS); Failure: if (lpLineTranslateOutput) GlobalFreePtr(lpLineTranslateOutput); return (lResult); } LINERESULT dialTranslateDialog(LPDPDIAL globals, HWND hWnd, DWORD dwDeviceID, LPTSTR szPhoneNumber) { LINERESULT lResult; lResult = lineTranslateDialog(globals->hLineApp, dwDeviceID, TAPIVERSION, hWnd, szPhoneNumber); return (lResult); } // // FUNCTION: void dialFillModemComboBox(HWND) // // PURPOSE: Fills the modem control with the available line devices. // // PARAMETERS: // hwndDlg - handle to the current "Dial" dialog // // RETURN VALUE: // none // // COMMENTS: // // This function enumerates through all the TAPI line devices and // queries each for the device name. The device name is then put into // the 'TAPI Line' control. These device names are kept in order rather // than sorted. This allows "Dial" to know which device ID the user // selected just by the knowing the index of the selected string. // // There are default values if there isn't a device name, if there is // an error on the device, or if the device name is an empty string. // The device name is also checked to make sure it is null terminated. // // Note that a Legacy API Version is negotiated. Since the fields in // the LINEDEVCAPS structure that we are interested in haven't moved, we // can negotiate a lower API Version than this sample is designed for // and still be able to access the necessary structure members. // // The first line that is usable by TapiComm is selected as the 'default' // line. Also note that if there was a previously selected line, this // remains the default line. This would likely only occur if this // function is called after the dialog has initialized once; for example, // if a new line is added. // // LINERESULT dialGetModemName(LPDPDIAL globals, DWORD dwDeviceID, LPSTR lpszModemName, DWORD dwModemNameSize) { LPLINEDEVCAPS lpLineDevCaps = NULL; LPSTR lpszLineName; LINEEXTENSIONID lineExtensionID; // Will be set to 0 to indicate no known extensions DWORD dwAPIVersion; // api version DWORD dwStrSize; LINERESULT lResult; /* negotiate API version for each line */ lResult = lineNegotiateAPIVersion(globals->hLineApp, dwDeviceID, TAPIVERSION, TAPIVERSION, &dwAPIVersion, &lineExtensionID); if LINEERROR(lResult) goto FAILURE; lResult = dialGetDevCaps(globals, dwDeviceID, dwAPIVersion, &lpLineDevCaps); if LINEERROR(lResult) goto FAILURE; if ((lpLineDevCaps->dwLineNameSize) && (lpLineDevCaps->dwLineNameOffset) && (lpLineDevCaps->dwStringFormat == STRINGFORMAT_ASCII)) { // This is the name of the device. lpszLineName = ((char *) lpLineDevCaps) + lpLineDevCaps->dwLineNameOffset; if (lpszLineName[0] != '\0') { // Reverse indented to make this fit // Make sure the device name is null terminated. if (lpszLineName[lpLineDevCaps->dwLineNameSize -1] != '\0') { // If the device name is not null terminated, null // terminate it. Yes, this looses the end character. // Its a bug in the service provider. lpszLineName[lpLineDevCaps->dwLineNameSize-1] = '\0'; DPF(0, "Device name for device 0x%lx is not null terminated.", dwDeviceID); } } else // Line name started with a NULL. { lResult = LINEERR_OPERATIONFAILED; goto FAILURE; } } else // DevCaps doesn't have a valid line name. Unnamed. { lResult = LINEERR_OPERATIONFAILED; goto FAILURE; } // return modem name (make sure it fits) dwStrSize = strlen(lpszLineName) + 1; if (dwStrSize <= dwModemNameSize) CopyMemory(lpszModemName, lpszLineName, dwStrSize); else { CopyMemory(lpszModemName, lpszLineName, dwModemNameSize - 1); lpszModemName[dwModemNameSize - 1] = '\0'; } FAILURE: if (lpLineDevCaps) GlobalFreePtr(lpLineDevCaps); return (lResult); } LINERESULT dialGetModemList(LPDPDIAL globals, BOOL bAnsi, LPVOID *lplpData, LPDWORD lpdwDataSize) { DWORD dwDeviceID; CHAR szModemName[MAXSTRINGSIZE]; LPBYTE lpData; DWORD dwDataSize, dwStrBytes, dwStrLen; LINERESULT lResult; // make space for all possible strings plus terminating null lpData = (LPBYTE) GlobalAllocPtr(GHND, globals->dwNumLines * MAXSTRINGSIZE * sizeof(WCHAR) + sizeof(WCHAR)); FAILWITHACTION(lpData == NULL, lResult = LINEERR_NOMEM, Failure); dwDataSize = 0; for (dwDeviceID = 0; dwDeviceID < globals->dwNumLines; dwDeviceID ++) { lResult = dialGetModemName(globals, dwDeviceID, szModemName, MAXSTRINGSIZE); if LINEERROR(lResult) continue; if (bAnsi) { dwStrBytes = (lstrlen(szModemName) + 1) * sizeof(CHAR); memcpy(lpData + dwDataSize, szModemName, dwStrBytes); } else { // NOTE: AnsiToWide returns the character count INCLUDING the terminating null character dwStrLen = AnsiToWide((LPWSTR) (lpData + dwDataSize), szModemName, MAXSTRINGSIZE * sizeof(WCHAR)); dwStrBytes = dwStrLen * sizeof(WCHAR); } dwDataSize += dwStrBytes; } // put a null at end of list to terminate it if (bAnsi) { *(lpData + dwDataSize) = 0; dwDataSize += sizeof(CHAR); } else { *((LPWSTR) (lpData + dwDataSize)) = 0; dwDataSize += sizeof(WCHAR); } // return buffer pointer and size *lplpData = lpData; *lpdwDataSize = dwDataSize; return (SUCCESS); Failure: return (lResult); } void dialFillModemComboBox(LPDPDIAL globals, HWND hwndDlg, int item, DWORD dwDefaultDevice) { DWORD dwDeviceID; CHAR szModemName[MAXSTRINGSIZE]; LINERESULT lResult; for (dwDeviceID = 0; dwDeviceID < globals->dwNumLines; dwDeviceID ++) { // // Attempt to get the modem name. If this fails, don't add the modem // to the dialog. // lResult = dialGetModemName(globals, dwDeviceID, szModemName, MAXSTRINGSIZE); if ( LINEERROR(lResult) == FALSE ) { // // This line appears to be usable, put the device name into the // dialog control and associate the TAPI modem ID with it // lResult = (DWORD) SendDlgItemMessage(hwndDlg, item, CB_ADDSTRING, 0, (LPARAM) szModemName); if ( lResult != CB_ERRSPACE ) { DWORD_PTR TempReturn; // // We've managed to get this entry into the control, make sure // we associate the proper TAPI modem ID with this item. This // should never fail. // TempReturn = SendDlgItemMessage( hwndDlg, item, CB_SETITEMDATA, lResult, dwDeviceID ); DDASSERT( TempReturn != CB_ERR ); // If this line is usable and we don't have a default initial // line yet, make this the initial line. if (dwDefaultDevice == MAXDWORD) dwDefaultDevice = lResult; } } } if (dwDefaultDevice == MAXDWORD) dwDefaultDevice = 0; // Set the initial default line SendDlgItemMessage(hwndDlg, item, CB_SETCURSEL, dwDefaultDevice, 0); } LRESULT dialGetDeviceIDFromName(LPDPDIAL globals, LPCSTR szTargetName, DWORD *lpdwDeviceID) { DWORD dwDeviceID; CHAR szModemName[MAXSTRINGSIZE]; LINERESULT lResult; for (dwDeviceID = 0; dwDeviceID < globals->dwNumLines; dwDeviceID ++) { lResult = dialGetModemName(globals, dwDeviceID, szModemName, MAXSTRINGSIZE); if LINEERROR(lResult) continue; if (strcmp(szModemName, szTargetName) == 0) { *lpdwDeviceID = dwDeviceID; return (SUCCESS); } } return (LINEERR_OPERATIONFAILED); } // // FUNCTION: void dialFillLocationComboBox(HWND) // // PURPOSE: Fills the control with the available calling from locations. // // PARAMETERS: // hwndDlg - handle to the current "Dial" dialog // // RETURN VALUE: // none // // COMMENTS: // // void dialFillLocationComboBox(LPDPDIAL globals, HWND hwndDlg, int item, DWORD dwDefaultLocation) { LPLINETRANSLATECAPS lpTranslateCaps = NULL; LPLINELOCATIONENTRY lpLocationEntry; DWORD dwCounter; LONG index; LINERESULT lResult; // get translate caps lResult = dialGetTranslateCaps(globals, TAPIVERSION, &lpTranslateCaps); if LINEERROR(lResult) return; // Find the location information in the TRANSLATECAPS lpLocationEntry = (LPLINELOCATIONENTRY) (((LPBYTE) lpTranslateCaps) + lpTranslateCaps->dwLocationListOffset); // First empty the combobox SendDlgItemMessage(hwndDlg, item, CB_RESETCONTENT, (WPARAM) 0, (LPARAM) 0); // enumerate all the locations for (dwCounter = 0; dwCounter < lpTranslateCaps->dwNumLocations; dwCounter++) { // Put each one into the combobox index = (DWORD)SendDlgItemMessage(hwndDlg, item, CB_ADDSTRING, (WPARAM) 0, (LPARAM) (((LPBYTE) lpTranslateCaps) + lpLocationEntry[dwCounter].dwLocationNameOffset)); // Is this location the 'current' location? if (lpLocationEntry[dwCounter].dwPermanentLocationID == lpTranslateCaps->dwCurrentLocationID) { // Set this to be the active location. SendDlgItemMessage(hwndDlg, item, CB_SETCURSEL, (WPARAM) index, (LPARAM) 0); } } if (lpTranslateCaps) GlobalFreePtr(lpTranslateCaps); } char gTempStr[200]; LONG lineError(LONG err, LPSTR modName, DWORD lineNum) { if (err) DPF(0, "TAPI line error in %s at line %d : %s", modName, lineNum, GetLineErrStr(err)); return (err); } LPSTR GetCallStateStr(DWORD callState) { switch (callState) { case LINECALLSTATE_IDLE: return ("LINECALLSTATE_IDLE"); case LINECALLSTATE_OFFERING: return ("LINECALLSTATE_OFFERING"); case LINECALLSTATE_ACCEPTED: return ("LINECALLSTATE_ACCEPTED"); case LINECALLSTATE_DIALTONE: return ("LINECALLSTATE_DIALTONE"); case LINECALLSTATE_DIALING: return ("LINECALLSTATE_DIALING"); case LINECALLSTATE_RINGBACK: return ("LINECALLSTATE_RINGBACK"); case LINECALLSTATE_BUSY: return ("LINECALLSTATE_BUSY"); case LINECALLSTATE_SPECIALINFO: return ("LINECALLSTATE_SPECIALINFO"); case LINECALLSTATE_CONNECTED: return ("LINECALLSTATE_CONNECTED"); case LINECALLSTATE_PROCEEDING: return ("LINECALLSTATE_PROCEEDING"); case LINECALLSTATE_ONHOLD: return ("LINECALLSTATE_ONHOLD"); case LINECALLSTATE_CONFERENCED: return ("LINECALLSTATE_CONFERENCED"); case LINECALLSTATE_ONHOLDPENDCONF: return ("LINECALLSTATE_ONHOLDPENDCONF"); case LINECALLSTATE_ONHOLDPENDTRANSFER: return ("LINECALLSTATE_ONHOLDPENDTRANSFER"); case LINECALLSTATE_DISCONNECTED: return ("LINECALLSTATE_DISCONNECTED"); case LINECALLSTATE_UNKNOWN: return ("LINECALLSTATE_UNKNOWN"); } wsprintf(gTempStr, "UNKNOWN CALL STATE = %lu", callState); return (gTempStr); } LPSTR GetLineMsgStr(DWORD msg) { switch (msg) { case LINE_ADDRESSSTATE: return ("LINE_ADDRESSSTATE"); case LINE_CALLINFO: return ("LINE_CALLINFO"); case LINE_CALLSTATE: return ("LINE_CALLSTATE"); case LINE_CLOSE: return ("LINE_CLOSE"); case LINE_DEVSPECIFIC: return ("LINE_DEVSPECIFIC"); case LINE_DEVSPECIFICFEATURE: return ("LINE_DEVSPECIFICFEATURE"); case LINE_GATHERDIGITS: return ("LINE_GATHERDIGITS"); case LINE_GENERATE: return ("LINE_GENERATE"); case LINE_LINEDEVSTATE: return ("LINE_LINEDEVSTATE"); case LINE_MONITORDIGITS: return ("LINE_MONITORDIGITS"); case LINE_MONITORMEDIA: return ("LINE_MONITORMEDIA"); case LINE_MONITORTONE: return ("LINE_MONITORTONE"); case LINE_REPLY: return ("LINE_REPLY"); case LINE_REQUEST: return ("LINE_REQUEST"); } wsprintf(gTempStr, "UNKNOWN LINE MESSAGE = %lu", msg); return (gTempStr); } LPSTR GetLineErrStr(LONG err) { switch (err) { case LINEERR_ADDRESSBLOCKED: return ("LINEERR_ADDRESSBLOCKED"); case LINEERR_ALLOCATED: return ("LINEERR_ALLOCATED"); case LINEERR_BADDEVICEID: return ("LINEERR_BADDEVICEID"); case LINEERR_BEARERMODEUNAVAIL: return ("LINEERR_BEARERMODEUNAVAIL"); case LINEERR_CALLUNAVAIL: return ("LINEERR_CALLUNAVAIL"); case LINEERR_COMPLETIONOVERRUN: return ("LINEERR_COMPLETIONOVERRUN"); case LINEERR_CONFERENCEFULL: return ("LINEERR_CONFERENCEFULL"); case LINEERR_DIALBILLING: return ("LINEERR_DIALBILLING"); case LINEERR_DIALQUIET: return ("LINEERR_DIALQUIET"); case LINEERR_DIALDIALTONE: return ("LINEERR_DIALDIALTONE"); case LINEERR_DIALPROMPT: return ("LINEERR_DIALPROMPT"); case LINEERR_INCOMPATIBLEAPIVERSION: return ("LINEERR_INCOMPATIBLEAPIVERSION"); case LINEERR_INCOMPATIBLEEXTVERSION: return ("LINEERR_INCOMPATIBLEEXTVERSION"); case LINEERR_INIFILECORRUPT: return ("LINEERR_INIFILECORRUPT"); case LINEERR_INUSE: return ("LINEERR_INUSE"); case LINEERR_INVALADDRESS: return ("LINEERR_INVALADDRESS"); case LINEERR_INVALADDRESSID: return ("LINEERR_INVALADDRESSID"); case LINEERR_INVALADDRESSMODE: return ("LINEERR_INVALADDRESSMODE"); case LINEERR_INVALADDRESSSTATE: return ("LINEERR_INVALADDRESSSTATE"); case LINEERR_INVALAPPHANDLE: return ("LINEERR_INVALAPPHANDLE"); case LINEERR_INVALAPPNAME: return ("LINEERR_INVALAPPNAME"); case LINEERR_INVALBEARERMODE: return ("LINEERR_INVALBEARERMODE"); case LINEERR_INVALCALLCOMPLMODE: return ("LINEERR_INVALCALLCOMPLMODE"); case LINEERR_INVALCALLHANDLE: return ("LINEERR_INVALCALLHANDLE"); case LINEERR_INVALCALLPARAMS: return ("LINEERR_INVALCALLPARAMS"); case LINEERR_INVALCALLPRIVILEGE: return ("LINEERR_INVALCALLPRIVILEGE"); case LINEERR_INVALCALLSELECT: return ("LINEERR_INVALCALLSELECT"); case LINEERR_INVALCALLSTATE: return ("LINEERR_INVALCALLSTATE"); case LINEERR_INVALCALLSTATELIST: return ("LINEERR_INVALCALLSTATELIST"); case LINEERR_INVALCARD: return ("LINEERR_INVALCARD"); case LINEERR_INVALCOMPLETIONID: return ("LINEERR_INVALCOMPLETIONID"); case LINEERR_INVALCONFCALLHANDLE: return ("LINEERR_INVALCONFCALLHANDLE"); case LINEERR_INVALCONSULTCALLHANDLE: return ("LINEERR_INVALCONSULTCALLHANDLE"); case LINEERR_INVALCOUNTRYCODE: return ("LINEERR_INVALCOUNTRYCODE"); case LINEERR_INVALDEVICECLASS: return ("LINEERR_INVALDEVICECLASS"); case LINEERR_INVALDIGITLIST: return ("LINEERR_INVALDIGITLIST"); case LINEERR_INVALDIGITMODE: return ("LINEERR_INVALDIGITMODE"); case LINEERR_INVALDIGITS: return ("LINEERR_INVALDIGITS"); case LINEERR_INVALFEATURE: return ("LINEERR_INVALFEATURE"); case LINEERR_INVALGROUPID: return ("LINEERR_INVALGROUPID"); case LINEERR_INVALLINEHANDLE: return ("LINEERR_INVALLINEHANDLE"); case LINEERR_INVALLINESTATE: return ("LINEERR_INVALLINESTATE"); case LINEERR_INVALLOCATION: return ("LINEERR_INVALLOCATION"); case LINEERR_INVALMEDIALIST: return ("LINEERR_INVALMEDIALIST"); case LINEERR_INVALMEDIAMODE: return ("LINEERR_INVALMEDIAMODE"); case LINEERR_INVALMESSAGEID: return ("LINEERR_INVALMESSAGEID"); case LINEERR_INVALPARAM: return ("LINEERR_INVALPARAM"); case LINEERR_INVALPARKMODE: return ("LINEERR_INVALPARKMODE"); case LINEERR_INVALPOINTER: return ("LINEERR_INVALPOINTER"); case LINEERR_INVALPRIVSELECT: return ("LINEERR_INVALPRIVSELECT"); case LINEERR_INVALRATE: return ("LINEERR_INVALRATE"); case LINEERR_INVALREQUESTMODE: return ("LINEERR_INVALREQUESTMODE"); case LINEERR_INVALTERMINALID: return ("LINEERR_INVALTERMINALID"); case LINEERR_INVALTERMINALMODE: return ("LINEERR_INVALTERMINALMODE"); case LINEERR_INVALTIMEOUT: return ("LINEERR_INVALTIMEOUT"); case LINEERR_INVALTONE: return ("LINEERR_INVALTONE"); case LINEERR_INVALTONELIST: return ("LINEERR_INVALTONELIST"); case LINEERR_INVALTONEMODE: return ("LINEERR_INVALTONEMODE"); case LINEERR_INVALTRANSFERMODE: return ("LINEERR_INVALTRANSFERMODE"); case LINEERR_LINEMAPPERFAILED: return ("LINEERR_LINEMAPPERFAILED"); case LINEERR_NOCONFERENCE: return ("LINEERR_NOCONFERENCE"); case LINEERR_NODEVICE: return ("LINEERR_NODEVICE"); case LINEERR_NODRIVER: return ("LINEERR_NODRIVER"); case LINEERR_NOMEM: return ("LINEERR_NOMEM"); case LINEERR_NOMULTIPLEINSTANCE: return ("LINEERR_NOMULTIPLEINSTANCE"); case LINEERR_NOREQUEST: return ("LINEERR_NOREQUEST"); case LINEERR_NOTOWNER: return ("LINEERR_NOTOWNER"); case LINEERR_NOTREGISTERED: return ("LINEERR_NOTREGISTERED"); case LINEERR_OPERATIONFAILED: return ("LINEERR_OPERATIONFAILED"); case LINEERR_OPERATIONUNAVAIL: return ("LINEERR_OPERATIONUNAVAIL"); case LINEERR_RATEUNAVAIL: return ("LINEERR_RATEUNAVAIL"); case LINEERR_REINIT: return ("LINEERR_REINIT"); case LINEERR_RESOURCEUNAVAIL: return ("LINEERR_RESOURCEUNAVAIL"); case LINEERR_STRUCTURETOOSMALL: return ("LINEERR_STRUCTURETOOSMALL"); case LINEERR_TARGETNOTFOUND: return ("LINEERR_TARGETNOTFOUND"); case LINEERR_TARGETSELF: return ("LINEERR_TARGETSELF"); case LINEERR_UNINITIALIZED: return ("LINEERR_UNINITIALIZED"); case LINEERR_USERUSERINFOTOOBIG: return ("LINEERR_USERUSERINFOTOOBIG"); } wsprintf(gTempStr, "UNKNOWN LINE ERROR = %ld", err); return (gTempStr); }