// // Copyright (c) Microsoft Corporation 1993-1995 // // rovdi.c // // This files contains Device Installer wrappers that we commonly use. // // History: // 11-13-95 ScottH Separated from NT modem class installer // #include "proj.h" #include "rovcomm.h" #include #define MAX_REG_KEY_LEN 128 #define CB_MAX_REG_KEY_LEN (MAX_REG_KEY_LEN * sizeof(TCHAR)) //----------------------------------------------------------------------------------- // Port mapping functions //----------------------------------------------------------------------------------- #define CPORTPAIR 8 typedef struct tagPORTPAIR { CHAR szPortName[MAX_BUF]; CHAR szFriendlyName[MAX_BUF]; } PORTPAIR, FAR * LPPORTPAIR; typedef struct tagPORTMAP { LPPORTPAIR rgports; // Alloc int cports; } PORTMAP, FAR * LPPORTMAP; /*---------------------------------------------------------- Purpose: Performs a local realloc my way Returns: TRUE on success Cond: -- */ BOOL PRIVATE MyLocalReAlloc( LPVOID FAR * ppv, int cbOld, int cbNew) { LPVOID pv = (LPVOID)LocalAlloc(LPTR, cbNew); if (LOCALOF(pv)) { BltByte(pv, *ppv, min(cbOld, cbNew)); LocalFreePtr(*ppv); *ppv = pv; } return (NULL != pv); } /*---------------------------------------------------------- Purpose: Device enumerator callback. Adds another device to the map table. Returns: TRUE to continue enumeration Cond: -- */ BOOL CALLBACK PortMap_Add( HPORTDATA hportdata, LPARAM lParam) { BOOL bRet; PORTDATA pd; pd.cbSize = sizeof(pd); bRet = PortData_GetProperties(hportdata, &pd); if (bRet) { LPPORTMAP pmap = (LPPORTMAP)lParam; LPPORTPAIR ppair; int cb; int cbUsed; // Time to reallocate the table? cb = LocalSize(LOCALOF(pmap->rgports)); cbUsed = pmap->cports * sizeof(*ppair); if (cbUsed >= cb) { // Yes cb += (CPORTPAIR * sizeof(*ppair)); bRet = MyLocalReAlloc((LPVOID FAR *)&pmap->rgports, cbUsed, cb); } if (bRet) { ppair = &pmap->rgports[pmap->cports++]; #ifdef UNICODE // Fields of LPPORTPAIR are always ANSI WideCharToMultiByte(CP_ACP, 0, pd.szPort, -1, ppair->szPortName, SIZECHARS(ppair->szPortName), 0, 0); WideCharToMultiByte(CP_ACP, 0, pd.szFriendly, -1, ppair->szFriendlyName, SIZECHARS(ppair->szFriendlyName), 0, 0); #else lstrcpy(ppair->szPortName, pd.szPort); lstrcpy(ppair->szFriendlyName, pd.szFriendly); #endif DEBUG_CODE( TRACE_MSGA(TF_GENERAL, "Added %s <-> %s to portmap", (LPSTR)ppair->szPortName, (LPSTR)ppair->szFriendlyName); ) } } return bRet; } /*---------------------------------------------------------- Purpose: Wide-char version. This function creates a port map table that maps port names to friendly names, and vice-versa. Returns: TRUE on success Cond: -- */ BOOL APIENTRY PortMap_Create( OUT HPORTMAP FAR * phportmap) { LPPORTMAP pmap; pmap = (LPPORTMAP)LocalAlloc(LPTR, sizeof(*pmap)); if (pmap) { // Initially alloc 8 entries pmap->rgports = (LPPORTPAIR)LocalAlloc(LPTR, CPORTPAIR*sizeof(*pmap->rgports)); if (pmap->rgports) { // Fill the map table EnumeratePorts(PortMap_Add, (LPARAM)pmap); } else { // Error LocalFreePtr(pmap); pmap = NULL; } } *phportmap = (HPORTMAP)pmap; return (NULL != pmap); } /*---------------------------------------------------------- Purpose: Gets the count of ports on the system. Returns: see above Cond: -- */ DWORD APIENTRY PortMap_GetCount( IN HPORTMAP hportmap) { DWORD dwRet; LPPORTMAP pmap = (LPPORTMAP)hportmap; try { dwRet = pmap->cports; } except (EXCEPTION_EXECUTE_HANDLER) { dwRet = 0; } return dwRet; } /*---------------------------------------------------------- Purpose: Gets the friendly name given the port name and places a copy in the supplied buffer. If no port name is found, the contents of the supplied buffer is not changed. Wide-char version. Returns: TRUE on success FALSE if the port name is not found Cond: -- */ BOOL APIENTRY PortMap_GetFriendlyW( IN HPORTMAP hportmap, IN LPCWSTR pwszPortName, OUT LPWSTR pwszBuf, IN DWORD cchBuf) { BOOL bRet; ASSERT(pwszPortName); ASSERT(pwszBuf); try { CHAR szPort[MAX_BUF_MED]; CHAR szBuf[MAX_BUF]; WideCharToMultiByte(CP_ACP, 0, pwszPortName, -1, szPort, SIZECHARS(szPort), 0, 0); bRet = PortMap_GetFriendlyA(hportmap, szPort, szBuf, SIZECHARS(szBuf)); if (bRet) { MultiByteToWideChar(CP_ACP, 0, szBuf, -1, pwszBuf, cchBuf); } } except (EXCEPTION_EXECUTE_HANDLER) { SetLastError(ERROR_INVALID_PARAMETER); bRet = FALSE; } return bRet; } /*---------------------------------------------------------- Purpose: Gets the friendly name given the port name and places a copy in the supplied buffer. If no port name is found, the contents of the supplied buffer is not changed. Returns: TRUE on success FALSE if the port name is not found Cond: -- */ BOOL APIENTRY PortMap_GetFriendlyA( IN HPORTMAP hportmap, IN LPCSTR pszPortName, OUT LPSTR pszBuf, IN DWORD cchBuf) { LPPORTMAP pmap = (LPPORTMAP)hportmap; ASSERT(pmap); ASSERT(pszPortName); ASSERT(pszBuf); try { LPPORTPAIR pport = pmap->rgports; int cports = pmap->cports; int i; for (i = 0; i < cports; i++, pport++) { if (0 == lstrcmpiA(pszPortName, pport->szPortName)) { lstrcpynA(pszBuf, pport->szFriendlyName, cchBuf); return TRUE; } } } except (EXCEPTION_EXECUTE_HANDLER) { SetLastError(ERROR_INVALID_PARAMETER); } return FALSE; } /*---------------------------------------------------------- Purpose: Gets the port name given the friendly name and places a copy in the supplied buffer. If no friendly name is found, the contents of the supplied buffer is not changed. Wide-char version. Returns: TRUE on success FALSE if the friendly name is not found Cond: -- */ BOOL APIENTRY PortMap_GetPortNameW( IN HPORTMAP hportmap, IN LPCWSTR pwszFriendly, OUT LPWSTR pwszBuf, IN DWORD cchBuf) { BOOL bRet; ASSERT(pwszFriendly); ASSERT(pwszBuf); try { CHAR szFriendly[MAX_BUF]; CHAR szBuf[MAX_BUF_MED]; WideCharToMultiByte(CP_ACP, 0, pwszFriendly, -1, szFriendly, SIZECHARS(szFriendly), 0, 0); bRet = PortMap_GetPortNameA(hportmap, szFriendly, szBuf, SIZECHARS(szBuf)); if (bRet) { MultiByteToWideChar(CP_ACP, 0, szBuf, -1, pwszBuf, cchBuf); } } except (EXCEPTION_EXECUTE_HANDLER) { bRet = FALSE; SetLastError(ERROR_INVALID_PARAMETER); } return bRet; } /*---------------------------------------------------------- Purpose: Gets the port name given the friendly name and places a copy in the supplied buffer. If no friendly name is found, the contents of the supplied buffer is not changed. Returns: TRUE FALSE if the friendly name is not found Cond: -- */ BOOL APIENTRY PortMap_GetPortNameA( IN HPORTMAP hportmap, IN LPCSTR pszFriendly, OUT LPSTR pszBuf, IN DWORD cchBuf) { LPPORTMAP pmap = (LPPORTMAP)hportmap; ASSERT(pmap); ASSERT(pszFriendly); ASSERT(pszBuf); try { LPPORTPAIR pport = pmap->rgports; int cports = pmap->cports; int i; for (i = 0; i < cports; i++, pport++) { if (0 == lstrcmpiA(pszFriendly, pport->szFriendlyName)) { lstrcpynA(pszBuf, pport->szPortName, cchBuf); return TRUE; } } } except (EXCEPTION_EXECUTE_HANDLER) { SetLastError(ERROR_INVALID_PARAMETER); } return FALSE; } /*---------------------------------------------------------- Purpose: Frees a port map Returns: -- Cond: -- */ BOOL APIENTRY PortMap_Free( IN HPORTMAP hportmap) { LPPORTMAP pmap = (LPPORTMAP)hportmap; if (pmap) { if (pmap->rgports) LocalFreePtr(pmap->rgports); LocalFreePtr(pmap); } return TRUE; } //----------------------------------------------------------------------------------- // Port enumeration functions //----------------------------------------------------------------------------------- #pragma data_seg(DATASEG_READONLY) TCHAR const FAR c_szSerialComm[] = TEXT("HARDWARE\\DEVICEMAP\\SERIALCOMM"); #pragma data_seg() /*---------------------------------------------------------- Purpose: Enumerates all the ports on the system and calls pfnDevice. pfnDevice can terminate the enumeration by returning FALSE. Returns: NO_ERROR if at least one port was found Cond: -- */ DWORD APIENTRY EnumeratePorts( IN ENUMPORTPROC pfnDevice, IN LPARAM lParam) OPTIONAL { DWORD dwRet; HKEY hkeyEnum; dwRet = RegOpenKey(HKEY_LOCAL_MACHINE, c_szSerialComm, &hkeyEnum); if (NO_ERROR == dwRet) { BOOL bContinue; PORTDATA pd; DWORD iSubKey; TCHAR szValue[MAX_BUF]; DWORD cbValue; DWORD cbData; DWORD dwType; dwRet = ERROR_PATH_NOT_FOUND; // assume no ports iSubKey = 0; cbValue = sizeof(szValue); cbData = sizeof(pd.szPort); while (NO_ERROR == RegEnumValue(hkeyEnum, iSubKey++, szValue, &cbValue, NULL, &dwType, (LPBYTE)pd.szPort, &cbData)) { if (REG_SZ == dwType) { // Friendly name is the same as the port name right now dwRet = NO_ERROR; pd.nSubclass = PORT_SUBCLASS_SERIAL; lstrcpy(pd.szFriendly, pd.szPort); bContinue = pfnDevice((HPORTDATA)&pd, lParam); // Continue? if ( !bContinue ) { // No break; } } cbValue = sizeof(szValue); cbData = sizeof(pd.szPort); } RegCloseKey(hkeyEnum); } return dwRet; } /*---------------------------------------------------------- Purpose: This function fills the given buffer with the properties of the particular port. Wide-char version. Returns: TRUE on success Cond: -- */ BOOL APIENTRY PortData_GetPropertiesW( IN HPORTDATA hportdata, OUT LPPORTDATA_W pdataBuf) { BOOL bRet = FALSE; ASSERT(hportdata); ASSERT(pdataBuf); if (hportdata && pdataBuf) { // Is the handle to a Widechar version? if (sizeof(PORTDATA_W) == pdataBuf->cbSize) { // Yes LPPORTDATA_W ppd = (LPPORTDATA_W)hportdata; pdataBuf->nSubclass = ppd->nSubclass; lstrcpynW(pdataBuf->szPort, ppd->szPort, SIZECHARS(pdataBuf->szPort)); lstrcpynW(pdataBuf->szFriendly, ppd->szFriendly, SIZECHARS(pdataBuf->szFriendly)); bRet = TRUE; } else if (sizeof(PORTDATA_A) == pdataBuf->cbSize) { // No; this is the Ansi version LPPORTDATA_A ppd = (LPPORTDATA_A)hportdata; pdataBuf->nSubclass = ppd->nSubclass; MultiByteToWideChar(CP_ACP, 0, ppd->szPort, -1, pdataBuf->szPort, SIZECHARS(pdataBuf->szPort)); MultiByteToWideChar(CP_ACP, 0, ppd->szFriendly, -1, pdataBuf->szFriendly, SIZECHARS(pdataBuf->szFriendly)); bRet = TRUE; } else { // Some invalid size ASSERT(0); } } return bRet; } /*---------------------------------------------------------- Purpose: This function fills the given buffer with the properties of the particular port. Returns: TRUE on success Cond: -- */ BOOL APIENTRY PortData_GetPropertiesA( IN HPORTDATA hportdata, OUT LPPORTDATA_A pdataBuf) { BOOL bRet = FALSE; ASSERT(hportdata); ASSERT(pdataBuf); if (hportdata && pdataBuf) { // Is the handle to a Widechar version? if (sizeof(PORTDATA_W) == pdataBuf->cbSize) { // Yes LPPORTDATA_W ppd = (LPPORTDATA_W)hportdata; pdataBuf->nSubclass = ppd->nSubclass; WideCharToMultiByte(CP_ACP, 0, ppd->szPort, -1, pdataBuf->szPort, SIZECHARS(pdataBuf->szPort), NULL, NULL); WideCharToMultiByte(CP_ACP, 0, ppd->szFriendly, -1, pdataBuf->szFriendly, SIZECHARS(pdataBuf->szFriendly), NULL, NULL); bRet = TRUE; } else if (sizeof(PORTDATA_A) == pdataBuf->cbSize) { // No; this is the Ansi version LPPORTDATA_A ppd = (LPPORTDATA_A)hportdata; pdataBuf->nSubclass = ppd->nSubclass; lstrcpynA(pdataBuf->szPort, ppd->szPort, SIZECHARS(pdataBuf->szPort)); lstrcpynA(pdataBuf->szFriendly, ppd->szFriendly, SIZECHARS(pdataBuf->szFriendly)); bRet = TRUE; } else { // Some invalid size ASSERT(0); } } return bRet; } //----------------------------------------------------------------------------------- // DeviceInstaller wrappers and support functions //----------------------------------------------------------------------------------- #pragma data_seg(DATASEG_READONLY) static TCHAR const FAR c_szBackslash[] = TEXT("\\"); static TCHAR const FAR c_szSeparator[] = TEXT("::"); static TCHAR const FAR c_szFriendlyName[] = TEXT("FriendlyName"); // REGSTR_VAL_FRIENDLYNAME static TCHAR const FAR c_szDeviceType[] = TEXT("DeviceType"); // REGSTR_VAL_DEVTYPE static TCHAR const FAR c_szAttachedTo[] = TEXT("AttachedTo"); static TCHAR const FAR c_szDriverDesc[] = TEXT("DriverDesc"); // REGSTR_VAL_DRVDESC static TCHAR const FAR c_szManufacturer[] = TEXT("Manufacturer"); static TCHAR const FAR c_szRespKeyName[] = TEXT("ResponsesKeyName"); TCHAR const FAR c_szRefCount[] = TEXT("RefCount"); TCHAR const FAR c_szResponses[] = TEXT("Responses"); #define DRIVER_KEY REGSTR_PATH_SETUP TEXT("\\Unimodem\\DeviceSpecific") #define RESPONSES_KEY TEXT("\\Responses") #pragma data_seg() /*---------------------------------------------------------- Purpose: Retrieves the friendly name of the device. If there is no such device or friendly name, this function returns FALSE. Returns: see above Cond: -- */ BOOL PUBLIC CplDiGetPrivateProperties( IN HDEVINFO hdi, IN PSP_DEVINFO_DATA pdevData, OUT PMODEM_PRIV_PROP pmpp) { BOOL bRet; HKEY hkey; ASSERT(hdi && INVALID_HANDLE_VALUE != hdi); ASSERT(pdevData); ASSERT(pmpp); if (sizeof(*pmpp) != pmpp->cbSize) { bRet = FALSE; SetLastError(ERROR_INVALID_PARAMETER); } else { hkey = CplDiOpenDevRegKey(hdi, pdevData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ); if (INVALID_HANDLE_VALUE == hkey) { #ifdef DEBUG DWORD dwErr = NO_ERROR; dwErr = GetLastError(); #endif bRet = FALSE; } else { DWORD cbData; DWORD dwMask = pmpp->dwMask; BYTE nValue; pmpp->dwMask = 0; if (IsFlagSet(dwMask, MPPM_FRIENDLY_NAME)) { // Attempt to get the friendly name cbData = sizeof(pmpp->szFriendlyName); if (NO_ERROR == RegQueryValueEx(hkey, c_szFriendlyName, NULL, NULL, (LPBYTE)pmpp->szFriendlyName, &cbData)) { SetFlag(pmpp->dwMask, MPPM_FRIENDLY_NAME); } } if (IsFlagSet(dwMask, MPPM_DEVICE_TYPE)) { // Attempt to get the device type cbData = sizeof(nValue); if (NO_ERROR == RegQueryValueEx(hkey, c_szDeviceType, NULL, NULL, &nValue, &cbData)) { pmpp->nDeviceType = nValue; // dword <-- byte SetFlag(pmpp->dwMask, MPPM_DEVICE_TYPE); } } if (IsFlagSet(dwMask, MPPM_PORT)) { // Attempt to get the attached port cbData = sizeof(pmpp->szPort); if (NO_ERROR == RegQueryValueEx(hkey, c_szAttachedTo, NULL, NULL, (LPBYTE)pmpp->szPort, &cbData)) { SetFlag(pmpp->dwMask, MPPM_PORT); } } bRet = TRUE; RegCloseKey(hkey); } } return bRet; } /*---------------------------------------------------------- Purpose: This function returns the bus type on which the device can be enumerated. Returns: TRUE on success Cond: -- */ BOOL PUBLIC CplDiGetBusType( IN HDEVINFO hdi, IN PSP_DEVINFO_DATA pdevData, OPTIONAL OUT LPDWORD pdwBusType) { BOOL bRet; TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN]; ASSERT(hdi && INVALID_HANDLE_VALUE != hdi); ASSERT(pdwBusType); #ifdef WIN95 // For Win95, the bus type was determined thru a couple of means. // Before the device is registered with the configuration manager // (CM), we parse the registry pathname to determine the bus type. // Once the device is registered, we use the CM APIs. // // The functions used in the modem legacy code were: // // IsRootEnumerated // IsPCMCIA // IsStrInStr // StrIsExternalPnP #else #define REGSTR_KEY_ISAENUM_ROOT (REGSTR_KEY_ISAENUM TEXT("\\")) // For NT SUR, the bus type is either ROOT or ISAPNP (treated as 'other') // Hot plug and play and additional enumerators will be implemented // after SUR. // // Get the device instance name, to determine if it's under the PNPISA // enumerator branch. // SetupDiGetDeviceInstanceId(hdi, pdevData, DeviceInstanceId, sizeof(DeviceInstanceId) / sizeof(TCHAR), NULL ); *pdwBusType = _tcsnicmp(DeviceInstanceId, REGSTR_KEY_ISAENUM_ROOT, sizeof(REGSTR_KEY_ISAENUM_ROOT) / sizeof(TCHAR) - 1) ? BUS_TYPE_ROOT : BUS_TYPE_OTHER; bRet = TRUE; #endif // WIN95 return bRet; } /*---------------------------------------------------------- Purpose: This function returns the name of the common driver type key for the given driver. We'll use the driver description string, since it's unique per driver but not per installation (the friendly name is the latter). Returns: TRUE on success FALSE on error Cond: -- */ BOOL PRIVATE OLD_GetCommonDriverKeyName( IN HKEY hkeyDrv, IN DWORD cbKeyName, OUT LPTSTR pszKeyName) { BOOL bRet = FALSE; // assume failure LONG lErr; lErr = RegQueryValueEx(hkeyDrv, c_szDriverDesc, NULL, NULL, (LPBYTE)pszKeyName, &cbKeyName); if (lErr != ERROR_SUCCESS) { TRACE_MSG(TF_WARNING, "RegQueryValueEx(DriverDesc) failed: %#08lx.", lErr); goto exit; } bRet = TRUE; exit: return(bRet); } /*---------------------------------------------------------- Purpose: This function tries to open the *old style* common Responses key for the given driver, which used only the driver description string for a key name. The key is opened with READ access. Returns: TRUE on success FALSE on error Cond: -- */ BOOL PRIVATE OLD_OpenCommonResponsesKey( IN HKEY hkeyDrv, OUT PHKEY phkeyResp) { BOOL bRet = FALSE; // assume failure LONG lErr; TCHAR szComDrv[MAX_REG_KEY_LEN]; TCHAR szPath[2*MAX_REG_KEY_LEN]; *phkeyResp = NULL; // Get the name (*old style*) of the common driver key. if (!OLD_GetCommonDriverKeyName(hkeyDrv, sizeof(szComDrv), szComDrv)) { TRACE_MSG(TF_ERROR, "OLD_GetCommonDriverKeyName() failed."); goto exit; } TRACE_MSG(TF_WARNING, "OLD_GetCommonDriverKeyName(): %s", szComDrv); // Construct the path to the (*old style*) Responses key. lstrcpy(szPath, DRIVER_KEY TEXT("\\")); lstrcat(szPath, szComDrv); lstrcat(szPath, RESPONSES_KEY); // Open the (*old style*) Responses key. lErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szPath, 0, KEY_READ, phkeyResp); if (lErr != ERROR_SUCCESS) { TRACE_MSG(TF_ERROR, "RegOpenKeyEx(Responses) failed: %#08lx.", lErr); goto exit; } bRet = TRUE; exit: return(bRet); } /*---------------------------------------------------------- Purpose: This function finds the name of the common driver type key for the given driver. First it'll look for the new style key name ("ResponsesKeyName" value), and if that doesn't exist then it'll look for the old style key name ("Description" value), both of which are stored in the driver node. NOTE: The given driver key handle is assumed to contain at least the Description value. Returns: TRUE on success FALSE on error Cond: -- */ BOOL PUBLIC FindCommonDriverKeyName( IN HKEY hkeyDrv, IN DWORD cbKeyName, OUT LPTSTR pszKeyName) { BOOL bRet = TRUE; // assume *success* LONG lErr; // Is the (new style) key name is registered in the driver node? lErr = RegQueryValueEx(hkeyDrv, c_szRespKeyName, NULL, NULL, (LPBYTE)pszKeyName, &cbKeyName); if (lErr == ERROR_SUCCESS) { goto exit; } // No. The key name will be in the old style: just the Description. lErr = RegQueryValueEx(hkeyDrv, c_szDriverDesc, NULL, NULL, (LPBYTE)pszKeyName, &cbKeyName); if (lErr == ERROR_SUCCESS) { goto exit; } // Couldn't get a key name!! Something's wrong.... ASSERT(0); bRet = FALSE; exit: return(bRet); } /*---------------------------------------------------------- Purpose: This function returns the name of the common driver type key for the given driver. The key name is the concatenation of 3 strings found in the driver node of the registry: the driver description, the manu- facturer, and the provider. (The driver description is used since it's unique per driver but not per installation (the "friendly" name is the latter). NOTE: The component substrings are either read from the driver's registry key, or from the given driver info data. If pdrvData is given, the strings it contains are assumed to be valid (non-NULL). Returns: TRUE on success FALSE on error Cond: -- */ BOOL PUBLIC GetCommonDriverKeyName( IN HKEY hkeyDrv, OPTIONAL IN PSP_DRVINFO_DATA pdrvData, OPTIONAL IN DWORD cbKeyName, OUT LPTSTR pszKeyName) { BOOL bRet = FALSE; // assume failure LONG lErr; DWORD dwByteCount, cbData; TCHAR szDescription[MAX_REG_KEY_LEN]; TCHAR szManufacturer[MAX_REG_KEY_LEN]; TCHAR szProvider[MAX_REG_KEY_LEN]; LPTSTR lpszDesc, lpszMfct, lpszProv; dwByteCount = 0; lpszDesc = NULL; lpszMfct = NULL; lpszProv = NULL; if (hkeyDrv) { // First see if it's already been registered in the driver node. lErr = RegQueryValueEx(hkeyDrv, c_szRespKeyName, NULL, NULL, (LPBYTE)pszKeyName, &cbKeyName); if (lErr == ERROR_SUCCESS) { bRet = TRUE; goto exit; } // Responses key doesn't exist - read its components from the registry. cbData = sizeof(szDescription); lErr = RegQueryValueEx(hkeyDrv, c_szDriverDesc, NULL, NULL, (LPBYTE)szDescription, &cbData); if (lErr == ERROR_SUCCESS) { // Is the Description string *alone* too long to be a key name? // If so then we're hosed - fail the call. if (cbData > CB_MAX_REG_KEY_LEN) { goto exit; } dwByteCount = cbData; lpszDesc = szDescription; cbData = sizeof(szManufacturer); lErr = RegQueryValueEx(hkeyDrv, c_szManufacturer, NULL, NULL, (LPBYTE)szManufacturer, &cbData); if (lErr == ERROR_SUCCESS) { // only use the manufacturer name if total string size is ok cbData += sizeof(c_szSeparator); if ((dwByteCount + cbData) <= CB_MAX_REG_KEY_LEN) { dwByteCount += cbData; lpszMfct = szManufacturer; } } cbData = sizeof(szProvider); lErr = RegQueryValueEx(hkeyDrv, REGSTR_VAL_PROVIDER_NAME, NULL, NULL, (LPBYTE)szProvider, &cbData); if (lErr == ERROR_SUCCESS) { // only use the provider name if total string size is ok cbData += sizeof(c_szSeparator); if ((dwByteCount + cbData) <= CB_MAX_REG_KEY_LEN) { dwByteCount += cbData; lpszProv = szProvider; } } } } // Weren't able to read key name components out of the driver node. // Get them from the driver info data if one was given. if (pdrvData && !dwByteCount) { lpszDesc = pdrvData->Description; if (!lpszDesc[0]) { // Didn't get a Description string. Fail the call. goto exit; } dwByteCount = CbFromCch(lstrlen(lpszDesc)+1); // Is the Description string *alone* too long to be a key name? // If so then we're hosed - fail the call. if (dwByteCount > CB_MAX_REG_KEY_LEN) { goto exit; } cbData = sizeof(c_szSeparator) + CbFromCch(lstrlen(pdrvData->MfgName)+1); if ((dwByteCount + cbData) <= CB_MAX_REG_KEY_LEN) { dwByteCount += cbData; lpszMfct = pdrvData->MfgName; } cbData = sizeof(c_szSeparator) + CbFromCch(lstrlen(pdrvData->ProviderName)+1); if ((dwByteCount + cbData) <= CB_MAX_REG_KEY_LEN) { dwByteCount += cbData; lpszProv = pdrvData->ProviderName; } } // By now we should have a Description string. If not, fail the call. if (!lpszDesc[0]) { goto exit; } // Construct the key name string out of its components. lstrcpy(pszKeyName, lpszDesc); if (lpszMfct && *lpszMfct) { lstrcat(pszKeyName, c_szSeparator); lstrcat(pszKeyName, lpszMfct); } if (lpszProv && *lpszProv) { lstrcat(pszKeyName, c_szSeparator); lstrcat(pszKeyName, lpszProv); } // Write the key name to the driver node (we know it's not there already). if (hkeyDrv) { lErr = RegSetValueEx(hkeyDrv, c_szRespKeyName, 0, REG_SZ, (LPBYTE)pszKeyName, CbFromCch(lstrlen(pszKeyName)+1)); if (lErr != ERROR_SUCCESS) { TRACE_MSG(TF_ERROR, "RegSetValueEx(RespKeyName) failed: %#08lx.", lErr); ASSERT(0); } } bRet = TRUE; exit: return(bRet); } /*---------------------------------------------------------- Purpose: This function creates the common driver type key for the given driver, or opens it if it already exists, with the requested access. NOTE: Either hkeyDrv or pdrvData must be provided. Returns: TRUE on success FALSE on error Cond: -- */ BOOL PUBLIC OpenCommonDriverKey( IN HKEY hkeyDrv, OPTIONAL IN PSP_DRVINFO_DATA pdrvData, OPTIONAL IN REGSAM samAccess, OUT PHKEY phkeyComDrv) { BOOL bRet = FALSE; // assume failure LONG lErr; HKEY hkeyDrvInfo = NULL; TCHAR szComDrv[MAX_REG_KEY_LEN]; TCHAR szPath[2*MAX_REG_KEY_LEN]; DWORD dwDisp; if (!GetCommonDriverKeyName(hkeyDrv, pdrvData, sizeof(szComDrv), szComDrv)) { TRACE_MSG(TF_ERROR, "GetCommonDriverKeyName() failed."); goto exit; } TRACE_MSG(TF_WARNING, "GetCommonDriverKeyName(): %s", szComDrv); // Construct the path to the common driver key. lstrcpy(szPath, DRIVER_KEY TEXT("\\")); lstrcat(szPath, szComDrv); // Create the common driver key - it'll be opened if it already exists. lErr = RegCreateKeyEx(HKEY_LOCAL_MACHINE, szPath, 0, NULL, REG_OPTION_NON_VOLATILE, samAccess, NULL, phkeyComDrv, &dwDisp); if (lErr != ERROR_SUCCESS) { TRACE_MSG(TF_ERROR, "RegCreateKeyEx(common drv) failed: %#08lx.", lErr); goto exit; } bRet = TRUE; exit: return(bRet); } /*---------------------------------------------------------- Purpose: This function opens or creates the common Responses key for the given driver, based on the given flags. Returns: TRUE on success FALSE on error Cond: -- */ BOOL PUBLIC OpenCommonResponsesKey( IN HKEY hkeyDrv, IN CKFLAGS ckFlags, IN REGSAM samAccess, OUT PHKEY phkeyResp, OUT LPDWORD lpdwExisted) { BOOL bRet = FALSE; // assume failure LONG lErr; HKEY hkeyComDrv = NULL; REGSAM sam; DWORD dwRefCount, cbData; *phkeyResp = NULL; sam = (ckFlags & CKFLAG_CREATE) ? KEY_ALL_ACCESS : KEY_READ; if (!OpenCommonDriverKey(hkeyDrv, NULL, sam, &hkeyComDrv)) { TRACE_MSG(TF_ERROR, "OpenCommonDriverKey() failed."); goto exit; } // Create or open the common Responses key. if (ckFlags & CKFLAG_CREATE) { lErr = RegCreateKeyEx(hkeyComDrv, c_szResponses, 0, NULL, REG_OPTION_NON_VOLATILE, samAccess, NULL, phkeyResp, lpdwExisted); if (lErr != ERROR_SUCCESS) { TRACE_MSG(TF_ERROR, "RegCreateKeyEx(common drv) failed: %#08lx.", lErr); ASSERT(0); goto exit; } // Create or increment a common Responses key reference count value. cbData = sizeof(dwRefCount); if (*lpdwExisted == REG_OPENED_EXISTING_KEY) { lErr = RegQueryValueEx(hkeyComDrv, c_szRefCount, NULL, NULL, (LPBYTE)&dwRefCount, &cbData); // To accomodate modems installed before this reference count // mechanism was added (post-Beta2), if the reference count doesn't // exist then just ignore it & install anyways. In this case the // shared Responses key will never be removed. if (lErr == ERROR_SUCCESS) { ASSERT(dwRefCount); // expecting non-0 ref count ASSERT(cbData == sizeof(DWORD)); // expecting DWORD ref count dwRefCount++; // increment ref count } else { if (lErr == ERROR_FILE_NOT_FOUND) dwRefCount = 0; else { // some error other than key doesn't exist TRACE_MSG(TF_ERROR, "RegQueryValueEx(RefCount) failed: %#08lx.", lErr); goto exit; } } } else dwRefCount = 1; if (dwRefCount) { lErr = RegSetValueEx(hkeyComDrv, c_szRefCount, 0, REG_DWORD, (LPBYTE)&dwRefCount, cbData); if (lErr != ERROR_SUCCESS) { TRACE_MSG(TF_ERROR, "RegSetValueEx(RefCount) failed: %#08lx.", lErr); ASSERT(0); goto exit; } } } else if (ckFlags & CKFLAG_OPEN) { lErr = RegOpenKeyEx(hkeyComDrv, c_szResponses, 0, samAccess, phkeyResp); if (lErr != ERROR_SUCCESS) { TRACE_MSG(TF_ERROR, "RegOpenKeyEx(common drv) failed: %#08lx.", lErr); goto exit; } } bRet = TRUE; exit: if (!bRet) { // something failed - close any open Responses key if (*phkeyResp) RegCloseKey(*phkeyResp); } if (hkeyComDrv) RegCloseKey(hkeyComDrv); return(bRet); } /*---------------------------------------------------------- Purpose: This function finds the Responses key for the given modem driver and returns an open hkey to it. The Responses key may exist in the common driver type key, or it may be in the individual driver key. The key is opened with READ access. Returns: TRUE on success FALSE on error Cond: -- */ BOOL PUBLIC OpenResponsesKey( IN HKEY hkeyDrv, OUT PHKEY phkeyResp) { LONG lErr; // Try to open the common Responses subkey. if (!OpenCommonResponsesKey(hkeyDrv, CKFLAG_OPEN, KEY_READ, phkeyResp, NULL)) { TRACE_MSG(TF_ERROR, "OpenCommonResponsesKey() failed, assume non-existent."); // Failing that, open the *old style* common Responses subkey. if (!OLD_OpenCommonResponsesKey(hkeyDrv, phkeyResp)) { // Failing that, try to open a Responses subkey in the driver node. lErr = RegOpenKeyEx(hkeyDrv, c_szResponses, 0, KEY_READ, phkeyResp); if (lErr != ERROR_SUCCESS) { TRACE_MSG(TF_ERROR, "RegOpenKeyEx() failed: %#08lx.", lErr); return (FALSE); } } } return(TRUE); } /*---------------------------------------------------------- Purpose: This function deletes a registry key and all of its subkeys. A registry key that is opened by an application can be deleted without error by another application in both Windows 95 and Windows NT. This is by design. This code makes no attempt to check or recover from partial deletions. NOTE: Adapted from sample code in the MSDN Knowledge Base article #Q142491. Returns: ERROR_SUCCESS on success WIN32 error code on error Cond: -- */ DWORD PRIVATE RegDeleteKeyNT( IN HKEY hStartKey, IN LPTSTR pKeyName) { DWORD dwRtn, dwSubKeyLength; LPTSTR pSubKey = NULL; TCHAR szSubKey[MAX_REG_KEY_LEN]; // this should be dynamic. HKEY hKey; // do not allow NULL or empty key name if (pKeyName && lstrlen(pKeyName)) { if ((dwRtn = RegOpenKeyEx(hStartKey, pKeyName, 0, KEY_ENUMERATE_SUB_KEYS | DELETE, &hKey)) == ERROR_SUCCESS) { while (dwRtn == ERROR_SUCCESS) { dwSubKeyLength = sizeof(szSubKey); dwRtn = RegEnumKeyEx( hKey, 0, // always index zero szSubKey, &dwSubKeyLength, NULL, NULL, NULL, NULL ); if (dwRtn == ERROR_NO_MORE_ITEMS) { dwRtn = RegDeleteKey(hStartKey, pKeyName); break; } else if (dwRtn == ERROR_SUCCESS) dwRtn = RegDeleteKeyNT(hKey, szSubKey); } RegCloseKey(hKey); // Do not save return code because error // has already occurred } } else dwRtn = ERROR_BADKEY; return dwRtn; } /*---------------------------------------------------------- Purpose: This function deletes the common driver key (or decrements its reference count) associated with the driver given by name. Returns: TRUE on success FALSE on error Cond: -- */ BOOL PUBLIC DeleteCommonDriverKeyByName( IN LPTSTR pszKeyName) { BOOL bRet = FALSE; // assume failure LONG lErr; TCHAR szPath[2*MAX_REG_KEY_LEN]; HKEY hkeyComDrv, hkeyPrnt; DWORD dwRefCount, cbData; // Construct the path to the driver's common key and open it. lstrcpy(szPath, DRIVER_KEY TEXT("\\")); lstrcat(szPath, pszKeyName); lErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szPath, 0, KEY_ALL_ACCESS, &hkeyComDrv); if (lErr != ERROR_SUCCESS) { TRACE_MSG(TF_ERROR, "RegOpenKeyEx() failed: %#08lx.", lErr); goto exit; } // Check the common driver key reference count and decrement // it or delete the key (& the Responses subkey). cbData = sizeof(dwRefCount); lErr = RegQueryValueEx(hkeyComDrv, c_szRefCount, NULL, NULL, (LPBYTE)&dwRefCount, &cbData); // To accomodate modems installed before this reference count // mechanism was added (post-Beta2), if the reference count doesn't // exist then just ignore it. In this case the shared Responses key // will never be removed. if (lErr == ERROR_SUCCESS) { ASSERT(dwRefCount); // expecting non-0 ref count if (--dwRefCount) { lErr = RegSetValueEx(hkeyComDrv, c_szRefCount, 0, REG_DWORD, (LPBYTE)&dwRefCount, cbData); if (lErr != ERROR_SUCCESS) { TRACE_MSG(TF_ERROR, "RegSetValueEx(RefCount) failed: %#08lx.", lErr); ASSERT(0); goto exit; } } else { lErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, DRIVER_KEY, 0, KEY_ENUMERATE_SUB_KEYS, &hkeyPrnt); if (lErr != ERROR_SUCCESS) { TRACE_MSG(TF_ERROR, "RegOpenKeyEx(Prnt) failed: %#08lx.", lErr); goto exit; } lErr = RegDeleteKeyNT(hkeyPrnt, pszKeyName); if (lErr != ERROR_SUCCESS) { TRACE_MSG(TF_ERROR, "RegDeleteKeyNT(ComDrv) failed: %#08lx.", lErr); goto exit; } } } else if (lErr != ERROR_FILE_NOT_FOUND) { // some error other than key doesn't exist TRACE_MSG(TF_ERROR, "RegQueryValueEx(RefCount) failed: %#08lx.", lErr); goto exit; } bRet = TRUE; exit: return(bRet); } /*---------------------------------------------------------- Purpose: This function deletes the common driver key (or decrements its reference count) associated with the driver given by driver key. Returns: TRUE on success FALSE on error Cond: -- */ BOOL PUBLIC DeleteCommonDriverKey( IN HKEY hkeyDrv) { BOOL bRet = FALSE; TCHAR szComDrv[MAX_REG_KEY_LEN]; // Get the name of the common driver key for this driver. if (!GetCommonDriverKeyName(hkeyDrv, NULL, sizeof(szComDrv), szComDrv)) { TRACE_MSG(TF_ERROR, "GetCommonDriverKeyName() failed."); goto exit; } if (!DeleteCommonDriverKeyByName(szComDrv)) { TRACE_MSG(TF_ERROR, "DeleteCommonDriverKey() failed."); } bRet = TRUE; exit: return(bRet); }