//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1999.
//
//  File:       I C O M P . C P P
//
//  Contents:   Implements the INetCfgComponent COM interface.
//
//  Notes:
//
//  Author:     shaunco   15 Jan 1999
//
//----------------------------------------------------------------------------

#include <pch.h>
#pragma hdrstop
#include "ibind.h"
#include "icomp.h"
#include "ienum.h"
#include "nccfgmgr.h"
#include "ncreg.h"
#include "ncsetup.h"
#include "ncvalid.h"
#include "netcfg.h"
#include "netconp.h"
#include "util.h"


HRESULT
HrIsValidINetCfgComponent (
    IN INetCfgComponent* pICompInterface)
{
    Assert (pICompInterface);

    CImplINetCfgComponent* pIComp;
    pIComp = (CImplINetCfgComponent*)pICompInterface;

	if (pIComp == NULL)
	{
		return(E_OUTOFMEMORY);
	}

    return pIComp->HrIsValidInterface (IF_DEFAULT);
}

CComponent*
PComponentFromComInterface (
    IN INetCfgComponent* pICompInterface)
{
    Assert (pICompInterface);

    CImplINetCfgComponent* pIComp;
    pIComp = (CImplINetCfgComponent*)pICompInterface;

    // Can't do the following assert because we may be referencing the
    // component before it has been added to the core.  This case is possible
    // when installing a new component that installed a required component
    // on behalf of itself.  We will wind up in the function when adding
    // the refernce for the obo token.
    //
    //Assert (S_OK == pIComp->HrIsValidInterface (dwFlags));

    Assert (pIComp->m_pComponent);
    return pIComp->m_pComponent;
}

// static
HRESULT
CImplINetCfgComponent::HrCreateInstance (
    IN  CImplINetCfg* pINetCfg,
    IN  CComponent* pComponent,
    OUT CImplINetCfgComponent** ppIComp)
{
    HRESULT hr = E_OUTOFMEMORY;

    CImplINetCfgComponent* pObj;
    pObj = new CComObject <CImplINetCfgComponent>;
    if (pObj)
    {
        // Initialize our members.
        //
        pObj->m_pComponent = pComponent;

        // Do the standard CComCreator::CreateInstance stuff.
        //
        pObj->SetVoid (NULL);
        pObj->InternalFinalConstructAddRef ();
        hr = pObj->FinalConstruct ();
        pObj->InternalFinalConstructRelease ();

        if (S_OK == hr)
        {
            // The last thing we do is addref any interfaces we hold.
            // We only do this if we are returning success.
            //
            pObj->HoldINetCfg (pINetCfg);

            AddRefObj (pObj->GetUnknown());
            *ppIComp = pObj;
        }

        if (S_OK != hr)
        {
            delete pObj;
        }
    }
    TraceHr (ttidError, FAL, hr, FALSE,
        "CImplINetCfgComponent::HrCreateInstance");
    return hr;
}

HRESULT
CImplINetCfgComponent::HrIsValidInterface (
    IN DWORD dwFlags)
{
    HRESULT hr;

    hr = m_pINetCfg->HrIsValidInterface (dwFlags);
    if (S_OK != hr)
    {
        return hr;
    }

    // Check for deleted component.
    //
    if (!m_pComponent)
    {
        return HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE);
    }

    // If we made it this far, the component this interface represents
    // should definately be in the core component list or in the core
    // that we started with in the case that this component is in the middle
    // of being removed.
    //
    Assert(m_pINetCfg->m_pNetConfig->Core.Components.
                FComponentInList (m_pComponent) ||
           m_pINetCfg->m_pNetConfig->ModifyCtx.m_CoreStartedWith.Components.
                FComponentInList (m_pComponent));

    if (dwFlags & IF_NEED_COMPONENT_DATA)
    {
        hr = m_pComponent->Ext.HrEnsureExternalDataLoaded ();
    }

    return hr;
}

// We need to override CImplINetCfgHolder::HrLockAndTestForValidInterface
// because we have our own HrIsValidInterface to be called.
//
HRESULT
CImplINetCfgComponent::HrLockAndTestForValidInterface (
    IN DWORD dwFlags,
    IN INetCfgComponent* pIOtherComp, OPTIONAL
    OUT CComponent** ppOtherComp OPTIONAL)
{
    HRESULT hr;

    Lock();

    hr = HrIsValidInterface (dwFlags);

    // If pIOtherComp was passed in, the caller wants that interface
    // validated and the internal CComponent pointer for it returned.
    //
    if ((S_OK == hr) && pIOtherComp)
    {
        CImplINetCfgComponent* pOther;

        Assert (ppOtherComp);

        pOther = (CImplINetCfgComponent*)pIOtherComp;

        hr = pOther->HrIsValidInterface (IF_NEED_COMPONENT_DATA);
        if (S_OK == hr)
        {
            *ppOtherComp = pOther->m_pComponent;
        }
    }

    if (S_OK != hr)
    {
        Unlock();
    }

    TraceHr (ttidError, FAL, hr, FALSE,
        "CImplINetCfgComponent::HrLockAndTestForValidInterface");
    return hr;
}

