//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1999. // // File: C L A S S I N S T . C P P // // Contents: Defines the interface between the binding engine and the // network class installer. // // Notes: // // Author: billbe 15 Jan 1999 // //---------------------------------------------------------------------------- #include #pragma hdrstop #include "adapter.h" #include "benchmrk.h" #include "classinst.h" #include "filtdev.h" #include "netcfg.h" #include "iatl.h" #include "lockdown.h" #include "ncatl.h" #include "ncoc.h" #include "ncreg.h" #include "ncsetup.h" #include "ncui.h" #include "ncwins.h" #include "persist.h" #include "provider.h" #include "resource.h" #include "util.h" // HrRegisterNotificationDll functions enum ciRegisterDllFunction {CIRDF_REGISTER, CIRDF_UNREGISTER}; // The temporary key used for dumping a component's registry entries. This is used // during the pruning of inapplicable protocols and filter services from the select // dialog. static const WCHAR c_szRegKeyTemp[] = L"System\\CurrentControlSet\\Control\\Network\\FTempKey"; //+-------------------------------------------------------------------------- // // Function: HrCiRegDeleteComponentNetworkKey // // Purpose: This function deletes the component key strInstanceGuid // (and its subkeys) under the Network\ tree. // // Arguments: // Class [in] The class of the component // pszInstanceGuid [in] The instance guid of the component // // Returns: HRESULT. S_OK if successful, an error code otherwise. // // Author: billbe 27 Apr 1997 // // Notes: // HRESULT HrCiRegDeleteComponentNetworkKey ( IN NETCLASS Class, IN PCWSTR pszInstanceGuid) { HRESULT hr = S_OK; HKEY hkeyClass = NULL; PCWSTR pszNetworkSubtreePath = MAP_NETCLASS_TO_NETWORK_SUBTREE[Class]; // Open the proper class key in the Network tree hr = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE, pszNetworkSubtreePath, KEY_WRITE, &hkeyClass); // Delete the instance key tree // if (S_OK == hr) { hr = HrRegDeleteKeyTree(hkeyClass, pszInstanceGuid); RegSafeCloseKey(hkeyClass); } TraceHr (ttidError, FAL, hr, FALSE, "HrCiRegDeleteComponentKey"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiRegisterNotificationDll // // Purpose: Registers or Unregisters a component's notification dll with // COM // // Arguments: // hkeyInstance [in] The handle to the instance key for the component // crdf [in] CIRDF_REGISTER if we are registering, // CIRDF_UNREGISTER if we are unregistering // // Returns: HRESULT. S_OK on if dll is successfully registered, // S_FALSE, if the component has no dll to // register, error code otherwise // // Author: billbe 23 Mar 1997 // // Notes: // HRESULT HrCiRegisterNotificationDll( IN HKEY hkeyInstance, IN ciRegisterDllFunction crdf) { Assert(hkeyInstance); HKEY hkeyNdi; HRESULT hr; // Open the ndi key in the component's instance key so we can get the // Dll path. hr = HrRegOpenKeyEx (hkeyInstance, L"Ndi", KEY_READ, &hkeyNdi); if (S_OK == hr) { // Get the notification dll path tstring strDllPath; hr = HrRegQueryString (hkeyNdi, L"ComponentDLL", &strDllPath); if (S_OK == hr) { TraceTag (ttidClassInst, "Attempting to (un)register notification dll '%S'", strDllPath.c_str()); hr = (CIRDF_REGISTER == crdf) ? HrRegisterComObject (strDllPath.c_str()) : HrUnregisterComObject (strDllPath.c_str()); } else { if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) { // The presence of the value is optional, so return // S_OK if it was not found hr = S_OK; } } RegCloseKey (hkeyNdi); } TraceHr (ttidError, FAL, hr, FALSE, "HrCiRegisterNotificationDll"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiInstallServices // // Purpose: Processes any Inf service sections using strInfSection as a // base name // // Arguments: // hinfFile [in] A handle to the inf file // pszInfSection [in] The base section name // // Returns: HRESULT. S_OK if successful, error code otherwise // // Author: billbe 2 Apr 1997 // // Notes: See SetupInstallServicesFromInfSection in SetupApi for more // info. // HRESULT HrCiInstallServices( IN HINF hinfFile, IN PCWSTR pszInfSection) { Assert (IsValidHandle(hinfFile)); Assert (pszInfSection && *pszInfSection); BOOL fSuccess; WCHAR szServiceSection[_MAX_PATH]; // append .Services to the section name // swprintf (szServiceSection, L"%s.%s", pszInfSection, INFSTR_SUBKEY_SERVICES); // Process the Services section fSuccess = SetupInstallServicesFromInfSection (hinfFile, szServiceSection, 0); if (!fSuccess) { // Since the section is optional, we can ignore // ERROR_SECTION_NOT_FOUND if (ERROR_SECTION_NOT_FOUND == GetLastError()) { fSuccess = TRUE; } } // Any errors must be converted HRESULT hr = S_OK; if (!fSuccess) { hr = HrFromLastWin32Error(); } TraceHr (ttidError, FAL, hr, FALSE, "HrCiInstallServices (%S)", szServiceSection); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiInstallFromInfSection // // Purpose: A wrapper function for SetupInstallFromInfSection. This // function handles setting up the copy files process for // SetupInstallFromInfSection as well. // // Arguments: // hinfFile [in] A handle to the inf file to install from // pszInfSectionName [in] The section to install // hkeyRelative [in] The key that will be used as the section's // HKR // hwndParent [in] The HWND to the parent window, used for UI // dwInstallFlags [in] See SetupInstallFromInfSection for info on // these flags // // Returns: HRESULT. S_OK if successful, error code otherwise // // Author: billbe 4 Apr 1997 // // Notes: See SetupApi documentation for more info on // SetupInstallFromInfSection and // SetupInstallFilesFromInfSection // HRESULT HrCiInstallFromInfSection( IN HINF hinfFile, IN PCWSTR pszInfSectionName, IN HKEY hkeyRelative, IN HWND hwndParent, IN DWORD dwInstallFlags) { Assert (IsValidHandle (hinfFile)); Assert (pszInfSectionName && *pszInfSectionName); HRESULT hr = S_OK; if (dwInstallFlags & SPINST_FILES) { // The next three variables are used for SetupApi's copy files process PSP_FILE_CALLBACK pfc; PVOID pvCtx; HSPFILEQ hfq; // If the inf file has a layout entry in its version section // we need to append its information for proper locations // of any files we need to copy. If the call fails we can // still install, it just means the prompt for files will not // have the correct directory to begin with (VOID) SetupOpenAppendInfFile (NULL, hinfFile, NULL); // We need to create our own file queue so we can scan all the // files to be copied. Scanning before committing our queue will // prompt the user if the files already exist in the destination // hr = HrSetupOpenFileQueue (&hfq); if (S_OK == hr) { BOOL fInGuiModeSetup = FInSystemSetup(); hr = HrSetupInstallFilesFromInfSection (hinfFile, NULL, hfq, pszInfSectionName, NULL, 0); // Set the default callback context // If the we are in system setup, we need to make sure the // callback doesn't display UI // if (S_OK == hr) { hr = HrSetupInitDefaultQueueCallbackEx (hwndParent, (fInGuiModeSetup ? (HWND)INVALID_HANDLE_VALUE : NULL), 0, 0, NULL, &pvCtx); if (S_OK == hr) { // Not doing anything special so use SetupApi default // handler for file copy. pfc = SetupDefaultQueueCallback; // Scan the queue to see if the files are already in the // destination and if so, prune them out. DWORD dwScanResult; hr = HrSetupScanFileQueueWithNoCallback (hfq, SPQ_SCAN_FILE_VALIDITY | SPQ_SCAN_PRUNE_COPY_QUEUE, hwndParent, &dwScanResult); // Now commit the queue so any files needing to be // copied, will be. If the scan result is 1 then there // is nothing to commit. // if ((S_OK == hr) && (1 != dwScanResult)) { hr = HrSetupCommitFileQueue (hwndParent, hfq, pfc, pvCtx); } // We need to release the default context and close our // file queue // SetupTermDefaultQueueCallback (pvCtx); SetupCloseFileQueue (hfq); } } } } if ((S_OK == hr) && (dwInstallFlags & ~SPINST_FILES)) { Assert (hkeyRelative); // Now we run all sections but CopyFiles hr = HrSetupInstallFromInfSection (hwndParent, hinfFile, pszInfSectionName, (dwInstallFlags & ~SPINST_FILES), hkeyRelative, NULL, 0, NULL, NULL, NULL, NULL); } TraceHr (ttidError, FAL, hr, HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr, "HrCiInstallFromInfSection"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiDoCompleteSectionInstall // // Purpose: Runs all relevant sections of an inf file using strSection // as the base section name. // // Arguments: // hinfFile [in] SetupApi handle to an inf file // hkeyRelative [in] The registry key that will be the HKR // key during inf processing. // pszSection [in] Section name to install // hwndParent [in] The handle to the parent, for // displaying UI // fEnumerated [in] TRUE if this component is PnP enumerated // FALSE otherwise // // Returns: HRESULT. S_OK if sucessful, error code otherwise // // Author: billbe 15 Apr 1997 // // Notes: // HRESULT HrCiDoCompleteSectionInstall( IN HINF hinfFile, IN HKEY hkeyRelative, IN PCWSTR pszSection, IN HWND hwndParent, IN BOOL fEnumerated) { Assert (IsValidHandle (hinfFile)); Assert (FImplies (!fEnumerated, hkeyRelative)); HRESULT hr = S_OK; // Only do this if there is a section name to work with if (pszSection && *pszSection) { // If this is an enumerated device, the service section and // the copy files section will be processed by the Device Installer // fcn SetupDiInstallDevice so we can exclude it from the following // calls. But we do some processing based on registry and log config // entries so we will pre-run the registry section for enumerated // devices and exclude the others // // Run the section found using hkeyRelative as the HKR hr = HrCiInstallFromInfSection (hinfFile, pszSection, hkeyRelative, hwndParent, (fEnumerated ? (SPINST_REGISTRY | SPINST_LOGCONFIG) : SPINST_ALL & ~SPINST_REGSVR)); if (!fEnumerated) { // We need to run the Services section and // check for Winsock dependency if they aren't specified to be // excluded. // // Note: Other sections may be added later. The default is to // run all sections not listed in dwExcludeSectionFlags // if (S_OK == hr) { // run services section if it exists hr = HrCiInstallServices (hinfFile, pszSection); if (S_OK == hr) { // Bug #383239: Wait till services are installed before // running the RegisterDlls section // hr = HrCiInstallFromInfSection (hinfFile, pszSection, hkeyRelative, hwndParent, SPINST_REGSVR); } } } if (S_OK == hr) { //sb This part can be called for either add or remove. We //sb are moving only the remove part forward. This should //sb still be performed for add. // // Determine if a .Winsock section exists for the // section specified in szActualSection PCWSTR pszSubSection = wcsstr(pszSection, L".Remove"); if(!pszSubSection || wcscmp(pszSubSection, L".Remove")) { hr = HrAddOrRemoveWinsockDependancy (hinfFile, pszSection); } // These other extensions are undocumented and some have been // added by external groups. We don't want any of them // processed for enumerated components. // if ((S_OK == hr) && !fEnumerated) { // Process the additional INF extensions (SNMP Agent, // PrintMonitors, etc.) // hr = HrProcessAllINFExtensions (hinfFile, pszSection); } } } TraceHr (ttidError, FAL, hr, (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr), "HrCiDoCompleteSectionInstall"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiRemoveNonEnumeratedComponent // // Purpose: This will run the remove section and delete the network // instance key for the component if necessary. This // function is called for partially (i.e. failed install) // and fully installed components // // Arguments: // hinf [in] The handle to the component's inf file // hkeyInstance [in] The handle to the component's instance key // Class [in] The class of the component // InstanceGuid [in] The instance guid of the component // pstrRemoveSection [out] Optional pointer to a tstring which receives // the remove section name. // // Returns: HRESULT. S_OK if successful, NETCFG_S_REBOOT if successful // but a reboot is required, or an error code otherwise // // Author: billbe 10 Dec 1996 // Revised 27 Apr 1997 // // Notes: // HRESULT HrCiRemoveNonEnumeratedComponent( IN HINF hinf, IN HKEY hkeyInstance, IN NETCLASS Class, IN const GUID& InstanceGuid, OUT tstring* pstrRemoveSection OPTIONAL) { Assert (IsValidHandle (hinf)); Assert (IsValidHandle (hkeyInstance)); static const WCHAR c_szRemoveSectionSuffix[] = L".Remove"; // We get the remove section name and process all relevant sections // We also try to unregister any Notify objects available // WCHAR szRemoveSection[_MAX_PATH]; DWORD cbBuffer = sizeof (szRemoveSection); HRESULT hr = HrRegQuerySzBuffer (hkeyInstance, REGSTR_VAL_INFSECTION, szRemoveSection, &cbBuffer); if (S_OK == hr) { wcscat (szRemoveSection, c_szRemoveSectionSuffix); if (pstrRemoveSection) { pstrRemoveSection->assign(szRemoveSection); } hr = HrCiDoCompleteSectionInstall (hinf, hkeyInstance, szRemoveSection, NULL, NULL); } // Whether unregistering the notify object is successful or not, // we must fully remove the component. (VOID) HrCiRegisterNotificationDll (hkeyInstance, CIRDF_UNREGISTER); // Now we need to remove the component key in the Network tree // We need to do this regardless of any previous errors // so we don't need the return value. WCHAR szGuid[c_cchGuidWithTerm]; StringFromGUID2 (InstanceGuid, szGuid, c_cchGuidWithTerm); (VOID) HrCiRegDeleteComponentNetworkKey (Class, szGuid); // if all went well, set the return value based on whether a reboot // is required or not or any error from HrRegisterNotificationDll. // if (S_FALSE == hr) { // S_FALSE is okay but should not be returned by this fcn. hr = S_OK; } TraceHr (ttidError, FAL, hr, FALSE, "HrCiRemoveNonEnumeratedComponent"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiRemoveComponent // // Purpose: Called from INetCfg, this will uninstall a network component. // // Arguments: // pComponent [in] The component to uninstall. // pstrRemoveSection [out] Optional pointer to a tstring which receives // the remove section name. // // Returns: HRESULT. S_OK if successful, NETCFG_S_REBOOT if successful // but a reboot is required, or an error code otherwise // // Author: billbe 10 Dec 1996 // Revised 27 Apr 1997 // // Notes: // HRESULT HrCiRemoveComponent( IN const CComponent* pComponent, OUT tstring* pstrRemoveSection OPTIONAL) { Assert (pComponent); HINF hinf = NULL; HDEVINFO hdi = NULL; SP_DEVINFO_DATA deid; HKEY hkeyInstance = NULL; HRESULT hr = S_OK; // If this is an enumerated net class component, then we need to // create the Device Installer structures for HrSetupDiRemoveDevice // if (FIsEnumerated (pComponent->Class())) { if (pComponent->m_dwCharacter & NCF_PHYSICAL) { // The binding engine calls us to remove physical devices // only when we need to potentially cleanup the information // we saved away when the class installer removed the device. // This happens when the class installer is told to remove // the device (which it does) and then notifies the binding // engine to remove it from its data structures. The binding // engine then calls this method to cleanup this info we // set so that the binding engine could notify components of // its removal. // // We can also be called here when a physical component is // removed (with the binding engine write lock held by someone) // and then readded immediately. The new component will get // the same PnpId as the removed one but the bindng engine still // has the removed component in its structures. When this // condition is detected, the binding engine will remove the // old instance (by calling us here). In this case, if we were // to open the device info on pComponent->m_pszPnpId, we'd open // the new instance that was added. We don't want to do this. // We just want to cleanup any of the information that we set // for the binding engine when we first removed the device. // HKEY hkeyComponent; hr = HrRegOpenKeyEx (HKEY_LOCAL_MACHINE, c_szTempNetcfgStorageForUninstalledEnumeratedComponent, KEY_WRITE, &hkeyComponent); if (S_OK == hr) { WCHAR szGuid[c_cchGuidWithTerm]; INT cch = StringFromGUID2 (pComponent->m_InstanceGuid, szGuid, c_cchGuidWithTerm); Assert (c_cchGuidWithTerm == cch); (VOID) HrRegDeleteKeyTree (hkeyComponent, szGuid); RegCloseKey (hkeyComponent); } } else { // Create a device info list hr = HrOpenDeviceInfo (pComponent->Class(), pComponent->m_pszPnpId, &hdi, &deid); if (S_OK == hr) { // removals must go through device installer // hook (NetClassInstaller). The function we are // in can only be called if the caller has the write lock // so we need to indicate this to the device installer hook // through our reserved data. ADAPTER_REMOVE_PARAMS arp = {0}; CiSetReservedField (hdi, &deid, &arp); // removals must go through device installer // hook (NetClassInstaller). hr = HrSetupDiCallClassInstaller (DIF_REMOVE, hdi, &deid); // clear the reserved field so we don't delete it later CiClearReservedField (hdi, &deid); if (S_OK == hr) { hr = FSetupDiCheckIfRestartNeeded (hdi, &deid) ? NETCFG_S_REBOOT : S_OK; #ifdef ENABLETRACE if (NETCFG_S_REBOOT == hr) { TraceTag (ttidClassInst, "***********************************" "**************************************************"); TraceTag (ttidClassInst, "The component %S needs a reboot " "in order to function", pComponent->m_pszPnpId); TraceTag (ttidClassInst, "***********************************" "**************************************************"); } #endif //ENABLETRACE } SetupDiDestroyDeviceInfoList (hdi); } } } else { // For non enumerated components, the instance key is the // component key hr = pComponent->HrOpenInstanceKey (KEY_ALL_ACCESS, &hkeyInstance, NULL, NULL); if (S_OK == hr) { if (NC_NETCLIENT == pComponent->Class ()) { hr = HrCiDeleteNetProviderInfo (hkeyInstance, NULL, NULL); } if (S_OK == hr) { hr = pComponent->HrOpenInfFile(&hinf); if( S_OK == hr ) { // Remove the component hr = HrCiRemoveNonEnumeratedComponent ( hinf, hkeyInstance, pComponent->Class(), pComponent->m_InstanceGuid, pstrRemoveSection); } } } RegSafeCloseKey (hkeyInstance); } TraceHr (ttidError, FAL, hr, NETCFG_S_REBOOT == hr, "HrCiRemoveComponent (%S)", pComponent->PszGetPnpIdOrInfId()); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiGetDriverInfo // // Purpose: Finds a component's driver information (in the inf file) and // creates a Device Info Data structure containing that // information as the structure's selected driver. // (see Device Installer Api for more info). // // Arguments: // hdi [in] See Device Installer Api documentation for more info. // pdeid [in, out] See Device Installer Api documentation for // more info. Should be allocated by caller, but empty. // guidClass [in] The class guid for the component. // pszInfId [in] The id of the component as found in its inf. // pszInfFile [in] Optional. The inf file for the component. // // Returns: HRESULT. S_OK if successful, error code otherwise. // // Author: billbe 11 Mar 1997 // // Notes: // HRESULT HrCiGetDriverInfo ( IN HDEVINFO hdi, IN OUT PSP_DEVINFO_DATA pdeid, IN const GUID& guidClass, IN PCWSTR pszInfId, IN PCWSTR pszInfFile OPTIONAL) { HRESULT hr; Assert (IsValidHandle (hdi)); Assert (pdeid); Assert (pszInfId); // Copy the Id since we may need to change it. // WCHAR szId[_MAX_PATH]; wcscpy (szId, pszInfId); // We cannot generate ids via HrSetupDiCreateDeviceInfo if they contain // slashes (e.g. Eisa\*pnp0232), so we need to convert any slashes in // the instance id to ampersands. // int iPos = 0; while (szId[iPos]) { if (L'\\' == szId[iPos]) { szId[iPos] = L'&'; } ++iPos; } // First, create a [temporary] device info. This will be used to // find the component's Inf file. hr = HrSetupDiCreateDeviceInfo (hdi, szId, guidClass, NULL, NULL, DICD_GENERATE_ID, pdeid); if (S_OK == hr) { // In order to find the Inf file, Device Installer Api needs the // component id which it calls the Hardware id. // // We need to include an extra null since this registry value is a // multi-sz // wcsncpy (szId, pszInfId, iPos); szId[iPos + 1] = 0; hr = HrSetupDiSetDeviceRegistryProperty (hdi, pdeid, SPDRP_HARDWAREID, (const BYTE*)szId, CbOfSzAndTerm (szId) + sizeof(WCHAR)); if (S_OK == hr) { // Get the install params and set the class for compat flag // This will use the device's class guid as a filter when // searching through infs, speeding things up. We can also // let Device Installer Api know that we want to use a single // inf. if we can't get the params and set it it isn't an error // since it only slows things down a bit // SP_DEVINSTALL_PARAMS deip; hr = HrSetupDiGetDeviceInstallParams (hdi, pdeid, &deip); if (S_OK == hr) { deip.FlagsEx |= DI_FLAGSEX_USECLASSFORCOMPAT; // If we were not given an inf file to use... // We have a map of known components and their inf files. // If this component is in the map then we can set the // driver path in the device info data and // set the enumerate a single inf flag. This will // cause the device installer to just look at the specified // inf file for the driver node. // // We only do this if the node doesn't already have a file // name set. // if (!(*deip.DriverPath)) { if (pszInfFile && *pszInfFile) { wcscpy (deip.DriverPath, pszInfFile); } else { FInfFileFromComponentId (pszInfId, deip.DriverPath); } } if (*deip.DriverPath) { TraceTag (ttidClassInst, "Class Installer was given %S " "as a filename for %S", deip.DriverPath, pszInfId); deip.Flags |= DI_ENUMSINGLEINF; if ((0 == _wcsicmp(L"netrasa.inf", deip.DriverPath)) || (0 == _wcsicmp(L"netpsa.inf", deip.DriverPath))) { deip.Flags |= DI_NOFILECOPY; } } // For non-device classes, we need to allow excluded // drivers in order to get any driver list returned. if (!FIsEnumerated (guidClass)) { deip.FlagsEx |= DI_FLAGSEX_ALLOWEXCLUDEDDRVS; } (VOID) HrSetupDiSetDeviceInstallParams (hdi, pdeid, &deip); } // Now we let Device Installer Api build a driver list based on // the information we have given so far. This will result in the // Inf file being found if it exists in the usual Inf directory // #ifdef ENABLETRACE CBenchmark bmrk; bmrk.Start ("SetupDiBuildDriverInfoList"); #endif //ENABLETRACE hr = HrSetupDiBuildDriverInfoList (hdi, pdeid, SPDIT_COMPATDRIVER); #ifdef ENABLETRACE bmrk.Stop(); TraceTag (ttidBenchmark, "%s : %s seconds", bmrk.SznDescription(), bmrk.SznBenchmarkSeconds (2)); #endif //ENABLETRACE if (S_OK == hr) { // HrSetupDiSelectBestCompatDrv finds and selects the best // driver for the device. // SP_DRVINFO_DATA drid; hr = HrSetupDiSelectBestCompatDrv(hdi, pdeid); if (HRESULT_FROM_SETUPAPI(ERROR_NO_COMPAT_DRIVERS) == hr) { // Make the ERROR_NO_COMPAT_DRIVERS case look like what // it really means -- the requested component's driver // info (i.e. inf) could not be found. // hr = SPAPI_E_NO_DRIVER_SELECTED; } } else if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) { // We couldn't find an inf file which means we cannot // selected the driver for this component. // hr = SPAPI_E_NO_DRIVER_SELECTED; } } // if anything failed, we should remove the device node we created if (FAILED(hr)) { (VOID) SetupDiDeleteDeviceInfo (hdi, pdeid); } } TraceHr (ttidError, FAL, hr, FALSE, "HrCiGetDriverInfo"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiGetClassAndInfFileOfInfId // // Purpose: Finds a component's class and inf file. // // Arguments: // pszInfId [in] The Id of the component as found in its Inf. // pClass [out] The class of the component. // pszInfFile [out] The filename of the component's inf // (must be _MAX_PATH long). // // Returns: HRESULT. S_OK if successful, error code otherwise. // // Author: billbe 16 Mar 1998 // // Notes: // HRESULT HrCiGetClassAndInfFileOfInfId ( IN PCWSTR pszInfId, OUT NETCLASS* pClass, OUT PWSTR pszInfFile) // Must be _MAX_PATH long { HRESULT hr; const COMPONENT_INFO* pCompInfo; HDEVINFO hdi; Assert (pszInfId && *pszInfId); Assert (pClass); Assert (pszInfFile); hr = S_OK; // First, try the fast route by seeing if it's in our internal map. // pCompInfo = PComponentInfoFromComponentId (pszInfId); if (pCompInfo) { *pClass = NetClassEnumFromGuid (*pCompInfo->pguidClass); if (FIsValidNetClass (*pClass)) { wcsncpy (pszInfFile, pCompInfo->pszInfFile, _MAX_PATH); pszInfFile [_MAX_PATH - 1] = 0; } else { hr = SPAPI_E_INVALID_CLASS; } } else { // Create a device info list. // hr = HrSetupDiCreateDeviceInfoList (NULL, NULL, &hdi); if (S_OK == hr) { SP_DEVINFO_DATA deid; // Get the driver info for the component and set it as the // selected driver // hr = HrCiGetDriverInfo (hdi, &deid, GUID_NULL, pszInfId, NULL); if (S_OK == hr) { SP_DRVINFO_DATA drid; // Get the selected driver. // hr = HrSetupDiGetSelectedDriver (hdi, &deid, &drid); if (S_OK == hr) { // Set the class output parameter from the dev info data // structure (HrGetDriverInfo updates this field if a driver // was found) // *pClass = NetClassEnumFromGuid (deid.ClassGuid); if (!FIsValidNetClass (*pClass)) { hr = SPAPI_E_INVALID_CLASS; } else { PSP_DRVINFO_DETAIL_DATA pdridd; // Now get the driver's detailed information // hr = HrCiGetDriverDetail (hdi, &deid, &drid, &pdridd); if (S_OK == hr) { // Get the inf filename and set the // output parameter. // wcsncpy (pszInfFile, pdridd->InfFileName, _MAX_PATH); pszInfFile[_MAX_PATH - 1] = 0; MemFree (pdridd); } } } } SetupDiDestroyDeviceInfoList (hdi); } } if (S_OK != hr) { *pClass = NC_INVALID; *pszInfFile = 0; } TraceHr (ttidError, FAL, hr, FALSE, "HrCiGetClassAndInfFileOfInfId"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiGetDriverDetail // // Purpose: Creates and fills a PSP_DRVINFO_DETAIL_DATA structure // with detailed information about the pDevInfoData's // selected driver // // Arguments: // hdi [in] See Device Installer Api documentation for more info // pdeid [in] See Device Installer Api documentation for more info // This value is NULL for non-physical net // components. // pdrid [in] See Device Installer Api documentation for more info // ppdridd [out] See Device Installer Api documentation for more info // // Returns: HRESULT. S_OK if successful, error code otherwise // // Author: billbe 11 Mar 1997 // // Notes: // HRESULT HrCiGetDriverDetail ( IN HDEVINFO hdi, IN PSP_DEVINFO_DATA pdeid OPTIONAL, OUT PSP_DRVINFO_DATA pdrid, OUT PSP_DRVINFO_DETAIL_DATA* ppdridd) { Assert(IsValidHandle(hdi)); Assert(pdrid); Assert(ppdridd); // initialize pdrid and set its cbSize field ZeroMemory (pdrid, sizeof (SP_DRVINFO_DATA)); pdrid->cbSize = sizeof (SP_DRVINFO_DATA); HRESULT hr = S_OK; *ppdridd = NULL; // Get the selected driver for the component hr = HrSetupDiGetSelectedDriver (hdi, pdeid, pdrid); if (S_OK == hr) { // Get driver detail info hr = HrSetupDiGetDriverInfoDetail (hdi, pdeid, pdrid, ppdridd); } // clean up on failure if (FAILED(hr)) { if (*ppdridd) { MemFree (*ppdridd); *ppdridd = NULL; } } TraceHr (ttidError, FAL, hr, FALSE, "HrCiGetDriverDetail"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiRegSetComponentInformation // // Purpose: Stores component information under the instance key of // the component. // // Arguments: // hkeyInstance [in] Component's instance registry key. // pcii [in] Component's information to store in hkeyInstance. // // Returns: HRESULT. S_OK if successful, error code otherwise. // // Author: billbe 11 Mar 1997 // // Notes: // HRESULT HrCiRegSetComponentInformation( IN HKEY hkeyInstance, IN COMPONENT_INSTALL_INFO* pcii) { Assert(hkeyInstance); Assert(pcii); HRESULT hr = S_OK; BOOL fIsEnumerated = FIsEnumerated (pcii->Class); // Store the characteristics, inf path, and main // install section for the component // hr = HrRegSetDword (hkeyInstance, L"Characteristics", pcii->dwCharacter); if (FAILED(hr)) { goto exit; } if (!fIsEnumerated) { hr = HrRegSetSz (hkeyInstance, L"InfPath" /*REGSTR_VAL_INFPATH*/, pcii->pszInfFile); if (FAILED(hr)) { goto exit; } hr = HrRegSetSz (hkeyInstance, L"InfSection"/*REGSTR_VAL_INFSECTION*/, pcii->pszSectionName); if (FAILED(hr)) { goto exit; } } // For non-enumerated components, store description into the registry. // if (!fIsEnumerated) { hr = HrRegSetSz (hkeyInstance, L"Description", pcii->pszDescription); if (FAILED(hr)) { goto exit; } } // If this component is already installed, then there is no need to write // the following information // if (FIsEnumerated (pcii->Class) && !pcii->fPreviouslyInstalled && FIsPhysicalAdapter (pcii->Class, pcii->dwCharacter) && (InterfaceTypeUndefined != pcii->BusType)) { hr = HrRegSetSzAsUlong (hkeyInstance, L"BusType", pcii->BusType, c_nBase10); if (FAILED(hr)) { goto exit; } } hr = HrRegSetSz (hkeyInstance, L"ComponentId", pcii->pszInfId); exit: TraceHr (ttidError, FAL, hr, FALSE, "HrCiRegSetComponentInformation"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiCreateInstanceKey // // Purpose: Creates an instance key for the component. For enumerated // devices, this is // HKLM\System\CCS\Control\Class\\ // For non-enumerated components, this is under // HKLM\System\CCS\Control\Network\\ // // Arguments: // pcii [inout] Component install info structure. // phkeyInstance [out] The component's registry instance key. // // Returns: HRESULT // // Author: billbe 22 Mar 1997 // // Notes: // HRESULT HrCiCreateInstanceKey( IN COMPONENT_INSTALL_INFO* pcii, OUT HKEY* phkeyInstance) { Assert (pcii); Assert (phkeyInstance); Assert (FImplies (FIsEnumerated (pcii->Class), IsValidHandle (pcii->hdi) && pcii->pdeid)); HRESULT hr = S_OK; // initialize the HKEY parameter *phkeyInstance = NULL; // Create the instance key for this component under the // Network\ tree. This will be the component's // instance key for all but physical net class components. Their // instance key is created by Device Installer Api and lives under the // Pnp Net Class driver tree. // If the object is an enumerated component then we let // the Device Installer api do the work // if (FIsEnumerated (pcii->Class)) { // We need to create the adapter's driver key under // the Pnp Net Class Driver tree. // hr = HrSetupDiCreateDevRegKey (pcii->hdi, pcii->pdeid, DICS_FLAG_GLOBAL, 0, DIREG_DRV, NULL, NULL, phkeyInstance); } else { // Not a physical net adapter so the component key is // the instance key // First, create the instance GUID hr = CoCreateGuid (&pcii->InstanceGuid); // Now create the key if (S_OK == hr) { WCHAR szInstanceKeyPath[_MAX_PATH]; CreateInstanceKeyPath (pcii->Class, pcii->InstanceGuid, szInstanceKeyPath); hr = HrRegCreateKeyEx (HKEY_LOCAL_MACHINE, szInstanceKeyPath, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, phkeyInstance, NULL); } } TraceHr (ttidError, FAL, hr, FALSE, "HrCiCreateInstanceKey"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiGetPropertiesFromInf // // Purpose: Retrieves a set of the component's proerties from the inf // file. // // Arguments: // hinfFile [in] A handle to the component's inf file // pcii [inout] The component info structure // See compinfo.h for more info // // Returns: HRESULT. S_OK if successful, error code otherwise // // Author: billbe 14 Jun 1997 // // Notes: // HRESULT HrCiGetPropertiesFromInf ( IN HINF hinfFile, IN OUT COMPONENT_INSTALL_INFO* pcii) { Assert (IsValidHandle (hinfFile)); Assert (pcii); Assert (pcii->pszSectionName); // Find the inf line that contains Characteristics and retrieve it HRESULT hr = HrSetupGetFirstDword (hinfFile, pcii->pszSectionName, L"Characteristics", &pcii->dwCharacter); if ((S_OK == hr) && (FIsPhysicalAdapter(pcii->Class, pcii->dwCharacter))) { hr = HrCiGetBusInfoFromInf (hinfFile, pcii); } #ifdef DBG else if (FAILED(hr)) { TraceTag(ttidError, "Inf contains no Characteristics field"); } #endif // DBG TraceHr (ttidError, FAL, hr, FALSE, "HrCiGetPropertiesFromInf"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiIsInstalledComponent // // Purpose: Checks if the component is already installed // // // Arguments: // pcici [in] A structure containing the component information // See compinst.h for definition // phkey [out] The registry instance key of the adapter // during inf processing. only set if fcn returns S_OK // // Returns: HRESULT - S_OK if the component is already installed // S_FALSE if the component is not already installed // A win32 converted error otherwise // // Author: billbe 17 Sep 1997 // // Notes: // HRESULT HrCiIsInstalledComponent ( IN COMPONENT_INSTALL_INFO* pcii, OUT HKEY* phkey) { HRESULT hr; Assert(pcii); if (phkey) { *phkey = NULL; } // If this is an enumerated component, we just check for NetCfgInstanceId // in the instance (driver) key. // if (FIsEnumerated (pcii->Class)) { HKEY hkey; hr = HrSetupDiOpenDevRegKey (pcii->hdi, pcii->pdeid, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_ALL_ACCESS, &hkey); if (S_OK == hr) { WCHAR szGuid[c_cchGuidWithTerm]; DWORD cbGuid = sizeof (szGuid); hr = HrRegQuerySzBuffer (hkey, L"NetCfgInstanceId", szGuid, &cbGuid); if (S_OK == hr) { IIDFromString (szGuid, &pcii->InstanceGuid); if (phkey) { *phkey = hkey; } } else { RegCloseKey (hkey); hr = S_FALSE; } } else if ((SPAPI_E_KEY_DOES_NOT_EXIST == hr) || (SPAPI_E_DEVINFO_NOT_REGISTERED == hr)) { TraceTag(ttidClassInst, "Component is not known by Net Config"); hr = S_FALSE; } } else { // For non-enumerated components, we check the netcfg "config blob" to // determine if this component is isntalled. CNetConfig NetConfig; hr = HrLoadNetworkConfigurationFromRegistry (KEY_READ, &NetConfig); if (S_OK == hr) { CComponent* pComponent; pComponent = NetConfig.Core.Components. PFindComponentByInfId(pcii->pszInfId, NULL); if (pComponent) { pcii->InstanceGuid = pComponent->m_InstanceGuid; if (phkey) { hr = pComponent->HrOpenInstanceKey(KEY_ALL_ACCESS, phkey, NULL, NULL); } } else { hr = S_FALSE; } } } TraceHr (ttidError, FAL, hr, S_FALSE == hr, "HrCiIsInstalledComponent"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiCreateInstanceKeyAndProcessMainInfSection // // Purpose: Processes a component's main inf section and // storing, in the registry, any extra information needed for // component initialization // // Arguments: // hinf [in] Handle to the component's inf file. // pcii [inout] Will be filled with information about the // component. // phkey [out] Handle to the component's registry instance key. // // // Returns: HRESULT. S_OK if successful, error code otherwise // // Author: billbe 15 Nov 1996 // // Notes: // HRESULT HrCiCreateInstanceKeyAndProcessMainInfSection( IN HINF hinf, IN COMPONENT_INSTALL_INFO* pcii, OUT HKEY* phkey) { #if defined(REMOTE_BOOT) GUID c_guidRemoteBoot; static const WCHAR c_szRemoteBootAdapterGuid[] = L"{54C7D140-09EF-11D1-B25A-F5FE627ED95E}"; DEFINE_GUID(c_guidRemoteBoot, 0x54c7d140, 0x09ef, 0x11d1, 0xb2, 0x5a, 0xf5, 0xfe, 0x62, 0x7e, 0xd9, 0x5e); #endif // defined(REMOTE_BOOT) Assert (IsValidHandle (hinf)); Assert (pcii); Assert (phkey); // The properties retrieved here will be written to the registry // later. HRESULT hr = HrCiGetPropertiesFromInf (hinf, pcii); if (S_OK == hr) { BOOL fEnumerated = FIsEnumerated (pcii->Class); // If this component is enumerated, then we need to know if it // is a remote boot adapter. if (fEnumerated) { Assert (IsValidHandle (pcii->hdi)); Assert (pcii->pdeid); #if defined(REMOTE_BOOT) // If this adapter is a remote boot adapter, then we have // to use a pre-determined GUID // if (S_OK == HrIsRemoteBootAdapter(pcii->hdi, pcii->pdeid)) { pcai->m_fRemoteBoot = TRUE; pcii->InstanceGuid = c_guidRemoteBoot; } #endif // defined(REMOTE_BOOT) } // Is this a fresh install or a reinstall? hr = HrCiIsInstalledComponent(pcii, phkey); if (S_FALSE == hr) { hr = S_OK; // Fresh install // if (S_OK == hr) { // For non-physical components, the relative key will // be the driver instance key which is under the class // branch of the Network key. Its form is // /. // For physical components, the key is under // the Pnp class driver tree. The next call will // create this key hr = HrCiCreateInstanceKey(pcii, phkey); if (fEnumerated) { // If we don't have an instance // guid (i.e. not remote boot adapter), // get one if (GUID_NULL == pcii->InstanceGuid) { hr = CoCreateGuid(&pcii->InstanceGuid); #ifdef ENABLETRACE WCHAR szGuid[c_cchGuidWithTerm]; StringFromGUID2(pcii->InstanceGuid, szGuid, c_cchGuidWithTerm); TraceTag(ttidClassInst, "NetCfg Instance Guid %S " "generated for %S", szGuid, pcii->pszInfId); #endif // ENABLETRACE } } } } else if (S_OK == hr) { // This component is being reinstalled pcii->fPreviouslyInstalled = TRUE; } if (S_OK == hr) { // Now that the instance key is created, we need to run // the main inf sections // hr = HrCiDoCompleteSectionInstall(hinf, *phkey, pcii->pszSectionName, pcii->hwndParent, fEnumerated); // On failure of fresh installs, remove components if (FAILED(hr) && !pcii->fPreviouslyInstalled) { if (!fEnumerated) { HrCiRemoveNonEnumeratedComponent (hinf, *phkey, pcii->Class, pcii->InstanceGuid, NULL); } } } } TraceHr (ttidError, FAL, hr, FALSE, "HrCiCreateInstanceKeyAndProcessMainInfSection"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiDoOemFileCopyIfNeeded // // Purpose: Calls HrSetupCopyOemInf if strInfPath is not already in the // inf directory. This will copy an Oem inf to the inf // directory with a new name. // // Arguments: // pszInfPath [in] Path to the inf file // pszNewName [out] The new name of the copied inf file // // Returns: HRESULT. S_OK if successful, error code otherwise // // Author: billbe 15 May 1997 // // Notes: // HRESULT HrCiDoOemFileCopyIfNeeded( IN PCWSTR pszInfPath, OUT PWSTR pszNewName) { Assert (pszInfPath); Assert (pszNewName); HRESULT hr = S_OK; WCHAR szInfDir[_MAX_PATH] = {0}; // fill buffer with path to %windir% GetSystemWindowsDirectory (szInfDir, _MAX_PATH); // the inf directory is %windir%\inf // wcscat (szInfDir, L"\\"); wcscat (szInfDir, L"Inf"); // Extract the directory from the filename // PWSTR pszEnd = wcsrchr (pszInfPath, L'\\'); DWORD cch; if (pszEnd) { cch = (DWORD)(pszEnd - pszInfPath); } else { cch = wcslen (pszInfPath); } // if the inf is not already in the inf directory, copy it there // if ((cch != wcslen (szInfDir)) || (0 != _wcsnicmp (pszInfPath, szInfDir, cch))) { WCHAR szDestFilePath[_MAX_PATH]; PWSTR pszDestFilename; hr = HrSetupCopyOemInfBuffer (pszInfPath, NULL, SPOST_PATH, 0, szDestFilePath, _MAX_PATH, &pszDestFilename); if (S_OK == hr) { wcscpy (pszNewName, pszDestFilename); } } else { // The inf is already in the right directory so just copy the // current filename component. if (pszEnd) { wcscpy (pszNewName, pszEnd + 1); } else { wcscpy (pszNewName, pszInfPath); } } TraceHr (ttidError, FAL, hr, FALSE, "HrCiDoOemFileCopyIfNeeded"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiInstallNonEnumeratedComponent // // Purpose: This function completes the installation of a non-enumerated // component // // Arguments: // hinf [in] SetupApi handle to an inf file // hkey [in] The registry instance key of the adapter // during inf processing. // pcii [in] A structure containing the component information // // Returns: HRESULT. S_OK if successful, or error code otherwise // // Author: billbe 28 Apr 1997 // // Notes: // HRESULT HrCiInstallNonEnumeratedComponent ( IN HINF hinf, IN HKEY hkey, IN COMPONENT_INSTALL_INFO* pcii) { // Register the notification dll for this component, // if it exists. HRESULT hr = HrCiRegisterNotificationDll(hkey, CIRDF_REGISTER); // Device Installer Api handles OEM files for // enumerated components in InstallDevice // Since this component is non-enumerated // we need to handle any oem files // manually. // // Copy the inf file if needed then store // the new inf name // Note: if the inf file does not need to // be copied, the new name will be the // old name without the directory info. // if (S_OK == hr) { WCHAR szNewName[_MAX_PATH];; hr = HrCiDoOemFileCopyIfNeeded (pcii->pszInfFile, szNewName); if (S_OK == hr) { // set the new path value in the registry. hr = HrRegSetSz (hkey, REGSTR_VAL_INFPATH, szNewName); if ((S_OK == hr) && (NC_NETCLIENT == pcii->Class)) { // if this is a network client, do appropriate processing. hr = HrCiAddNetProviderInfo (hinf, pcii->pszSectionName, hkey, pcii->fPreviouslyInstalled); } } } // On failures of first time installs, remove the component. // if (FAILED(hr) && !pcii->fPreviouslyInstalled) { TraceTag (ttidClassInst, "Install canceled. Removing..."); (VOID) HrCiRemoveNonEnumeratedComponent (hinf, hkey, pcii->Class, pcii->InstanceGuid, NULL); } TraceHr (ttidError, FAL, hr, FALSE, "HrCiInstallNonEnumeratedComponent"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiInstallComponentInternal // // Purpose: Installs a component // // Arguments: // pcii [in, out] Will be filled with information about the // component. // // Returns: HRESULT. S_OK if successful, error code otherwise // // Author: billbe 15 Nov 1996 // // Notes: // HRESULT HrCiInstallComponentInternal ( IN OUT COMPONENT_INSTALL_INFO* pcii) { HRESULT hr = S_OK; HINF hinfInstallFile = NULL; HKEY hkeyInstance = NULL; TraceTag (ttidClassInst, "Installing %S from %S", pcii->pszInfId, pcii->pszInfFile); // Open the component's inf file. hr = HrSetupOpenInfFile (pcii->pszInfFile, NULL, INF_STYLE_WIN4, NULL, &hinfInstallFile); // Continue only if we have opened the file. if (S_OK == hr) { // The section we have currently might need to be decorated // with OS and Platform specific suffixes. The next call // will return the actual decorated section name or our // current section name if the decorated one does not exist. // // Store the original section name pointer so we can restore // it after we have finished. PCWSTR pszOriginalSectionName = pcii->pszSectionName; // Now we get the actual section name. // WCHAR szActualSection[_MAX_PATH]; hr = HrSetupDiGetActualSectionToInstallWithBuffer (hinfInstallFile, pcii->pszSectionName, szActualSection, _MAX_PATH, NULL, NULL); if (S_OK == hr) { pcii->pszSectionName = szActualSection; hr = HrCiCreateInstanceKeyAndProcessMainInfSection ( hinfInstallFile, pcii, &hkeyInstance); if (S_OK == hr) { // Set up the registry with the component info. hr = HrCiRegSetComponentInformation (hkeyInstance, pcii); if (S_OK == hr) { // We do different things during install based // on whether PnP knows about the component // (i.e. enumerated) or not. if (FIsEnumerated (pcii->Class)) { hr = HrCiInstallEnumeratedComponent ( hinfInstallFile, hkeyInstance, *pcii); } else { hr = HrCiInstallNonEnumeratedComponent ( hinfInstallFile, hkeyInstance, pcii); } } RegSafeCloseKey(hkeyInstance); } // set the section name back pcii->pszSectionName = pszOriginalSectionName; } SetupCloseInfFile(hinfInstallFile); } TraceHr (ttidError, FAL, hr, FALSE, "HrCiInstallComponentInternal"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiCallClassInstallerToInstallComponent // // Purpose: This function invokes the class installer to install an // enumerated component. // // Arguments: // hdi [in] See Device Installer docs for more info. // pdeid [in] // // Returns: HRESULT. S_OK if successful, or error code otherwise // // Author: billbe 28 Apr 1997 // // Notes: This should only be called while the INetCfg lock is held. // HRESULT HrCiCallClassInstallerToInstallComponent(HDEVINFO hdi, PSP_DEVINFO_DATA pdeid) { HRESULT hr; Assert(IsValidHandle(hdi)); Assert(pdeid); // We need to register the device before we do any work on it. hr = HrSetupDiCallClassInstaller (DIF_REGISTERDEVICE, hdi, pdeid); if (S_OK == hr) { // Check if we can install of this component. i.e. is the inf // a valid Windows 2000 inf. hr = HrSetupDiCallClassInstaller (DIF_ALLOW_INSTALL, hdi, pdeid); if (S_OK == hr) { BOOL fFileCopy = TRUE; SP_DEVINSTALL_PARAMS deip; // Fu fu fu: SetupApi is ignoring DI_NOFILECOPY so we'll overcome // their shortcomings and do it ourselves. // hr = HrSetupDiGetDeviceInstallParams (hdi, pdeid, &deip); if (S_OK == hr) { if (deip.Flags & DI_NOFILECOPY) { fFileCopy = FALSE; } } if (fFileCopy) { // Install needed files. hr = HrSetupDiCallClassInstaller (DIF_INSTALLDEVICEFILES, hdi, pdeid); } if (S_OK == hr) { // Now that all files have been copied, we need to set the // no file copy flag. Otherwise, setupapi will try to copy // files at each dif code. This results in multiple (1 per // dif code) unsigned driver popups if the driver was // unsigned. // We'll only do this if the no copy file flag wasn't already // set. i.e. if fFileCopy is TRUE. // if (fFileCopy) { // An error here isn't bad enough to stop installation. // HRESULT hrTemp; hrTemp = HrSetupDiSetDeipFlags (hdi, pdeid, DI_NOFILECOPY, SDDFT_FLAGS, SDFBO_OR); TraceHr (ttidError, FAL, hrTemp, FALSE, "HrCiCallClassInstallerToInstallComponent: " "HrSetupDiSetDeipFlags"); } // Device co-installers need to be registered at this point // so they can do work. hr = HrSetupDiCallClassInstaller (DIF_REGISTER_COINSTALLERS, hdi, pdeid); if (S_OK == hr) { hr = HrSetupDiCallClassInstaller (DIF_INSTALLINTERFACES, hdi, pdeid); if (S_OK == hr) { hr = HrSetupDiCallClassInstaller (DIF_INSTALLDEVICE, hdi, pdeid); } } } } // If we failed for any reason, we need to clean up since // we initiated this install. if (FAILED(hr)) { ADAPTER_REMOVE_PARAMS arp; arp.fBadDevInst = TRUE; arp.fNotifyINetCfg = FALSE; CiSetReservedField (hdi, pdeid, &arp); HrSetupDiCallClassInstaller (DIF_REMOVE, hdi, pdeid); CiClearReservedField (hdi, pdeid); } } TraceHr (ttidError, FAL, hr, FALSE, "HrCiCallClassInstallerToInstallComponent"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiInstallComponent // // Purpose: This function takes a Network Component's Id and its class // guid and gathers the information needed by // HrCiInstallComponent. Since it is called from INetCfg, we // have the write lock // // Arguments: // Params [in] Component install params. See install.h // ppComponent [out] A created CComponent representing the installed // component. // pdwNewCharacter [out] Optional pointer to a DWORD to receive the // characteristics of the component. // // Returns: HRESULT. S_OK is successful, NETCFG_S_REBOOT if a reboot is // needed to start the device, or an error code // // Author: billbe 16 Mar 1997 // // Notes: // HRESULT HrCiInstallComponent ( IN const COMPONENT_INSTALL_PARAMS& Params, OUT CComponent** ppComponent, OUT DWORD* pdwNewCharacter) { Assert (FIsValidNetClass (Params.Class)); Assert (Params.pszInfId && *Params.pszInfId); Assert (!Params.pComponent); HRESULT hr = S_OK; HDEVINFO hdi = NULL; SP_DEVINFO_DATA deid; const GUID* pguidClass = MAP_NETCLASS_TO_GUID[Params.Class]; if (ppComponent) { *ppComponent = NULL; } if (pdwNewCharacter) { *pdwNewCharacter = 0; } // If we're about to install the component, it better not be in // lockdown. // Assert (!FIsComponentLockedDown (Params.pszInfId)); // First create a device info set hr = HrSetupDiCreateDeviceInfoList (pguidClass, NULL, &hdi); if (S_OK == hr) { // This will create an node in the enum tree for this component. // If it is enumerated, we will register it which will make // it persist. If non-enumerated, we will not register it and // the node will be deleted in the call to SetDiDestroyDeviceInfoList. // hr = HrCiGetDriverInfo (hdi, &deid, *pguidClass, Params.pszInfId, Params.pszInfFile); // Get the driver info for the component if (S_OK == hr) { BASIC_COMPONENT_DATA Data = {0}; Data.Class = Params.Class; Data.pszInfId = Params.pszInfId; if (FIsEnumerated (Params.Class)) { // If the component is enumerated, we will need a place to // store its pnp id. WCHAR szPnpId[MAX_DEVICE_ID_LEN]; ADAPTER_OUT_PARAMS AdapterOutParams; ZeroMemory (&AdapterOutParams, sizeof(AdapterOutParams)); // Net class components have to go through the device // installer hook (aka NetClassInstaller) // if (FInSystemSetup()) { // if we are in GUI mode we need to make the // device install quiet and always copy from // the source location even if the files are // already present. We also need to set // the in system setup flag. This is what // syssetup would do if it initiated the install // so INetCfg initiated installs must do the same. // // We should also set the parent hwnd. // SP_DEVINSTALL_PARAMS deip; HRESULT hrTemp = HrSetupDiGetDeviceInstallParams ( hdi, &deid, &deip); if (S_OK == hr) { deip.hwndParent = Params.hwndParent; deip.Flags |= DI_QUIETINSTALL | DI_FORCECOPY; deip.FlagsEx |= DI_FLAGSEX_IN_SYSTEM_SETUP; hrTemp = HrSetupDiSetDeviceInstallParams ( hdi, &deid, &deip); } TraceHr (ttidError, FAL, hrTemp, FALSE, "Error " "getting and setting device params."); } CiSetReservedField (hdi, &deid, &AdapterOutParams); // Officially call the class installer to install // this device // hr = HrCiCallClassInstallerToInstallComponent (hdi, &deid); CiClearReservedField (hdi, &deid); Data.dwCharacter = AdapterOutParams.dwCharacter; Data.InstanceGuid = AdapterOutParams.InstanceGuid; if (S_OK == hr) { hr = HrSetupDiGetDeviceInstanceId (hdi, &deid, szPnpId, MAX_DEVICE_ID_LEN, NULL); if (S_OK == hr) { Data.pszPnpId = szPnpId; } } } else // Non-net class components { COMPONENT_INSTALL_INFO cii; PSP_DRVINFO_DETAIL_DATA pdridd = NULL; SP_DRVINFO_DATA drid; // Now get the driver's detailed information hr = HrCiGetDriverDetail (hdi, &deid, &drid, &pdridd); if (S_OK == hr) { ZeroMemory (&cii, sizeof(cii)); cii.Class = Params.Class; cii.pszInfId = Params.pszInfId; cii.pszInfFile = pdridd->InfFileName; cii.hwndParent = Params.hwndParent; cii.pszDescription = drid.Description; cii.pszSectionName = pdridd->SectionName; HINF hinf; hr = HrSetupOpenInfFile (pdridd->InfFileName, NULL, INF_STYLE_WIN4, NULL, &hinf); if (S_OK == hr) { // Make sure this is an NT5 inf network inf // hr = HrSetupIsValidNt5Inf (hinf); SetupCloseInfFile (hinf); } if (S_OK == hr) { hr = HrCiInstallComponentInternal (&cii); if (S_OK == hr) { Data.InstanceGuid = cii.InstanceGuid; Data.dwCharacter = cii.dwCharacter; } } MemFree (pdridd); } } if ((S_OK == hr) && ppComponent) // !previously installed { CComponent* pComponent; hr = CComponent::HrCreateInstance ( &Data, CCI_ENSURE_EXTERNAL_DATA_LOADED, Params.pOboToken, &pComponent); if (S_OK == hr) { *ppComponent = pComponent; } } if ((S_OK == hr) && pdwNewCharacter) { *pdwNewCharacter = Data.dwCharacter; } } SetupDiDestroyDeviceInfoList(hdi); } #ifdef ENABLETRACE if (S_OK == hr) { TraceTag(ttidClassInst, "Component now installed!"); } #endif //ENABLETRACE TraceHr (ttidError, FAL, hr, FALSE, "HrCiInstallComponent (%S)", Params.pszInfId); return hr; } //+-------------------------------------------------------------------------- // // Function: SetBadDriverFlagIfNeededInList // // Purpose: Enumerates a driver list setting the DNF_BAD_DRIVER flag // in every node that has a DNF_EXCLUDEFROMLIST flag. // // Arguments: // hdi [in] See Device Installer Api documentaion for details // // Returns: HRESULT. S_OK // // Author: billbe 24 Nov 1998 // // Notes: SetupDi forces us to use the DNF_BAD_DRIVER flag for non-netdevice // classes if we want to exclude them from the select device dialog. // This means to non-netclass components that // DNF_BAD_DRIVER = DNF_EXCLUDEFROMLIST. // VOID SetBadDriverFlagIfNeededInList(HDEVINFO hdi) { Assert(IsValidHandle(hdi)); HRESULT hr = S_OK; SP_DRVINSTALL_PARAMS drip; SP_DRVINFO_DATA drid; DWORD dwIndex = 0; // Enumerate each driver in hdi while (S_OK == (hr = HrSetupDiEnumDriverInfo(hdi, NULL, SPDIT_CLASSDRIVER, dwIndex++, &drid))) { hr = HrSetupDiGetDriverInstallParams(hdi, NULL, &drid, &drip); if (S_OK == hr) { // If the driver already has the bad driver flag set, // go on to the next one. if (drip.Flags & DNF_BAD_DRIVER) { continue; } // If the driver has the exclude flag set, then set the // bad driver flag. if (drip.Flags & DNF_EXCLUDEFROMLIST) { drip.Flags |= DNF_BAD_DRIVER; (VOID) HrSetupDiSetDriverInstallParams(hdi, NULL, &drid, &drip); } } } if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr) { hr = S_OK; } TraceHr (ttidError, FAL, hr, FALSE, "SetBadDriverFlagIfNeededInList"); } //+-------------------------------------------------------------------------- // // Function: HrCiExcludeNonNetClassDriverFromSelectUsingInfId // // Purpose: Locates a driver in a driver list and sets its exclude from // select flag. // // Arguments: // hdi [in] See Device Installer Api documentaion for details // pszInfId [in] The INF id of the component to exclude // // Returns: HRESULT. S_OK // // Author: billbe 29 Oct 1998 // // Notes: // HRESULT HrCiExcludeNonNetClassDriverFromSelectUsingInfId( IN HDEVINFO hdi, IN PCWSTR pszInfId) { Assert(IsValidHandle(hdi)); Assert(pszInfId); HRESULT hr = S_OK; SP_DRVINSTALL_PARAMS drip; SP_DRVINFO_DATA drid; PSP_DRVINFO_DETAIL_DATA pdridd; DWORD dwIndex = 0; // Enumerate each driver in hdi while (S_OK == (hr = HrSetupDiEnumDriverInfo (hdi, NULL, SPDIT_CLASSDRIVER, dwIndex++, &drid))) { (VOID) HrSetupDiGetDriverInstallParams (hdi, NULL, &drid, &drip); // If the driver is already excluded for some other reason // don't bother trying to determine if it should be excluded. // Note that setupdi forces us to use DNF_BAD_DRIVER to exclude // non-device drivers rather than using DNF_EXCLUDEFROMLIST. if (drip.Flags & DNF_BAD_DRIVER) { continue; } // Get driver detail info hr = HrSetupDiGetDriverInfoDetail (hdi, NULL, &drid, &pdridd); if (S_OK == hr) { // If the IDs match, exclude it from the dialog // if (0 == lstrcmpiW (pdridd->HardwareID, pszInfId)) { // Non-device drivers can't use DNF_EXCLUDEFROMLIST // and must use DNF_BAD_DRIVER. drip.Flags |= DNF_BAD_DRIVER; (VOID) HrSetupDiSetDriverInstallParams (hdi, NULL, &drid, &drip); } MemFree (pdridd); } } if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr) { hr = S_OK; } TraceHr (ttidError, FAL, hr, FALSE, "HrCiExcludeNonNetClassDriverFromSelectUsingInfId"); return hr; } //+--------------------------------------------------------------------------- // // Function: ExcludeLockedDownComponents // // Purpose: A callback function compatible with EnumLockedDownComponents // that is used to exclude locked down components from // selection. Called from HrCiPrepareSelectDeviceDialog. // This call back is called for each locked down component. // // Arguments: // pszInfId [in] the INF ID to exclude. // pvCallerData [in] the HDEVINFO cast to PVOID // // Returns: nothing // // Author: shaunco 24 May 1999 // // Notes: The callback interface was chosen so that the class installer // is not burdended with the details of how/where the locked // down components are implemented. // VOID CALLBACK ExcludeLockedDownComponents ( IN PCWSTR pszInfId, IN PVOID pvCallerData) { Assert (pszInfId && *pszInfId); Assert (pvCallerData); HDEVINFO hdi = (HDEVINFO)pvCallerData; HrCiExcludeNonNetClassDriverFromSelectUsingInfId (hdi, pszInfId); } //+-------------------------------------------------------------------------- // // Function: HrCiBuildExcludedDriverList // // Purpose: Non-Net class components can only be installed once // So we need to iterate through the installed // components, find their matching driver node in // a Device Installer Api built driver list for the class, // and set their exclude from select flag. This list // will then be given to SetupDiSelectDevice which // will not display the nodes with the exclude flag set. // // Arguments: // hdi [in] See Device Installer Api documentaion for details // guidClass [in] The class of components to build a driver list for // pNetCfg [out] The current network configuration (i.e. what is // installed). // // Returns: HRESULT. S_OK // // Author: billbe 10 Dec 1996 // // Notes: Device Installer Api builds the driver list by rummaging // through the inf directory and finding the components // that are in files with the same class guid as the // HDEVINFO. This is the same processing done // in SetupDiSelectDevice, but the process is // not repeated twice because we will give the // list we built here to SetupDiSelectDevice. // HRESULT HrCiBuildExcludedDriverList( IN HDEVINFO hdi, IN NETCLASS Class, IN CNetConfig* pNetConfig) { HRESULT hr; Assert(IsValidHandle(hdi)); Assert(pNetConfig); // This might take some time. We are doing the same work as // SetupDiSelectDevice would do. When we are done, we will // hand the driver list to SetupDiSelectDevice so it won't // need to rummage through the inf directory // CWaitCursor wc; // For non-device classes, we need to allow excluded drivers // in order to get a list returned. hr = HrSetupDiSetDeipFlags(hdi, NULL, DI_FLAGSEX_ALLOWEXCLUDEDDRVS, SDDFT_FLAGSEX, SDFBO_OR); if (S_OK == hr) { #ifdef ENABLETRACE CBenchmark bmrk; bmrk.Start("SetupDiBuildDriverInfoList"); #endif //ENABLETRACE hr = HrSetupDiBuildDriverInfoList(hdi, NULL, SPDIT_CLASSDRIVER); #ifdef ENABLETRACE bmrk.Stop(); TraceTag(ttidBenchmark, "%s : %s seconds", bmrk.SznDescription(), bmrk.SznBenchmarkSeconds(2)); #endif //ENABLETRACE } // Go through the network configuration and hide already installed // components. Note: We only do this the first time. We show installed // components if the user selects Have Disk on the dialog. CComponent* pComponent; CComponentList::const_iterator iter; for (iter = pNetConfig->Core.Components.begin(); iter != pNetConfig->Core.Components.end(); iter++) { pComponent = *iter; Assert (pComponent); if (Class == pComponent->Class()) { // Hide the driver hr = HrCiExcludeNonNetClassDriverFromSelectUsingInfId( hdi, pComponent->m_pszInfId); } } TraceHr (ttidError, FAL, hr, FALSE, "HrCiBuildExcludedDriverList"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiSelectComponent // // Purpose: This function displays the Select Device dialog for the // class of components specified by guidClass. Once the // component has been selected, it is installed.. Since // this fcn is called from INetCfg, we have the write lock. // // Arguments: // Class [in] The class of components to display in the // Select Device dialog // hwndParent [in] The HWND of the parent window, used to display // the UI // pcfi [in] A structure used to determine what // components should be filtered out of // the select dialog (defined in netcfg.h) // ppParams [out] Params used to install the component. // // Returns: HRESULT. S_OK if successful, S_FALSE if the component // selected is being reinstalled instead of fresh // installed, an error code otherwise // // Author: billbe 11 Nov 1996 // // Notes: Filtering is only performed when selecting protocols, // services, and clients. // HRESULT HrCiSelectComponent( IN NETCLASS Class, IN HWND hwndParent, IN const CI_FILTER_INFO* pcfi, OUT COMPONENT_INSTALL_PARAMS** ppParams) { Assert (ppParams); Assert (!FIsEnumerated (Class)); HRESULT hr; HDEVINFO hdi; // We need to create a DeviceInfoSet item to use the SelectDevice dialog. hr = HrSetupDiCreateDeviceInfoList( MAP_NETCLASS_TO_GUID[Class], hwndParent, &hdi); if (S_OK == hr) { // call the class installer to bring up the select device dialog // for enumerated components. This will notify any coinstallers // // This will be a map of component ids to instance guids // for all installed components of Class. CNetConfig NetConfig; hr = HrLoadNetworkConfigurationFromRegistry (KEY_READ, &NetConfig); if (S_OK == hr) { hr = HrCiBuildExcludedDriverList (hdi, Class, &NetConfig); } if (S_OK == hr) { // Store the filter info in the hdi CiSetReservedField(hdi, NULL, pcfi); // We want the Have disk button, but if the call fails we can // still continue (VOID) HrSetupDiSetDeipFlags(hdi, NULL, DI_SHOWOEM, SDDFT_FLAGS, SDFBO_OR); // Bring up the dialog hr = HrSetupDiCallClassInstaller(DIF_SELECTDEVICE, hdi, NULL); if (S_OK == hr) { SP_DRVINFO_DATA drid; PSP_DRVINFO_DETAIL_DATA pdridd; // Now get the driver's detailed information hr = HrCiGetDriverDetail (hdi, NULL, &drid, &pdridd); if (S_OK == hr) { DWORD cbInfId = CbOfSzAndTerm(pdridd->HardwareID); DWORD cbInfFile = CbOfSzAndTerm(pdridd->InfFileName); // Create a component install params structure so we // can install the component. hr = E_OUTOFMEMORY; *ppParams = new (extrabytes, cbInfId + cbInfFile) COMPONENT_INSTALL_PARAMS; if (*ppParams) { ZeroMemory (*ppParams, sizeof (COMPONENT_INSTALL_PARAMS)); (*ppParams)->Class = Class; (*ppParams)->hwndParent = hwndParent; (*ppParams)->pszInfId = (PCWSTR)(*ppParams + 1); wcscpy ((PWSTR)(*ppParams)->pszInfId, pdridd->HardwareID); (*ppParams)->pszInfFile = (PCWSTR)((BYTE*)(*ppParams)->pszInfId + cbInfId); wcscpy ((PWSTR)(*ppParams)->pszInfFile, pdridd->InfFileName); hr = S_OK; } MemFree (pdridd); } } // Clear the field so we don't try to destroy it later // via DIF_DESTROYPRIVATEDATA CiClearReservedField(hdi, NULL); } SetupDiDestroyDeviceInfoList(hdi); } TraceHr (ttidError, FAL, hr, HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr, "HrCiSelectComponent"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiHideIrrelevantRasProtocols // // Purpose: Hides protocols from the SelectDevice dialog that RAS does // not interact with. // // Arguments: // hdi [in] Contains a list of available drivers. // See Device Installer Api documentation for // more info // eFilter [in] Either FC_RASSRV or FC_RASCLI. // // Returns: HRESULT. S_OK if successful, error code otherwise // // Author: billbe 18 May 1998 // // Notes: // HRESULT HrCiHideIrrelevantRasProtocols ( IN HDEVINFO hdi, IN CI_FILTER_COMPONENT eFilter) { DWORD dwIndex = 0; SP_DRVINFO_DATA drid; SP_DRVINSTALL_PARAMS drip; PSP_DRVINFO_DETAIL_DATA pdridd; HRESULT hr; extern const WCHAR c_szInfId_MS_AppleTalk[]; extern const WCHAR c_szInfId_MS_NetMon[]; extern const WCHAR c_szInfId_MS_NWIPX[]; extern const WCHAR c_szInfId_MS_TCPIP[]; static const WCHAR* const c_aszServerProtocols[] = { c_szInfId_MS_AppleTalk, c_szInfId_MS_NetMon, c_szInfId_MS_NWIPX, c_szInfId_MS_TCPIP }; static const WCHAR* const c_aszClientProtocols[] = { c_szInfId_MS_NetMon, c_szInfId_MS_NWIPX, c_szInfId_MS_TCPIP }; Assert ((FC_RASSRV == eFilter) || (FC_RASCLI == eFilter)); const WCHAR* const* aszProtocols; DWORD cProtocols; // What we show as available protocols to install differs between // ras server and ras client (aka Incoming connectoid and Dial-up). // if (FC_RASSRV == eFilter) { aszProtocols = c_aszServerProtocols; cProtocols = celems(c_aszServerProtocols); } else { aszProtocols = c_aszClientProtocols; cProtocols = celems(c_aszClientProtocols); } // Enumerate each driver in hdi while (S_OK == (hr = HrSetupDiEnumDriverInfo(hdi, NULL, SPDIT_CLASSDRIVER, dwIndex++, &drid))) { (VOID) HrSetupDiGetDriverInstallParams(hdi, NULL, &drid, &drip); // If the driver is already excluded for some other reason // don't bother trying to determine if it should be excluded // Note that setupdi forces us to use DNF_BAD_DRIVER to exclude // non-device drivers rather than using DNF_EXCLUDEFROMLIST. if (drip.Flags & DNF_BAD_DRIVER) { continue; } // Get driver detail info hr = HrSetupDiGetDriverInfoDetail(hdi, NULL, &drid, &pdridd); if (S_OK == hr) { // go through the list of relevant protocols to find which // ones can be shown // // Assume we are going to hide this protocol BOOL fHideProtocol = TRUE; for (DWORD i = 0; i < cProtocols; i++) { // If the protocol is on the guest list, we won't boot // it out // if (0 == _wcsicmp(aszProtocols[i], pdridd->HardwareID)) { fHideProtocol = FALSE; } } if (fHideProtocol) { // exclude from select // Note that setupdi forces us to use DNF_BAD_DRIVER to // exclude non-device drivers rather than using // DNF_EXCLUDEFROMLIST. drip.Flags |= DNF_BAD_DRIVER; (VOID) HrSetupDiSetDriverInstallParams(hdi, NULL, &drid, &drip); } MemFree (pdridd); } } if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr) { hr = S_OK; } TraceHr (ttidError, FAL, hr, FALSE, "HrCiHideIrrelevantRasProtocols"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiHideIrrelevantDrivers // // Purpose: Enumerates a driver list, opening each driver file and // processing its registry entries into a temporary key. // The lower range of each driver is then examined for // a match with pszUpperRange. If no match is // found, the driver's DNF_BAD_DRIVER flag is set // which will prevent it from being shown in the // Select Device Dialog // // Arguments: // hdi [in] Contains a list of available drivers. // See Device Installer Api documentation for // more info // pszUpperRange [in] The upper range will be used to hide irrelevant // drivers. // // Returns: HRESULT. S_OK if successful, error code otherwise // // Author: billbe 7 May 1998 // // Notes: // HRESULT HrCiHideIrrelevantDrivers( IN HDEVINFO hdi, IN PCWSTR pszUpperRange) { Assert(IsValidHandle(hdi)); Assert(pszUpperRange); // Create a temporary key so we can process each protocol's // registry entries in an effort to get its supported // lower range of interfaces HKEY hkeyTemp; HRESULT hr = HrRegCreateKeyEx(HKEY_LOCAL_MACHINE, c_szRegKeyTemp, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyTemp, NULL); if (S_OK == hr) { DWORD dwIndex = 0; SP_DRVINFO_DATA drid; SP_DRVINSTALL_PARAMS drip; HKEY hkeyInterfaces; // Enumerate each driver in hdi while (S_OK == (hr = HrSetupDiEnumDriverInfo(hdi, NULL, SPDIT_CLASSDRIVER, dwIndex++, &drid))) { (VOID) HrSetupDiGetDriverInstallParams(hdi, NULL, &drid, &drip); // If the driver is already excluded for some other reason // don't bother trying to determine if it should be exluded. // Note that setupdi forces us to use DNF_BAD_DRIVER to exclude // non-device drivers rather than using DNF_EXCLUDEFROMLIST. if (drip.Flags & DNF_BAD_DRIVER) { continue; } // Get driver detail info PSP_DRVINFO_DETAIL_DATA pdridd = NULL; hr = HrSetupDiGetDriverInfoDetail(hdi, NULL, &drid, &pdridd); if (S_OK == hr) { HINF hinf = NULL; // Open the driver inf hr = HrSetupOpenInfFile(pdridd->InfFileName, NULL, INF_STYLE_WIN4, NULL, &hinf); WCHAR szActual[_MAX_PATH]; if (S_OK == hr) { // Get the actual install section name (i.e. with // os/platform extension if it exists) hr = HrSetupDiGetActualSectionToInstallWithBuffer (hinf, pdridd->SectionName, szActual, _MAX_PATH, NULL, NULL); if (S_OK == hr) { // Run the registry sections into the temporary key hr = HrCiInstallFromInfSection(hinf, szActual, hkeyTemp, NULL, SPINST_REGISTRY); } } if (S_OK == hr) { // Open the interfaces key of the driver hr = HrRegOpenKeyEx(hkeyTemp, L"Ndi\\Interfaces", KEY_ALL_ACCESS, &hkeyInterfaces); if (S_OK == hr) { PWSTR pszLowerRange = NULL; // Read the lower interfaces value. // hr = HrRegQuerySzWithAlloc (hkeyInterfaces, L"LowerRange", &pszLowerRange); // If we succeeded in reading the list and // there is no match with one of the upper // interfaces... if ((S_OK == hr) && !FSubstringMatch (pszUpperRange, pszLowerRange, NULL, NULL)) { // exclude from select drip.Flags |= DNF_BAD_DRIVER; (VOID) HrSetupDiSetDriverInstallParams(hdi, NULL, &drid, &drip); } // Clear lower interface list for next component MemFree(pszLowerRange); RegDeleteValue (hkeyInterfaces, L"LowerRange"); RegCloseKey(hkeyInterfaces); } } SetupCloseInfFileSafe(hinf); MemFree (pdridd); } } if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr) { hr = S_OK; } RegCloseKey(hkeyTemp); HrRegDeleteKeyTree(HKEY_LOCAL_MACHINE, c_szRegKeyTemp); } TraceHr (ttidError, FAL, hr, FALSE, "HrCiHideIrrelevantDrivers"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiHideIrrelevantFilterServices // // Purpose: Enumerates a driver list, opening each driver file and // processing its registry entries into a temporary key. // Only filter services are filtered here. If the component is // a filter service (characteristics|NCF_FILTER) // the FilterMediaTypes and the LowerExclude attributes are examined // to see if the filter service can bind to the adapter. The filter // services which cannot bind to the adapter are culled from // the select dialog // // Arguments: // hdi [in] Contains a list of available drivers. // See Device Installer Api documentation for // more info // pAdapter [in] Pointer to a CComponent object representing the // adapter for which filter services are to be hidden. // // Returns: HRESULT. S_OK if successful, error code otherwise // // Author: sumeetb October 17, 2001 // // Notes: // HRESULT HrCiHideIrrelevantFilterServices( IN HDEVINFO hdi, IN const CComponent * const pAdapter) { Assert(IsValidHandle(hdi)); Assert(pAdapter); // Create a temporary key so we can process each filter's // registry entries in an effort to get its FilterMediaTypes and // LowerExclude attributes HKEY hkeyTemp; HRESULT hr = HrRegCreateKeyEx(HKEY_LOCAL_MACHINE, c_szRegKeyTemp, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyTemp, NULL); if (S_OK == hr) { DWORD dwIndex = 0; SP_DRVINFO_DATA drid; SP_DRVINSTALL_PARAMS drip; HKEY hkeyInterfaces; // Enumerate each driver in hdi while (S_OK == (hr = HrSetupDiEnumDriverInfo(hdi, NULL, SPDIT_CLASSDRIVER, dwIndex++, &drid))) { BOOL fCanBind = TRUE; (VOID) HrSetupDiGetDriverInstallParams(hdi, NULL, &drid, &drip); // If the driver is already excluded for some other reason // don't bother trying to determine if it should be excluded. // Note that setupdi forces us to use DNF_BAD_DRIVER to exclude // non-device drivers rather than using DNF_EXCLUDEFROMLIST. if (drip.Flags & DNF_BAD_DRIVER) { continue; } // Get driver detail info PSP_DRVINFO_DETAIL_DATA pdridd = NULL; hr = HrSetupDiGetDriverInfoDetail(hdi, NULL, &drid, &pdridd); if (S_OK == hr) { HINF hinf = NULL; // Open the driver inf hr = HrSetupOpenInfFile(pdridd->InfFileName, NULL, INF_STYLE_WIN4, NULL, &hinf); if (S_OK == hr) { WCHAR szActual[_MAX_PATH]; // Get the actual install section name (i.e. with // os/platform extension if it exists) hr = HrSetupDiGetActualSectionToInstallWithBuffer (hinf, pdridd->SectionName, szActual, _MAX_PATH, NULL, NULL); if (S_OK == hr) { DWORD dwCharacteristics = 0; hr = HrSetupGetFirstDword (hinf, szActual, L"Characteristics", &dwCharacteristics); if (S_OK == hr ) { if (dwCharacteristics & NCF_FILTER) { // it is a filter. // Run the registry sections into the temporary key hr = HrCiInstallFromInfSection(hinf, szActual, hkeyTemp, NULL, SPINST_REGISTRY); if (S_OK == hr) { hr = HrRegOpenKeyEx(hkeyTemp, L"Ndi\\Interfaces", KEY_ALL_ACCESS, &hkeyInterfaces); if (S_OK == hr) { // check filter attributes - FilterMediaTypes and // LowerExclude to see if the filter can bind to // the adapter PWSTR pszFilterMediaTypes = NULL; PWSTR pszLowerExclude = NULL; (VOID) HrRegQuerySzWithAlloc (hkeyInterfaces, L"FilterMediaTypes", &pszFilterMediaTypes); (VOID) HrRegQuerySzWithAlloc (hkeyInterfaces, L"LowerExclude", &pszLowerExclude); fCanBind = pAdapter->FCanDirectlyBindToFilter( pszFilterMediaTypes, pszLowerExclude); MemFree(pszFilterMediaTypes); MemFree(pszLowerExclude); // clean up the relevant keys from the // registry for the filter RegDeleteValue (hkeyInterfaces, L"FilterMediaTypes"); RegDeleteValue (hkeyInterfaces, L"LowerExclude"); RegCloseKey(hkeyInterfaces); } // end open Interface key } // end install from inf section } // end if filter } // end get Characteristics } // end get actual install section. SetupCloseInfFileSafe(hinf); } // end open inf file if (!fCanBind) { drip.Flags |= DNF_BAD_DRIVER; (VOID) HrSetupDiSetDriverInstallParams(hdi, NULL, &drid, &drip); } MemFree (pdridd); } // end get driver detail } // end while if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr) { hr = S_OK; } RegCloseKey(hkeyTemp); HrRegDeleteKeyTree(HKEY_LOCAL_MACHINE, c_szRegKeyTemp); } // end create temp key TraceHr (ttidError, FAL, hr, FALSE, "HrCiHideIrrelevantFilterServices"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiSetSelectDeviceDialogStrings // // Purpose: This function sets the strings displayed in the Select Device // dialog based on the class of devices being selected. // // Arguments: // hdi [in] See Device Installer Api // pdeid [in] // guidClass [in] The class of device being selected // // Returns: HRESULT. S_OK if successful, an error code otherwise // // Author: billbe 11 Nov 1996 // // Notes: // HRESULT HrCiSetSelectDeviceDialogStrings( IN HDEVINFO hdi, IN PSP_DEVINFO_DATA pdeid, IN const GUID& guidClass) { Assert(IsValidHandle(hdi)); SP_SELECTDEVICE_PARAMS sdep; // The strings used in the dialog are specified through the // SP_SELECTDEVICE_PARAMS structure // HRESULT hr = HrSetupDiGetFixedSizeClassInstallParams(hdi, pdeid, (PSP_CLASSINSTALL_HEADER)&sdep, sizeof(sdep)); if (FAILED(hr)) { // If the error is ERROR_NO_CLASSINSTALL_PARAMS then this function // didn't really fail since it is possible if (SPAPI_E_NO_CLASSINSTALL_PARAMS == hr) { hr = S_OK; } } else if (DIF_SELECTDEVICE != sdep.ClassInstallHeader.InstallFunction) { TraceTag(ttidClassInst, "Incorrect function in Class Install Header " "Expected DIF_SELECTDEVICE, got %lX", sdep.ClassInstallHeader.InstallFunction); } BOOL fHaveDiskShown = FALSE; if (S_OK == hr) { // Get the install params and check if the DI_SHOWOEM flag is set // if so, the Have Disk button will be shown // SP_DEVINSTALL_PARAMS deip; // If the call fails we can still go on unfazed. (VOID) HrSetupDiGetDeviceInstallParams(hdi, pdeid, &deip); if (deip.Flags & DI_SHOWOEM) { fHaveDiskShown = TRUE; } // Now we set the strings based on the type of component we are // selecting if (GUID_DEVCLASS_NETCLIENT == guidClass) { wcscpy (sdep.Title, SzLoadIds (IDS_SELECTDEVICECLIENTTITLE)); wcscpy (sdep.ListLabel, SzLoadIds (IDS_SELECTDEVICECLIENTLISTLABEL)); wcscpy (sdep.Instructions, SzLoadIds (IDS_SELECTDEVICECLIENTINSTRUCTIONS)); } else if (GUID_DEVCLASS_NETSERVICE == guidClass) { wcscpy (sdep.Title, SzLoadIds (IDS_SELECTDEVICESERVICETITLE)); wcscpy (sdep.ListLabel, SzLoadIds (IDS_SELECTDEVICESERVICELISTLABEL)); wcscpy (sdep.Instructions, SzLoadIds (IDS_SELECTDEVICESERVICEINSTRUCTIONS)); } else if (GUID_DEVCLASS_NETTRANS == guidClass) { wcscpy (sdep.Title, SzLoadIds (IDS_SELECTDEVICEPROTOCOLTITLE)); wcscpy (sdep.ListLabel, SzLoadIds (IDS_SELECTDEVICEPROTOCOLLISTLABEL)); wcscpy (sdep.Instructions, SzLoadIds (IDS_SELECTDEVICEPROTOCOLINSTRUCTIONS)); } else if (GUID_DEVCLASS_NET == guidClass) { wcscpy (sdep.Title, SzLoadIds (IDS_SELECTDEVICEADAPTERTITLE)); wcscpy (sdep.SubTitle, SzLoadIds (IDS_SELECTDEVICEADAPTERSUBTITLE)); wcscpy (sdep.ListLabel, SzLoadIds (IDS_SELECTDEVICEADAPTERLISTLABEL)); wcscpy (sdep.Instructions, SzLoadIds (IDS_SELECTDEVICEADAPTERINSTRUCTIONS)); } else if (GUID_DEVCLASS_INFRARED == guidClass) { wcscpy (sdep.Title, SzLoadIds (IDS_SELECTDEVICEINFRAREDTITLE)); wcscpy (sdep.SubTitle, SzLoadIds (IDS_SELECTDEVICEINFRAREDSUBTITLE)); wcscpy (sdep.ListLabel, SzLoadIds (IDS_SELECTDEVICEINFRAREDLISTLABEL)); wcscpy (sdep.Instructions, SzLoadIds (IDS_SELECTDEVICEINFRAREDINSTRUCTIONS)); } else { // We should never get here AssertSz(FALSE, "Invalid Class"); } // If the Have Disk button is shown, we need to add instructions for // it if (fHaveDiskShown) { wcscat (sdep.Instructions, SzLoadIds (IDS_HAVEDISK_INSTRUCTIONS)); } sdep.ClassInstallHeader.InstallFunction = DIF_SELECTDEVICE; // Now we update the parameters. hr = HrSetupDiSetClassInstallParams (hdi, pdeid, (PSP_CLASSINSTALL_HEADER)&sdep, sizeof(SP_SELECTDEVICE_PARAMS)); } TraceHr (ttidError, FAL, hr, FALSE, "HrCiSetSelectDeviceDialogStrings"); return hr; } //+-------------------------------------------------------------------------- // // Function: HrCiPrepareSelectDeviceDialog // // Purpose: Sets the strings that will appear in the Select Device // dialog based on class type. Also, filters out components // based on filtering criteria (note: only for non-net // class components // // Arguments: // hdi [in] See Device Installer Api documentation for more info // pdeid [in] // // Returns: HRESULT. S_OK if successful, error code otherwise // // Author: billbe 26 Jun 1997 // // Notes: // HRESULT HrCiPrepareSelectDeviceDialog( IN HDEVINFO hdi, IN PSP_DEVINFO_DATA pdeid) { Assert(IsValidHandle(hdi)); GUID guidClass; CI_FILTER_INFO* pcfi; HRESULT hr = S_OK; static const WCHAR c_szNetwareInfId[] = L"MS_NwClient"; static const WCHAR c_szQosInfId[] = L"MS_PSched"; if (pdeid) { // Get the class guid from the specified device element guidClass = pdeid->ClassGuid; } else { // otherwise, get it from the hdi hr = HrSetupDiGetDeviceInfoListClass (hdi, &guidClass); } if ((S_OK == hr) && !FIsEnumerated (guidClass)) { // This might take some time. We are doing the same work as // SetupDiSelectDevice would do. When we are done, we will // hand the driver list to SetupDiSelectDevice so it won't // need to rummage through the inf directory // CWaitCursor wc; // For non-device classes, we need to allow excluded drivers // in order to get a list returned. hr = HrSetupDiSetDeipFlags(hdi, NULL, DI_FLAGSEX_ALLOWEXCLUDEDDRVS, SDDFT_FLAGSEX, SDFBO_OR); if (S_OK == hr) { #ifdef ENABLETRACE CBenchmark bmrk; bmrk.Start("SetupDiBuildDriverInfoList"); #endif //ENABLETRACE // If we have already built a driver list, this will return // immediately. // hr = HrSetupDiBuildDriverInfoList(hdi, NULL, SPDIT_CLASSDRIVER); #ifdef ENABLETRACE bmrk.Stop(); TraceTag(ttidBenchmark, "%s : %s seconds", bmrk.SznDescription(), bmrk.SznBenchmarkSeconds(2)); #endif //ENABLETRACE } if (S_OK == hr) { // Go through every driver node and set DNF_BAD_DRIVER // if DNF_EXCLUDEFROMLIST is set. Note: SetupDi forces us // to do this for non netclass driver lists. SetBadDriverFlagIfNeededInList(hdi); // Exclude components that are in lockdown. // EnumLockedDownComponents (ExcludeLockedDownComponents, hdi); SP_DEVINSTALL_PARAMS deip; hr = HrSetupDiGetDeviceInstallParams (hdi, pdeid, &deip); if (S_OK == hr) { pcfi = (CI_FILTER_INFO*)deip.ClassInstallReserved; // if filter info was present and we are selecting protocols... if (pcfi) { if (GUID_DEVCLASS_NETTRANS == guidClass) { // If the filter is for lan or atm and pvReserved is // not null... if (((FC_LAN == pcfi->eFilter) || (FC_ATM == pcfi->eFilter)) && pcfi->pvReserved) { // Hide any drivers that can't bind to pvReserved hr = HrCiHideIrrelevantDrivers(hdi, (PCWSTR)((CComponent *)pcfi->pvReserved)->Ext.PszUpperRange()); } else if ((FC_RASSRV == pcfi->eFilter) || (FC_RASCLI == pcfi->eFilter)) { // Hide from the select dialog any protocols RAS does // not support hr = HrCiHideIrrelevantRasProtocols (hdi, pcfi->eFilter); } } else if ((GUID_DEVCLASS_NETCLIENT == guidClass) && (FC_ATM == pcfi->eFilter)) { // ATM adapters don't bind to Netware Client so // we need to try to hide it from the dialog (VOID) HrCiExcludeNonNetClassDriverFromSelectUsingInfId( hdi, c_szNetwareInfId); } else if ((GUID_DEVCLASS_NETSERVICE == guidClass) && (pcfi->pvReserved)) { // Hide any filters that can't bind to this adapter hr = HrCiHideIrrelevantFilterServices(hdi, (CComponent *)pcfi->pvReserved); } } } } } if (S_OK == hr) { // Set the strings for the Select Device dialog. // This is done by changing the parameters in the DeviceInfoSet. // The next call will create this InfoSet // If the call fails, we can still go on, we'll just have // slightly odd descriptions in the dialog. This is done after // the section above because strings change based on the existence // of the Have Disk button (VOID) HrCiSetSelectDeviceDialogStrings(hdi, pdeid, guidClass); // Now we need to indicate that we created a class install params // header in the structures and set the select device dialog strings // in it. If the call fails, we can still proceed though the // dialog will appear a bit strange (VOID) HrSetupDiSetDeipFlags(hdi, pdeid, DI_USECI_SELECTSTRINGS | DI_CLASSINSTALLPARAMS, SDDFT_FLAGS, SDFBO_OR); } TraceHr (ttidError, FAL, hr, FALSE, "HrCiPrepareSelectDeviceDialog"); return hr; } HRESULT HrCiInstallFilterDevice ( IN HDEVINFO hdi, IN PCWSTR pszInfId, IN CComponent* pAdapter, IN CComponent* pFilter, IN CFilterDevice** ppFilterDevice) { HRESULT hr; SP_DEVINFO_DATA deid; Assert (hdi); Assert (pszInfId && *pszInfId); Assert (pAdapter); Assert (FIsEnumerated(pAdapter->Class())); Assert (pFilter); Assert (pFilter->FIsFilter()); Assert (NC_NETSERVICE == pFilter->Class()); Assert (ppFilterDevice); *ppFilterDevice = NULL; // Initialize the devinfo data corresponding to the driver the // caller wants us to install. // hr = HrCiGetDriverInfo (hdi, &deid, *MAP_NETCLASS_TO_GUID[NC_NET], pszInfId, NULL); if (S_OK == hr) { ADAPTER_OUT_PARAMS AdapterOutParams; ZeroMemory (&AdapterOutParams, sizeof(AdapterOutParams)); CiSetReservedField (hdi, &deid, &AdapterOutParams); // Perform the installation. // hr = HrCiCallClassInstallerToInstallComponent (hdi, &deid); CiClearReservedField (hdi, &deid); if (S_OK == hr) { WCHAR szInstanceGuid[c_cchGuidWithTerm]; INT cch; HKEY hkeyInstance; // Convert the instance guid to a string. // cch = StringFromGUID2 ( AdapterOutParams.InstanceGuid, szInstanceGuid, c_cchGuidWithTerm); Assert (c_cchGuidWithTerm == cch); // Open the instance key of the newly installed device // so we can write the instance guid and the back pointer // to the filter. // hr = HrSetupDiOpenDevRegKey (hdi, &deid, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_WRITE, &hkeyInstance); if (S_OK == hr) { // Write the instance guid. // hr = HrRegSetSz (hkeyInstance, L"NetCfgInstanceId", szInstanceGuid); // Write the inf id of the parent filter. // hr = HrRegSetSz (hkeyInstance, L"FilterInfId", pFilter->m_pszInfId); RegCloseKey (hkeyInstance); } // Set the friendly name to include the adapter being // filtered. // if (S_OK == hr) { PWSTR pszFilterDesc; hr = HrSetupDiGetDeviceRegistryPropertyWithAlloc ( hdi, &deid, SPDRP_DEVICEDESC, NULL, (BYTE**)&pszFilterDesc); if (S_OK == hr) { #define SZ_NAME_SEP L" - " PWSTR pszName; ULONG cb; // sizeof(SZ_NAME_SEP) includes the NULL-terminator // so that will automatically add room for the // NULL-terminator we need to allocate for pszName. // cb = CbOfSzSafe (pAdapter->Ext.PszDescription()) + sizeof(SZ_NAME_SEP) + CbOfSzSafe (pszFilterDesc); pszName = (PWSTR)MemAlloc (cb); if (pszName) { wcscpy (pszName, pAdapter->Ext.PszDescription()); wcscat (pszName, SZ_NAME_SEP); wcscat (pszName, pszFilterDesc); Assert (cb == CbOfSzAndTerm(pszName)); hr = HrSetupDiSetDeviceRegistryProperty ( hdi, &deid, SPDRP_FRIENDLYNAME, (const BYTE*)pszName, cb); MemFree (pszName); } MemFree (pszFilterDesc); } // If the above fails, its not a big deal. // hr = S_OK; } if (S_OK == hr) { hr = CFilterDevice::HrCreateInstance ( pAdapter, pFilter, &deid, szInstanceGuid, ppFilterDevice); } } } TraceHr (ttidError, FAL, hr, FALSE, "HrCiInstallFilterDevice"); return hr; } HRESULT HrCiRemoveFilterDevice ( IN HDEVINFO hdi, IN SP_DEVINFO_DATA* pdeid) { HRESULT hr; ADAPTER_REMOVE_PARAMS arp = {0}; Assert (hdi); Assert (pdeid); CiSetReservedField (hdi, pdeid, &arp); hr = HrSetupDiCallClassInstaller (DIF_REMOVE, hdi, pdeid); CiClearReservedField (hdi, pdeid); TraceHr (ttidError, FAL, hr, FALSE, "HrCiRemoveFilterDevice"); return hr; }