/*++ Copyright (c) 1996 Microsoft Corporation Module Name: tapiutil.c Abstract: Utility functions for working with TAPI Environment: Windows NT fax driver user interface Revision History: 09/18/96 -davidx- Created it. mm/dd/yy -author- description --*/ #include "faxui.h" #include "tapiutil.h" // // Global variables used for accessing TAPI services // static INT tapiRefCount = 0; static HLINEAPP tapiLineApp = 0; static DWORD tapiVersion = 0x00020000; //TAPI_CURRENT_VERSION; static LPLINECOUNTRYLIST pLineCountryList = NULL; BOOL GetCountries( VOID ) /*++ Routine Description: Return a list of countries from TAPI Arguments: NONE Return Value: TRUE if successful, FALSE if there is an error NOTE: We cache the result of lineGetCountry here since it's incredibly slow. This function must be invoked inside a critical section since it updates globally shared information. --*/ #define INITIAL_SIZE_ALL_COUNTRY 22000 { DWORD cbNeeded; LONG status; INT repeatCnt = 0; if (pLineCountryList == NULL) { // // Initial buffer size // cbNeeded = INITIAL_SIZE_ALL_COUNTRY; while (TRUE) { MemFree(pLineCountryList); if (! (pLineCountryList = MemAlloc(cbNeeded))) { Error(("Memory allocation failed\n")); break; } pLineCountryList->dwTotalSize = cbNeeded; status = lineGetCountryW(0, tapiVersion, pLineCountryList); if ((pLineCountryList->dwNeededSize > pLineCountryList->dwTotalSize) && (status == NO_ERROR || status == LINEERR_STRUCTURETOOSMALL || status == LINEERR_NOMEM) && (repeatCnt++ == 0)) { cbNeeded = pLineCountryList->dwNeededSize + 1; Warning(("LINECOUNTRYLIST size: %d\n", cbNeeded)); continue; } if (status != NO_ERROR) { Error(("lineGetCountry failed: %x\n", status)); MemFree(pLineCountryList); pLineCountryList = NULL; } else Verbose(("Number of countries: %d\n", pLineCountryList->dwNumCountries)); break; } } return pLineCountryList != NULL; } VOID CALLBACK TapiLineCallback( DWORD hDevice, DWORD dwMessage, ULONG_PTR dwInstance, ULONG_PTR dwParam1, ULONG_PTR dwParam2, ULONG_PTR dwParam3 ) /*++ Routine Description: TAPI line callback function: Even though we don't actually have anything to do here, we must provide a callback function to keep TAPI happy. Arguments: hDevice - Line or call handle dwMessage - Reason for the callback dwInstance - LINE_INFO index dwParam1 - Callback parameter #1 dwParam2 - Callback parameter #2 dwParam3 - Callback parameter #3 Return Value: NONE --*/ { } BOOL InitTapiService( VOID ) /*++ Routine Description: Initialize the TAPI service if necessary Arguments: NONE Return Value: TRUE if successful, FALSE otherwise NOTE: Every call to this function must be balanced by a call to DeinitTapiService. --*/ { EnterDrvSem(); tapiRefCount++; // // Perform TAPI initialization if necessary // if (!tapiLineApp) { DWORD nLineDevs; LONG status; LINEINITIALIZEEXPARAMS lineInitParams; ZeroMemory(&lineInitParams, sizeof(lineInitParams)); lineInitParams.dwTotalSize = lineInitParams.dwNeededSize = lineInitParams.dwUsedSize = sizeof(lineInitParams); status = lineInitializeExW(&tapiLineApp, ghInstance, TapiLineCallback, L"Fax Configuration", &nLineDevs, &tapiVersion, &lineInitParams); if (status != NO_ERROR) { Error(("lineInitializeEx failed: %x\n", status)); tapiLineApp = 0; } } // // Get the list of countries from TAPI and cache it // if (tapiLineApp && !pLineCountryList) { DWORD startTimer; startTimer = GetCurrentTime(); GetCountries(); Verbose(("lineGetCountryW took %d milliseconds\n", GetCurrentTime() - startTimer)); } LeaveDrvSem(); if (! tapiLineApp) Error(("TAPI initialization failed\n")); return tapiLineApp ? TRUE : FALSE; } VOID DeinitTapiService( VOID ) /*++ Routine Description: Deinitialize the TAPI service if necessary Arguments: NONE Return Value: NONE --*/ { EnterDrvSem(); if (tapiRefCount > 0 && --tapiRefCount == 0) { if (tapiLineApp) { lineShutdown(tapiLineApp); tapiLineApp = 0; } MemFree(pLineCountryList); pLineCountryList = NULL; } LeaveDrvSem(); } DWORD GetDefaultCountryID( VOID ) /*++ Routine Description: Return the default country ID for the current location Arguments: NONE Return Value: The current ID for the current location --*/ { // // We assume the correct information has already been saved to the // registry during the installation process. // return 0; } LPLINETRANSLATECAPS GetTapiLocationInfo( HWND hWnd ) /*++ Routine Description: Get a list of locations from TAPI Arguments: NONE Return Value: Pointer to a LINETRANSLATECAPS structure, NULL if there is an error --*/ #define INITIAL_LINETRANSLATECAPS_SIZE 5000 { DWORD cbNeeded = INITIAL_LINETRANSLATECAPS_SIZE; LONG status; INT repeatCnt = 0; LPLINETRANSLATECAPS pTranslateCaps = NULL; if (!tapiLineApp) return NULL; while (TRUE) { // // Free any existing buffer and allocate a new one with larger size // MemFree(pTranslateCaps); if (! (pTranslateCaps = MemAlloc(cbNeeded))) { Error(("Memory allocation failed\n")); return NULL; } // // Get the LINETRANSLATECAPS structure from TAPI // pTranslateCaps->dwTotalSize = cbNeeded; status = lineGetTranslateCapsW(tapiLineApp, tapiVersion, pTranslateCaps); // // try to bring up UI if there are no locations. // if (status == LINEERR_INIFILECORRUPT) { if (lineTranslateDialog( tapiLineApp, 0, tapiVersion, hWnd, NULL )) { MemFree(pTranslateCaps); return NULL; } continue; } // // Retry if our initial estimated buffer size was too small // if ((pTranslateCaps->dwNeededSize > pTranslateCaps->dwTotalSize) && (status == NO_ERROR || status == LINEERR_STRUCTURETOOSMALL || status == LINEERR_NOMEM) && (repeatCnt++ == 0)) { cbNeeded = pTranslateCaps->dwNeededSize; Warning(("LINETRANSLATECAPS size: %d\n", cbNeeded)); continue; } break; } if (status != NO_ERROR) { Error(("lineGetTranslateCaps failed: %x\n", status)); MemFree(pTranslateCaps); pTranslateCaps = NULL; } return pTranslateCaps; } BOOL SetCurrentLocation( DWORD locationID ) /*++ Routine Description: Change the default TAPI location Arguments: locationID - The permanant ID for the new default TAPI location Return Value: TRUE if successful, FALSE if there is an error --*/ { if (tapiLineApp && (lineSetCurrentLocation(tapiLineApp, locationID) == NO_ERROR)) { Verbose(("Current location changed: ID = %d\n", locationID)); return TRUE; } else { Error(("Couldn't change current TAPI location\n")); return FALSE; } } LPLINECOUNTRYENTRY FindCountry( DWORD countryId ) /*++ Routine Description: Find the specified country from a list of all countries and return a pointer to the corresponding LINECOUNTRYENTRY structure Arguments: countryId - Specifies the country ID we're interested in Return Value: Pointer to a LINECOUNTRYENTRY structure corresponding to the specified country ID NULL if there is an error --*/ { LPLINECOUNTRYENTRY pEntry; DWORD index; if (pLineCountryList == NULL || countryId == 0) return NULL; // // Look at each LINECOUNTRYENTRY structure and compare its country ID with // the specified country ID // pEntry = (LPLINECOUNTRYENTRY) ((PBYTE) pLineCountryList + pLineCountryList->dwCountryListOffset); for (index=0; index < pLineCountryList->dwNumCountries; index++, pEntry++) { if (pEntry->dwCountryID == countryId) return pEntry; } return NULL; } INT AreaCodeRules( LPLINECOUNTRYENTRY pEntry ) /*++ Routine Description: Given a LINECOUNTRYENTRY structure, determine if area code is needed in that country Arguments: pEntry - Points to a LINECOUNTRYENTRY structure Return Value: AREACODE_DONTNEED - Area code is not used in the specified country AREACODE_OPTIONAL - Area code is optional in the specified country AREACODE_REQUIRED - Area code is required in the specified country --*/ { if ((pEntry != NULL) && (pEntry->dwLongDistanceRuleSize != 0) && (pEntry->dwLongDistanceRuleOffset != 0)) { LPTSTR pLongDistanceRule; // // Get the long distance rules for the specified country // Assert(pLineCountryList != NULL); pLongDistanceRule = (LPTSTR) ((PBYTE) pLineCountryList + pEntry->dwLongDistanceRuleOffset); // // Area code is required in this country // if (_tcschr(pLongDistanceRule, TEXT('F')) != NULL) return AREACODE_REQUIRED; // // Area code is not needed in this country // if (_tcschr(pLongDistanceRule, TEXT('I')) == NULL) return AREACODE_DONTNEED; } // // Default case: area code is optional in this country // return AREACODE_OPTIONAL; } VOID AssemblePhoneNumber( LPTSTR pAddress, DWORD countryCode, LPTSTR pAreaCode, LPTSTR pPhoneNumber ) /*++ Routine Description: Assemble a canonical phone number given the following: country code, area code, and phone number Arguments: pAddress - Specifies a buffer to hold the resulting fax address countryCode - Specifies the country code pAreaCode - Specifies the area code string pPhoneNumber - Specifies the phone number string Return Value: NONE Note: We assume the caller has allocated a large enough destination buffer. --*/ { // // Country code if neccessary // if (countryCode != 0) { *pAddress++ = TEXT('+'); wsprintf(pAddress, TEXT("%d "), countryCode); pAddress += _tcslen(pAddress); } // // Area code if necessary // if (pAreaCode && !IsEmptyString(pAreaCode)) { if (countryCode != 0) *pAddress++ = TEXT('('); _tcscpy(pAddress, pAreaCode); pAddress += _tcslen(pAddress); if (countryCode != 0) *pAddress++ = TEXT(')'); *pAddress++ = TEXT(' '); } // // Phone number at last // Assert(pPhoneNumber != NULL); _tcscpy(pAddress, pPhoneNumber); } VOID UpdateAreaCodeField( HWND hwndAreaCode, DWORD countryId ) /*++ Routine Description: Update any area code text field associated with a country list box Arguments: hwndAreaCode - Specifies the text field associated with the country list box countryId - Currently selected country ID Return Value: NONE --*/ { static TCHAR AreaCode[11] = TEXT(""); static BOOL bGetAreaCode = TRUE; if (hwndAreaCode == NULL) return; if ((countryId == -1) || (AreaCodeRules(FindCountry(countryId)) == AREACODE_DONTNEED)) { if (bGetAreaCode == TRUE) { bGetAreaCode = FALSE; SendMessage(hwndAreaCode, WM_GETTEXT, sizeof(AreaCode) / sizeof(TCHAR), (LPARAM) AreaCode); } SendMessage(hwndAreaCode, WM_SETTEXT, 0, (LPARAM) TEXT("")); EnableWindow(hwndAreaCode, FALSE); } else { EnableWindow(hwndAreaCode, TRUE); if (bGetAreaCode == FALSE) { bGetAreaCode = TRUE; SendMessage(hwndAreaCode, WM_SETTEXT, 0, (LPARAM) AreaCode); } } } VOID InitCountryListBox( HWND hwndList, HWND hwndAreaCode, DWORD countryId ) /*++ Routine Description: Initialize the country list box Arguments: hwndList - Handle to the country list box window hwndAreaCode - Handle to an associated area code text field countryId - Initially selected country ID Return Value: NONE --*/ #define MAX_COUNTRY_NAME 256 { DWORD index; TCHAR buffer[MAX_COUNTRY_NAME]; LPLINECOUNTRYENTRY pEntry; // // Disable redraw on the list box and reset its content // SendMessage(hwndList, WM_SETREDRAW, FALSE, 0); SendMessage(hwndList, CB_RESETCONTENT, FALSE, 0); // // Loop through LINECOUNTRYENTRY structures and add the available selections to // the country list box. // if (pLineCountryList != NULL) { pEntry = (LPLINECOUNTRYENTRY) ((PBYTE) pLineCountryList + pLineCountryList->dwCountryListOffset); for (index=0; index < pLineCountryList->dwNumCountries; index++, pEntry++) { if (pEntry->dwCountryNameSize && pEntry->dwCountryNameOffset) { wsprintf(buffer, TEXT("%s (%d)"), (PBYTE) pLineCountryList + pEntry->dwCountryNameOffset, pEntry->dwCountryCode); SendMessage(hwndList, CB_SETITEMDATA, SendMessage(hwndList, CB_ADDSTRING, 0, (LPARAM) buffer), pEntry->dwCountryID); } } } // // Insert None as the very first selection // //LoadString(ghInstance, IDS_NO_COUNTRY, buffer, MAX_COUNTRY_NAME); //SendMessage(hwndList, CB_INSERTSTRING, 0, (LPARAM) buffer); //SendMessage(hwndList, CB_SETITEMDATA, 0, 0); // // Figure out which item in the list should be selected // if (pLineCountryList != NULL) { for (index=0; index <= pLineCountryList->dwNumCountries; index++) { if ((DWORD) SendMessage(hwndList, CB_GETITEMDATA, index, 0) == countryId) break; } if (index > pLineCountryList->dwNumCountries) index = countryId = 0; } else index = countryId = 0; SendMessage(hwndList, CB_SETCURSEL, index, 0); SendMessage(hwndList, WM_SETREDRAW, TRUE, 0); // // Update the associated area code text field // //UpdateAreaCodeField(hwndAreaCode, countryId); } VOID SelChangeCountryListBox( HWND hwndList, HWND hwndAreaCode ) /*++ Routine Description: Handle dialog selection changes in the country list box Arguments: hwndList - Handle to the country list box window hwndAreaCode - Handle to an associated area code text field Return Value: NONE --*/ { UpdateAreaCodeField(hwndAreaCode, GetCountryListBoxSel(hwndList)); } DWORD GetCountryListBoxSel( HWND hwndList ) /*++ Routine Description: Return the current selection of country list box Arguments: hwndList - Handle to the country list box window Return Value: Currently selected country ID --*/ { INT msgResult; if ((msgResult = (INT)SendMessage(hwndList, CB_GETCURSEL, 0, 0)) == CB_ERR || (msgResult = (INT)SendMessage(hwndList, CB_GETITEMDATA, msgResult, 0)) == CB_ERR) { return -1; } return msgResult; } BOOL DoTapiProps( HWND hDlg ) { SHELLEXECUTEINFO shellExeInfo = { sizeof(SHELLEXECUTEINFO), SEE_MASK_NOCLOSEPROCESS, hDlg, L"Open", L"rundll32", L"shell32.dll,Control_RunDLL telephon.cpl", NULL, SW_SHOWNORMAL, }; // // if they said yes, then launch the control panel applet // if (!ShellExecuteEx(&shellExeInfo)) { DisplayMessageDialog(hDlg, 0, 0, IDS_ERR_TAPI_CPL_LAUNCH); return FALSE; } WaitForSingleObject( shellExeInfo.hProcess, INFINITE ); CloseHandle( shellExeInfo.hProcess ) ; return TRUE; }