HRESULT
CImplINetCfgComponent::HrAccessExternalStringAtOffsetAndCopy (
    IN UINT unOffset,
    OUT PWSTR* ppszDst)
{
    HRESULT hr;

    // Validate parameter.
    //
    if (FBadOutPtr (ppszDst))
    {
        hr = E_POINTER;
    }
    else
    {
        *ppszDst = NULL;

        hr = HrLockAndTestForValidInterface (IF_NEED_COMPONENT_DATA, NULL, NULL);
        if (S_OK == hr)
        {
            hr = HrCoTaskMemAllocAndDupSz (
                    m_pComponent->Ext.PszAtOffset (unOffset),
                    ppszDst);

            Unlock();
        }
    }

    TraceHr (ttidError, FAL, hr, FALSE,
        "CImplINetCfgComponent::HrAccessExternalStringAtOffsetAndCopy");
    return hr;
}

//+---------------------------------------------------------------------------
// INetCfgComponent -
//
STDMETHODIMP
CImplINetCfgComponent::GetDisplayName (
    OUT PWSTR* ppszDisplayName)
{
    HRESULT hr;

    hr = HrAccessExternalStringAtOffsetAndCopy (
            ECD_OFFSET(m_pszDescription),
            ppszDisplayName);

    TraceHr (ttidError, FAL, hr, FALSE,
        "CImplINetCfgComponent::GetDisplayName");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::SetDisplayName (
    IN PCWSTR pszDisplayName)
{
    HRESULT hr;

    // Validate parameter.
    //
    if (FBadInPtr (pszDisplayName))
    {
        hr = E_POINTER;
    }
    else
    {
        hr = HrLockAndTestForValidInterface (IF_NEED_COMPONENT_DATA, NULL, NULL);
        if (S_OK == hr)
        {
            // We only allow changing the display name (SPDRP_FRIENDLYNAME,
            // actually) of enumerated components.
            //
            if (FIsEnumerated(m_pComponent->Class()))
            {
                HDEVINFO hdi;
                SP_DEVINFO_DATA deid;

                hr = m_pComponent->HrOpenDeviceInfo (&hdi, &deid);
                if (S_OK == hr)
                {
                    hr = HrSetupDiSetDeviceName (hdi, &deid, pszDisplayName);

                    if (S_OK == hr)
                    {
                        m_pComponent->Ext.HrSetDescription (pszDisplayName);
                    }

                    SetupDiDestroyDeviceInfoList (hdi);
                }
            }
            else
            {
                hr = E_NOTIMPL;
            }

            Unlock();
        }
    }

    TraceHr (ttidError, FAL, hr, FALSE,
        "CImplINetCfgComponent::SetDisplayName");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::GetHelpText (
    OUT PWSTR* pszHelpText)
{
    HRESULT hr;

    hr = HrAccessExternalStringAtOffsetAndCopy (
            ECD_OFFSET(m_pszHelpText),
            pszHelpText);

    TraceHr (ttidError, FAL, hr, FALSE,
        "CImplINetCfgComponent::GetHelpText");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::GetId (
    OUT PWSTR* ppszId)
{
    HRESULT hr;

    // Validate parameters.
    //
    if (FBadOutPtr (ppszId))
    {
        hr = E_POINTER;
    }
    else
    {
        hr = HrLockAndTestForValidInterface (IF_DEFAULT, NULL, NULL);
        if (S_OK == hr)
        {
            hr = HrCoTaskMemAllocAndDupSz (
                    m_pComponent->m_pszInfId,
                    ppszId);

            Unlock();
        }
        else
        {
            *ppszId = NULL;
        }
    }

    TraceHr (ttidError, FAL, hr, FALSE,
        "CImplINetCfgComponent::GetId");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::GetCharacteristics (
    OUT LPDWORD pdwCharacteristics)
{
    HRESULT hr;

    // Validate parameters.
    //
    if (FBadOutPtr (pdwCharacteristics))
    {
        hr = E_POINTER;
    }
    else
    {
        hr = HrLockAndTestForValidInterface (IF_DEFAULT, NULL, NULL);
        if (S_OK == hr)
        {
            *pdwCharacteristics = m_pComponent->m_dwCharacter;

            Unlock();
        }
        else
        {
            *pdwCharacteristics = 0;
        }
    }

    TraceHr (ttidError, FAL, hr, FALSE,
        "CImplINetCfgComponent::GetCharacteristics");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::GetInstanceGuid (
    OUT GUID* pInstanceGuid)
{
    HRESULT hr;

    // Validate parameters.
    //
    if (FBadOutPtr (pInstanceGuid))
    {
        hr = E_POINTER;
    }
    else
    {
        hr = HrLockAndTestForValidInterface (IF_DEFAULT, NULL, NULL);
        if (S_OK == hr)
        {
            *pInstanceGuid = m_pComponent->m_InstanceGuid;

            Unlock();
        }
        else
        {
            *pInstanceGuid = GUID_NULL;
        }
    }

    TraceHr (ttidError, FAL, hr, FALSE,
        "CImplINetCfgComponent::GetInstanceGuid");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::GetPnpDevNodeId (
    OUT PWSTR* ppszDevNodeId)
{
    HRESULT hr;

    // Validate parameters.
    //
    if (FBadOutPtr (ppszDevNodeId))
    {
        hr = E_POINTER;
    }
    else
    {
        hr = HrLockAndTestForValidInterface (IF_DEFAULT, NULL, NULL);
        if (S_OK == hr)
        {
            if (FIsEnumerated(m_pComponent->Class()))
            {
                hr = HrCoTaskMemAllocAndDupSz (
                        m_pComponent->m_pszPnpId,
                        ppszDevNodeId);
            }
            else
            {
                hr = E_NOTIMPL;
            }

            Unlock();
        }
        else
        {
            *ppszDevNodeId = NULL;
        }
    }

    TraceHr (ttidError, FAL, hr, FALSE,
        "CImplINetCfgComponent::GetPnpDevNodeId");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::GetClassGuid (
    OUT GUID* pguidClass)
{
    HRESULT hr;

    // Validate parameters.
    //
    if (FBadOutPtr (pguidClass))
    {
        hr = E_POINTER;
    }
    else
    {
        hr = HrLockAndTestForValidInterface (IF_DEFAULT, NULL, NULL);
        if (S_OK == hr)
        {
            *pguidClass = *MAP_NETCLASS_TO_GUID[m_pComponent->Class()];

            Unlock();
        }
        else
        {
            *pguidClass = GUID_NULL;
        }
    }

    TraceHr (ttidError, FAL, hr, FALSE,
        "CImplINetCfgComponent::GetClassGuid");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::GetBindName (
    OUT PWSTR* ppszBindName)
{
    HRESULT hr;

    hr = HrAccessExternalStringAtOffsetAndCopy (
            ECD_OFFSET(m_pszBindName),
            ppszBindName);

    TraceHr (ttidError, FAL, hr, FALSE,
        "CImplINetCfgComponent::GetBindName");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::GetDeviceStatus (
    OUT ULONG* pulStatus)
{
    HRESULT hr;

    // Validate parameters.
    //
    if (FBadOutPtr (pulStatus))
    {
        hr = E_POINTER;
    }
    else
    {
        *pulStatus = 0;

        hr = HrLockAndTestForValidInterface (IF_DEFAULT, NULL, NULL);
        if (S_OK == hr)
        {
            if (!FIsEnumerated(m_pComponent->Class()))
            {
                hr = E_UNEXPECTED;
            }
            else
            {
                HDEVINFO hdi;
                SP_DEVINFO_DATA deid;

                hr = m_pComponent->HrOpenDeviceInfo (&hdi, &deid);

                if (S_OK == hr)
                {
                    ULONG ulStatus;
                    ULONG ulProblem;
                    CONFIGRET cfgRet;

                    cfgRet = CM_Get_DevNode_Status_Ex (
                                &ulStatus, &ulProblem, deid.DevInst, 0, NULL);

                    if (CR_SUCCESS == cfgRet)
                    {
                        hr = S_OK;
                        *pulStatus = ulProblem;
                    }
                    else if(CR_NO_SUCH_DEVINST == cfgRet)
                    {
                        hr = NETCFG_E_ADAPTER_NOT_FOUND;
                    }
                    else
                    {
                        TraceTag (ttidError, "CM_Get_DevNode_Status_Ex for "
                            "%S returned cfgRet=0x%08x, ulStatus=0x%08x, ulProblem=0x%08x",
                            m_pComponent->m_pszPnpId,
                            cfgRet,
                            ulStatus,
                            ulProblem);

                        hr = HrFromConfigManagerError (cfgRet, E_FAIL);
                    }

                    SetupDiDestroyDeviceInfoList (hdi);
                }
            }

            Unlock();
        }
    }

    TraceHr (ttidError, FAL, hr, FALSE,
        "CImplINetCfgComponent::GetDeviceStatus");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::OpenParamKey (
    OUT HKEY* phkey)
{
    HRESULT hr;

    // Validate parameters.
    //
    if (FBadOutPtr (phkey))
    {
        hr = E_POINTER;
    }
    else
    {
        *phkey = NULL;

        hr = HrLockAndTestForValidInterface (IF_NEED_COMPONENT_DATA, NULL, NULL);
        if (S_OK == hr)
        {
            // Get the correct REGSAM value base on?
            //
            REGSAM samDesired = KEY_READ_WRITE;

            // For enumerated components, the parameter key is the
            // instance key.
            //
            if (FIsEnumerated (m_pComponent->Class()))
            {
                hr = m_pComponent->HrOpenInstanceKey (
                            samDesired, phkey, NULL, NULL);
            }

            // For non-enumerated components, the parameter is either under
            // the service key (if the component has a service) or it is
            // under the instance key.
            //
            else
            {
                // Get the parent of the parameters key.
                //
                HKEY hkeyParent;

                #if DBG
                hkeyParent = NULL;
                #endif

                if (m_pComponent->FHasService())
                {
                    hr = m_pComponent->HrOpenServiceKey (
                            samDesired, &hkeyParent);
                }
                else
                {
                    hr = m_pComponent->HrOpenInstanceKey (
                            samDesired, &hkeyParent, NULL, NULL);
                }

                if (S_OK == hr)
                {
                    Assert (hkeyParent);

                    DWORD dwDisposition;
                    hr = HrRegCreateKeyEx (
                            hkeyParent,
                            L"Parameters",
                            REG_OPTION_NON_VOLATILE,
                            samDesired,
                            NULL,
                            phkey,
                            &dwDisposition);

                    RegCloseKey (hkeyParent);
                }
            }

            Unlock();
        }
    }

    TraceHr (ttidError, FAL, hr, FALSE,
        "CImplINetCfgComponent::OpenParamKey");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::RaisePropertyUi (
    IN HWND hwndParent,
    IN DWORD dwFlags, /* NCRP_FLAGS */
    IN IUnknown* punkContext OPTIONAL)
{
    HRESULT hr;

    // Validate parameters.
    //
    if ((!IsWindow (hwndParent) && (dwFlags & NCRP_SHOW_PROPERTY_UI)) ||
        !(dwFlags & (NCRP_QUERY_PROPERTY_UI | NCRP_SHOW_PROPERTY_UI)) ||
        ((dwFlags & NCRP_QUERY_PROPERTY_UI) && (dwFlags & NCRP_SHOW_PROPERTY_UI)))
    {
        hr = E_INVALIDARG;
    }
    else if (FBadInPtrOptional (punkContext))
    {
        hr = E_POINTER;
    }
    else
    {
        DWORD dwIfFlags = IF_NEED_WRITE_LOCK;
        BOOL fReadOnlyRasUiContext = FALSE;
        
        // Special case: for RAS UI.  We need to allow raising property
        // sheets within the context of a RAS connection even when we
        // don't have the write lock.  This is because non-admins need to be
        // able to change TCP/IP properties for their connections.  The 
        // property values will be stored in the phonebook and we won't need 
        // to make any netcfg changes anyway.  Therefore, if we have a
        // punkContext, we'll check to see if it supports the private 
        // interface that we know RAS uses when it raises properties.
        // If this interface is present, we won't require the write lock
        // to proceed
        //
        if (punkContext && !m_pINetCfg->m_WriteLock.FIsOwnedByMe ())
        {
            INetRasConnectionIpUiInfo* pRasUiInfo;
            hr = punkContext->QueryInterface (IID_INetRasConnectionIpUiInfo,
                                (PVOID*)&pRasUiInfo);
            if (S_OK == hr)
            {
                dwIfFlags &= ~IF_NEED_WRITE_LOCK;
                dwIfFlags |= IF_NEED_COMPONENT_DATA;
                fReadOnlyRasUiContext = TRUE;
                
                ReleaseObj (pRasUiInfo);
            }
            hr = S_OK;
        }
        // End special case
        
        hr = HrLockAndTestForValidInterface (dwIfFlags, NULL, NULL);
        if (S_OK == hr)
        {
            // Special case: (see above)
            //
            if (fReadOnlyRasUiContext)
            {
                if (0 == wcscmp (m_pComponent->m_pszInfId, L"ms_tcpip"))
                {
                    hr = m_pComponent->Notify.HrEnsureNotifyObjectInitialized (
                            m_pINetCfg, FALSE);
                }
                else
                {
                    hr = NETCFG_E_NO_WRITE_LOCK;
                }
            }
            // End special case
            
            if (S_OK == hr)
            {
                if (dwFlags & NCRP_QUERY_PROPERTY_UI)
                {
                    hr = m_pComponent->Notify.HrQueryPropertyUi (
                            m_pINetCfg,
                            punkContext);
                }
                else
                {
                    Assert (dwFlags & NCRP_SHOW_PROPERTY_UI);

                    hr = m_pComponent->Notify.HrShowPropertyUi (
                            m_pINetCfg,
                            hwndParent,
                            punkContext);
                }
            }

            Unlock ();
        }
    }

    TraceHr (ttidError, FAL, hr,
        (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr) ||
        (S_FALSE == hr),
        "CImplINetCfgComponent::RaisePropertyUi");
    return hr;
}

//+---------------------------------------------------------------------------
// INetCfgComponentBindings -
//
HRESULT
CImplINetCfgComponent::HrBindToOrUnbindFrom (
    IN INetCfgComponent* pIOtherComp,
    IN DWORD dwChangeFlag)
{
    HRESULT hr;

    Assert ((dwChangeFlag == NCN_ENABLE) || (dwChangeFlag == NCN_DISABLE));

    // Validate parameters.
    //
    if (FBadInPtr (pIOtherComp))
    {
        hr = E_POINTER;
    }
    else
    {
        CComponent* pLower;

        hr = HrLockAndTestForValidInterface (IF_NEED_WRITE_LOCK,
                pIOtherComp, &pLower);

        if (S_OK == hr)
        {
            const CComponent* pUpper = m_pComponent;

            // Assume the components do not bind.
            //
            hr = S_FALSE;

            if (pUpper != pLower)
            {
                CBindingSet BindingSet;

                hr = m_pINetCfg->m_pNetConfig->Core.HrGetComponentBindings (
                        pUpper, GBF_DEFAULT, &BindingSet);

                if (S_OK == hr)
                {
                    CBindPath* pBindPath;

                    // Assume we don't find the component in any bindings.
                    //
                    hr = S_FALSE;

                    for (pBindPath  = BindingSet.begin();
                         pBindPath != BindingSet.end();
                         pBindPath++)
                    {
                        // Skip bindpaths that don't contain the lower
                        // component.
                        //
                        if (!pBindPath->FContainsComponent (pLower))
                        {
                            continue;
                        }

                        hr = m_pINetCfg->m_pNetConfig->ModifyCtx.
                                HrEnableOrDisableBindPath (
                                    dwChangeFlag,
                                    pBindPath,
                                    NULL);

                        if (S_OK != hr)
                        {
                            break;
                        }
                    }
                }
            }

            Unlock();
        }
    }

    TraceHr (ttidError, FAL, hr, S_FALSE == hr,
        "CImplINetCfgComponent::HrBindToOrUnbindFrom");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::BindTo (
    IN INetCfgComponent* pIOtherComp)
{
    HRESULT hr;

    hr = HrBindToOrUnbindFrom (pIOtherComp, NCN_ENABLE);

    TraceHr (ttidError, FAL, hr, S_FALSE == hr,
        "CImplINetCfgComponent::BindTo");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::UnbindFrom (
    IN INetCfgComponent* pIOtherComp)
{
    HRESULT hr;

    hr = HrBindToOrUnbindFrom (pIOtherComp, NCN_DISABLE);

    TraceHr (ttidError, FAL, hr, S_FALSE == hr,
        "CImplINetCfgComponent::UnbindFrom");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::SupportsBindingInterface (
    IN DWORD dwFlags,
    IN PCWSTR pszInterfaceName)
{
    HRESULT hr;

    // Validate parameters.
    //
    if (!((dwFlags & NCF_UPPER) || (dwFlags & NCF_LOWER)))
    {
        hr = E_INVALIDARG;
    }
    else if (FBadInPtr (pszInterfaceName))
    {
        hr = E_POINTER;
    }
    else
    {
        hr = HrLockAndTestForValidInterface (IF_NEED_COMPONENT_DATA, NULL, NULL);
        if (S_OK == hr)
        {
            PCWSTR pszRange;

            pszRange = (dwFlags & NCF_LOWER)
                            ? m_pComponent->Ext.PszLowerRange()
                            : m_pComponent->Ext.PszUpperRange();

            hr =  (FSubstringMatch (pszRange, pszInterfaceName, NULL, NULL))
                    ? S_OK
                    : S_FALSE;

            Unlock();
        }
    }

    TraceHr (ttidError, FAL, hr, S_FALSE == hr,
        "CImplINetCfgComponent::SupportsBindingInterface");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::IsBoundTo (
    IN INetCfgComponent* pIOtherComp)
{
    HRESULT hr;

    // Validate parameters.
    //
    if (FBadInPtr (pIOtherComp))
    {
        hr = E_POINTER;
    }
    else
    {
        CComponent* pLower;

        hr = HrLockAndTestForValidInterface (IF_NEED_COMPONENT_DATA,
                pIOtherComp, &pLower);

        if (S_OK == hr)
        {
            const CComponent* pUpper = m_pComponent;

            hr = S_FALSE;  // assume it is not bound or is disabled

            if (pUpper != pLower)
            {
                CBindingSet BindingSet;

                hr = m_pINetCfg->m_pNetConfig->Core.HrGetComponentBindings (
                        pUpper, GBF_DEFAULT, &BindingSet);

                // If we think its bound, make sure it exists in at least
                // one bindpath that is not disabled.
                //
                if (S_OK == hr)
                {
                    CBindPath* pBindPath;

                    // Assume we don't fint it in at least one enabled
                    // bindpath.
                    //
                    hr = S_FALSE;

                    for (pBindPath  = BindingSet.begin();
                         pBindPath != BindingSet.end();
                         pBindPath++)
                    {
                        // If the bindpath contains the component, and it is
                        // not a disabled bindpath, it means pUpper has a
                        // path to pLower.
                        //

                        if (pBindPath->FContainsComponent (pLower) &&
                            !m_pINetCfg->m_pNetConfig->Core.
                                FIsBindPathDisabled (pBindPath,
                                    IBD_MATCH_SUBPATHS_TOO))
                        {
                            hr = S_OK;
                            break;
                        }
                    }
                }
            }

            Unlock();
        }
    }

    TraceHr (ttidError, FAL, hr, S_FALSE == hr,
        "CImplINetCfgComponent::IsBoundTo");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::IsBindableTo (
    IN INetCfgComponent* pIOtherComp)
{
    HRESULT hr;

    // Validate parameters.
    //
    if (FBadInPtr (pIOtherComp))
    {
        hr = E_POINTER;
    }
    else
    {
        CComponent* pLower;

        hr = HrLockAndTestForValidInterface (IF_NEED_COMPONENT_DATA,
                pIOtherComp, &pLower);

        if (S_OK == hr)
        {
            const CComponent* pUpper = m_pComponent;

            hr = S_FALSE;  // assume it does not bind

            if (pUpper != pLower)
            {
                CBindingSet BindingSet;

                hr = m_pINetCfg->m_pNetConfig->Core.HrGetComponentBindings (
                        pUpper, GBF_DEFAULT, &BindingSet);

                if (S_OK == hr)
                {
                    hr = (BindingSet.FContainsComponent (pLower))
                            ? S_OK : S_FALSE;
                }
            }

            Unlock();
        }
    }

    TraceHr (ttidError, FAL, hr, S_FALSE == hr,
        "CImplINetCfgComponent::IsBindableTo");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::EnumBindingPaths (
    IN DWORD dwFlags,
    OUT IEnumNetCfgBindingPath** ppIEnum)
{
    HRESULT hr;

    // Validate parameters.
    //
    if (FBadOutPtr (ppIEnum))
    {
        hr = E_POINTER;
    }
    else if ((EBP_ABOVE != dwFlags) &&
             (EBP_BELOW != dwFlags))
    {
        hr = E_INVALIDARG;
    }
    else
    {
        *ppIEnum = NULL;

        hr = HrLockAndTestForValidInterface (IF_DEFAULT, NULL, NULL);
        if (S_OK == hr)
        {
            CImplIEnumNetCfgBindingPath* pIEnum;

            // Create an empty bindpath enumerator.  We create it empty
            // before we get the set of bindings so we don't have to copy
            // the bindings.
            //
            hr = CImplIEnumNetCfgBindingPath::HrCreateInstance (
                    m_pINetCfg,
                    NULL,
                    EBPC_CREATE_EMPTY,
                    &pIEnum);

            if (S_OK == hr)
            {
                // Get the bindset and store it directly in the enumerator
                // for its exclusive use.
                //
                if (EBP_ABOVE == dwFlags)
                {
                    hr = m_pINetCfg->m_pNetConfig->Core.
                            HrGetBindingsInvolvingComponent (
                                m_pComponent,
                                GBF_DEFAULT,
                                &pIEnum->m_InternalBindSet);
                }
                else
                {
                    hr = m_pINetCfg->m_pNetConfig->Core.
                            HrGetComponentBindings (
                                m_pComponent,
                                GBF_DEFAULT,
                                &pIEnum->m_InternalBindSet);
                }

                if (S_OK == hr)
                {
                    // Must Reset so that the internal iterator is setup properly
                    // after we initialized the InternalBindSet above.
                    //
                    hr = pIEnum->Reset ();
                    Assert (S_OK == hr);

                    AddRefObj (pIEnum->GetUnknown());
                    *ppIEnum = pIEnum;
                }

                ReleaseObj (pIEnum->GetUnknown());
            }

            Unlock();
        }
    }

    TraceHr (ttidError, FAL, hr, FALSE,
        "CImplINetCfgComponent::EnumBindingPaths");
    return hr;
}

HRESULT
CImplINetCfgComponent::HrMoveBindPath (
    IN INetCfgBindingPath* pIPathSrc,
    IN INetCfgBindingPath* pIPathDst,
    IN MOVE_FLAG Flag)
{
    HRESULT hr;

    // Validate parameters.
    //
    if (FBadInPtr(pIPathSrc) || FBadInPtrOptional (pIPathDst))
    {
        hr = E_POINTER;
    }
    else
    {
        hr = HrLockAndTestForValidInterface (IF_NEED_WRITE_LOCK, NULL, NULL);
        if (S_OK == hr)
        {
            CImplINetCfgBindingPath* pISrc;
            CImplINetCfgBindingPath* pIDst;
            CBindPath SrcBindPath;
            CBindPath DstBindPath;
            CBindPath::const_iterator iterSrc;
            CBindPath::const_iterator iterDst;
            CStackEntry SrcEntry;
            CStackEntry DstEntry;

            Assert (m_pINetCfg);
            Assert (m_pINetCfg->m_pNetConfig->ModifyCtx.m_fPrepared);

            pISrc = (CImplINetCfgBindingPath*)pIPathSrc;
            pIDst = (CImplINetCfgBindingPath*)pIPathDst;

            hr = pISrc->HrIsValidInterface (IF_NEED_WRITE_LOCK, &SrcBindPath);
            if (S_OK != hr)
            {
                goto unlock;
            }

            // pIPathDst (hence pIDst) may be NULL.
            //
            if (pIDst)
            {
                hr = pIDst->HrIsValidInterface (IF_NEED_WRITE_LOCK, &DstBindPath);
                if (S_OK != hr)
                {
                    goto unlock;
                }
            }

            // The first component of both bindpaths must be this component.
            //
            if ((m_pComponent != SrcBindPath.POwner()) ||
                (pIDst && (m_pComponent != DstBindPath.POwner())))
            {
                hr = E_INVALIDARG;
                goto unlock;
            }

            if (pIDst)
            {
                // Scan down both bindpaths until we find the first components
                // that don't match.  Assume we don't find this occurance and
                // return E_INVALIDARG if we don't.
                //
                hr = E_INVALIDARG;

                for (iterSrc  = SrcBindPath.begin(), iterDst  = DstBindPath.begin();
                     iterSrc != SrcBindPath.end() && iterDst != DstBindPath.end();
                     iterSrc++, iterDst++)
                {
                    // First time through *iterSrc is guaranteed to be the
                    // sameas *iterDst because the first component in both
                    // bindpaths is m_pComponent as tested above.
                    //
                    if (*iterSrc != *iterDst)
                    {
                        SrcEntry.pLower = *iterSrc;
                        Assert (SrcEntry.pLower);

                        DstEntry.pLower = *iterDst;
                        Assert (DstEntry.pUpper);

                        Assert (SrcEntry.pUpper == DstEntry.pUpper);
                        Assert (SrcEntry.pLower != DstEntry.pLower);

                        hr = m_pINetCfg->m_pNetConfig->Core.StackTable.
                                HrMoveStackEntries (
                                    &SrcEntry,
                                    &DstEntry,
                                    Flag,
                                    &m_pINetCfg->m_pNetConfig->ModifyCtx);

                        if(SUCCEEDED(hr))
                        {
                            // Mark this component as dirty so it's bindings will be written out and 
                            // NDIS will be notified.
                            m_pINetCfg->m_pNetConfig->ModifyCtx.
                                HrDirtyComponentAndComponentsAbove(SrcEntry.pUpper);
                            m_pINetCfg->m_pNetConfig->ModifyCtx.
                                HrDirtyComponentAndComponentsAbove(DstEntry.pUpper);
                        }

                        break;
                    }

                    // Remember the upper components as we are about to
                    // advance past them.
                    //
                    SrcEntry.pUpper = *iterSrc;
                    Assert (SrcEntry.pUpper);

                    DstEntry.pUpper = *iterDst;
                    Assert (SrcEntry.pUpper);

                    Assert (SrcEntry.pUpper == DstEntry.pUpper);
                }
            }
            else
            {
                SrcEntry.pUpper = SrcBindPath.POwner();
                Assert ((SrcBindPath.begin() + 1) != SrcBindPath.end());
                SrcEntry.pLower = *(SrcBindPath.begin() + 1);

                hr = m_pINetCfg->m_pNetConfig->Core.StackTable.
                        HrMoveStackEntries (
                            &SrcEntry,
                            NULL,
                            Flag,
                            &m_pINetCfg->m_pNetConfig->ModifyCtx);

                if(SUCCEEDED(hr))
                {
                    // Mark this component as dirty so it's bindings will be written out and 
                    // NDIS will be notified.
                    m_pINetCfg->m_pNetConfig->ModifyCtx.
                        HrDirtyComponentAndComponentsAbove(SrcEntry.pUpper);
                }
            }

unlock:
            Unlock();
        }
    }
    TraceHr (ttidError, FAL, hr, FALSE,
        "CImplINetCfgComponent::HrMoveBindPath");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::MoveBefore (
    IN INetCfgBindingPath* pIPathSrc,
    IN INetCfgBindingPath* pIPathDst)
{
    HRESULT hr;

    hr = HrMoveBindPath (pIPathSrc, pIPathDst, MOVE_BEFORE);

    TraceHr (ttidError, FAL, hr, FALSE,
        "CImplINetCfgComponent::MoveBefore");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::MoveAfter (
    IN INetCfgBindingPath* pIPathSrc,
    IN INetCfgBindingPath* pIPathDst)
{
    HRESULT hr;

    hr = HrMoveBindPath (pIPathSrc, pIPathDst, MOVE_AFTER);

    TraceHr (ttidError, FAL, hr, FALSE,
        "CImplINetCfgComponent::MoveAfter");
    return hr;
}

//+---------------------------------------------------------------------------
// INetCfgComponentPrivate -
//
STDMETHODIMP
CImplINetCfgComponent::QueryNotifyObject (
    IN REFIID riid,
    OUT VOID** ppvObject)
{
    HRESULT hr;

    // Validate parameters.
    //
    if (FBadInPtr(&riid) || FBadOutPtr (ppvObject))
    {
        hr = E_POINTER;
    }
    else
    {
        *ppvObject = NULL;

        hr = HrLockAndTestForValidInterface (IF_NEED_COMPONENT_DATA, NULL, NULL);
        if (S_OK == hr)
        {
            hr = m_pComponent->Notify.QueryNotifyObject (
                    m_pINetCfg, riid, ppvObject);

            Unlock();
        }
    }

    TraceHr (ttidError, FAL, hr, FALSE,
        "CImplINetCfgComponent::QueryNotifyObject");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::SetDirty ()
{
    HRESULT hr;

    hr = HrLockAndTestForValidInterface (IF_NEED_WRITE_LOCK, NULL, NULL);
    if (S_OK == hr)
    {
        hr = m_pINetCfg->m_pNetConfig->ModifyCtx.HrDirtyComponent(
                m_pComponent);

        Unlock ();
    }

    TraceHr (ttidError, FAL, hr, FALSE, "CImplINetCfgComponent::SetDirty");
    return hr;
}

STDMETHODIMP
CImplINetCfgComponent::NotifyUpperEdgeConfigChange ()
{
    HRESULT hr;

    hr = HrLockAndTestForValidInterface (IF_NEED_WRITE_LOCK, NULL, NULL);
    if (S_OK == hr)
    {
        hr = m_pINetCfg->m_pNetConfig->ModifyCtx.
                HrDirtyComponentAndComponentsAbove (m_pComponent);

        Unlock ();
    }
    TraceHr (ttidError, FAL, hr, FALSE,
        "CImplINetCfgComponent::NotifyUpperEdgeConfigChange");
    return hr;
}