windows-nt/Source/XPSP1/NT/net/config/netcfg/engine/classinst.cpp

3302 lines
105 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
//+---------------------------------------------------------------------------
//
// 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 <pch.h>
#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};
//+--------------------------------------------------------------------------
//
// Function: HrCiRegDeleteComponentNetworkKey
//
// Purpose: This function deletes the component key strInstanceGuid
// (and its subkeys) under the Network\<guidClass> 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;
}
}
#ifdef ENABLETRACE
else
{
TraceTag (ttidNetcfgBase,
"Perf Warning: No knowledge of INF file for the '%S' "
"component. SetupApi is now thrashing the disk looking for it.",
pszInfId);
}
#endif // ENABLETRACE
// 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\<net guid>\<instance id>
// For non-enumerated components, this is under
// HKLM\System\CCS\Control\Network\<Class Guid>\<Instance Guid>
//
// 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\<net guid> 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
// <Class GUID>/<instance GUID>.
// 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 = 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);
static const WCHAR c_szRegKeyTemp[] =
L"System\\CurrentControlSet\\Control\\Network\\FTempKey";
// 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: 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)pcfi->pvReserved);
}
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) &&
(FC_ATM == pcfi->eFilter))
{
// ATM adapters don't bind to QoS so try to hide it
(VOID) HrCiExcludeNonNetClassDriverFromSelectUsingInfId(
hdi, c_szQosInfId);
}
}
}
}
}
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;
}