//+---------------------------------------------------------------------------
//
//  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};

// 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\<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;
                    }
                }

                // 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 = (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;
}