text: //+---------------------------------------------------------------------------
  //
  //  Microsoft Windows
  //  Copyright (C) Microsoft Corporation, 1992-2001.
  //
  //  File:       A D A P T E R . C P P
  //
  //  Contents:   Physical adapter class definition.
  //
  //  Notes:
  //
  //  Author:     Alok Sinha
  //
  //----------------------------------------------------------------------------
  
  #include "adapter.h"
  #include "common.h"
  
  #ifdef  CUSTOM_EVENTS
  #include "public.h"
  #endif
  
  //+---------------------------------------------------------------------------
  //
  // Function:  CMuxPhysicalAdapter::CMuxPhysicalAdapter
  //
  // Purpose:   Constructor for class CMuxPhysicalAdapter
  //
  // Arguments: None
  //
  // Returns:
  //
  // Notes:
  //
  
  CMuxPhysicalAdapter::CMuxPhysicalAdapter (INetCfg *pnc,
                                            GUID *pguidAdapter)
  {
  
      TraceMsg( L"-->CMuxPhysicalAdapter::CMuxPhysicalAdapter(Constructor).\n" );
  
      m_pnc = pnc;
      m_pnc->AddRef();
  
      CopyMemory( &m_guidAdapter,
                  pguidAdapter,
                  sizeof(GUID) );
  
      TraceMsg( L"<--CMuxPhysicalAdapter::CMuxPhysicalAdapter(Constructor).\n" );
  }
  
  //+---------------------------------------------------------------------------
  //
  // Function:  CMuxPhysicalAdapter::~CMuxPhysicalAdapter
  //
  // Purpose:   Destructor for class CMuxPhysicalAdapter
  //
  // Arguments: None
  //
  // Returns:
  //
  // Notes:
  //
  
  CMuxPhysicalAdapter::~CMuxPhysicalAdapter (VOID)
  {
      CMuxVirtualMiniport  *pMiniport;
      DWORD                dwMiniportCount;
      DWORD                i;
  
  
      TraceMsg( L"-->CMuxPhysicalAdapter::~CMuxPhysicalAdapter(Destructor).\n" );
  
      //
      // Delete all the instances representing the virtual miniports.
      // We are only deleting the class instances, not uninstalling the
      // the virtual miniports.
      //
  
      dwMiniportCount = m_MiniportList.ListCount();
  
      for (i=0; i < dwMiniportCount; ++i) {
  
          m_MiniportList.Remove( &pMiniport );
          delete pMiniport;
      }
  
      dwMiniportCount = m_MiniportsToAdd.ListCount();
  
      for (i=0; i < dwMiniportCount; ++i) {
  
          m_MiniportsToAdd.Remove( &pMiniport );
          delete pMiniport;
      }
  
      dwMiniportCount = m_MiniportsToRemove.ListCount();
  
      for (i=0; i < dwMiniportCount; ++i) {
  
          m_MiniportsToRemove.Remove( &pMiniport );
          delete pMiniport;
      }
  
      ReleaseObj( m_pnc );
  
      TraceMsg( L"<--CMuxPhysicalAdapter::~CMuxPhysicalAdapter(Destructor).\n" );
  
  }
  
  //+---------------------------------------------------------------------------
  //
  // Function:  CMuxPhysicalAdapter::LoadConfiguration
  //
  // Purpose:   Read the registry to get the device IDs of the 
  //            virtual miniports installed on the adapter and
  //            crate an instance to represent each virtual miniport.
  //
  // Arguments: None
  //
  // Returns: S_OK on success, otherwise an error code.
  //
  // Notes:
  //
  
  HRESULT CMuxPhysicalAdapter::LoadConfiguration (VOID)
  {
      HKEY                    hkeyAdapterGuid;
      WCHAR                   szAdapterGuidKey[MAX_PATH+1];
      WCHAR                   szAdapterGuid[MAX_PATH+1];
      LPWSTR                  lpMiniportList;
      LPWSTR                  lpMiniport;
      LPWSTR                  lpMiniportGuid;
      DWORD                   dwDisp;
      CMuxVirtualMiniport     *pMiniport;
      GUID                    guidMiniport;
      DWORD                   dwBytes;
      LONG                    lResult;
  
      TraceMsg( L"-->CMuxPhysicalAdapter::LoadConfiguration.\n" );
  
      //
      // Build the registry key using the adapter guid under which 
      // device IDs of the virtual miniports are stored.
      //
  
      StringFromGUID2( m_guidAdapter,
                      szAdapterGuid,
                      MAX_PATH+1 );
  
      swprintf( szAdapterGuidKey,
                L"%s\\%s",
                c_szAdapterList,
                szAdapterGuid );
  
      lResult = RegCreateKeyExW( HKEY_LOCAL_MACHINE,
                                  szAdapterGuidKey,
                                  0,
                                  NULL,
                                  REG_OPTION_NON_VOLATILE,
                                  KEY_ALL_ACCESS,
                                  NULL,
                                  &hkeyAdapterGuid,
                                  &dwDisp);
  
      if ( lResult == ERROR_SUCCESS ) {
  
          //
          // If dwDisp indicates that a new key is created then, we know there
          // is no virtual miniport currently listed underneath and we simply
          // return.
          //
  
          if ( dwDisp != REG_CREATED_NEW_KEY ) {
  
              dwBytes = 0;
              lResult =  RegQueryValueExW(
                                          hkeyAdapterGuid,
                                          c_szUpperBindings,
                                          NULL,
                                          NULL,
                                          NULL,
                                          &dwBytes );
  
              lpMiniportList = (LPWSTR)calloc( dwBytes, 1 );
  
              if ( lpMiniportList ) {
  
                  lResult =  RegQueryValueExW(
                                      hkeyAdapterGuid,
                                      c_szUpperBindings,
                                      NULL,
                                      NULL,
                                      (LPBYTE)lpMiniportList,
                                      &dwBytes );
  
                  if ( lResult == ERROR_SUCCESS ) {
  
                      lpMiniport = lpMiniportList;
  
  #ifndef PASSTHRU_NOTIFY
  
                      //
                      // In case of mux, c_szUpperBindings is a multi_sz string.
                      //
  
                      while ( wcslen(lpMiniport) ) {
  
                          lpMiniportGuid = RemoveDevicePrefix( lpMiniport );
  
                          TraceMsg( L"   Loading configuration for miniport %s...\n",
                          lpMiniportGuid );
  
                          if ( lpMiniportGuid ) {
  
                              CLSIDFromString( lpMiniportGuid,
                                               &guidMiniport );
           
                              //
                              // Create an instance representing the virtual miniport.
                              //
  
                              pMiniport = new CMuxVirtualMiniport( m_pnc,
                                                                   &guidMiniport,
                                                                   &m_guidAdapter );
  
                              if ( pMiniport ) {
  
                                  //
                                  // Load any miniport specific configuration.
                                  //
  
                                  pMiniport->LoadConfiguration();
  
                                  //
                                  // Save the miniport instance in a list.
                                  //
  
                                  m_MiniportList.Insert( pMiniport,
                                                         guidMiniport );
  
                              }
  
                              free( lpMiniportGuid );
                          }
  
                          //
                          // Get next miniport guid.
                          //
  
                          lpMiniport += wcslen(lpMiniport) + 1;
                      }
  
  #else
  
                      //
                      // In case of the passthru driver, c_szUpperBindings is
                      // a reg_sz string.
                      //
  
                      lpMiniportGuid = RemoveDevicePrefix( lpMiniport );
  
                      TraceMsg( L"   Loading configuration for miniport %s...\n",
                                lpMiniportGuid );
  
                      if ( lpMiniportGuid ) {
  
                          CLSIDFromString( lpMiniportGuid,
                                           &guidMiniport );
  
                          //
                          // Create an instance representing the virtual miniport.
                          //
  
                          pMiniport = new CMuxVirtualMiniport( m_pnc,
                                                               &guidMiniport,
                                                               &m_guidAdapter );
  
                          if ( pMiniport ) {
  
                              //
                              // Load any miniport specific configuration.
                              //
  
                              pMiniport->LoadConfiguration();
  
                              //
                              // Save the miniport instance in a list.
                              //
  
                              m_MiniportList.Insert( pMiniport,
                                                     guidMiniport );
                          }
  
                          free( lpMiniportGuid );
                      }
  #endif
                  }
                  else {
                      TraceMsg( L"   Failed to read the registry value: %s.\n",
                                c_szUpperBindings );
                  }
  
                  free( lpMiniportList );
              }
              else {
                  lResult = ERROR_NOT_ENOUGH_MEMORY;
              }
          }
  
          RegCloseKey( hkeyAdapterGuid );
      }
      else {
  
          TraceMsg( L"   Failed to open the registry key: %s.\n",
                    szAdapterGuidKey );
      }
  
      TraceMsg( L"<--CMuxPhysicalAdapter::LoadConfiguration(HRESULT = %x).\n",
                HRESULT_FROM_WIN32(lResult) );
  
      return HRESULT_FROM_WIN32(lResult);
  }
  
  //+---------------------------------------------------------------------------
  //
  // Function:  CMuxPhysicalAdapter::GetAdapterGUID
  //
  // Purpose:   Returns the adapter GUID.
  //
  // Arguments:
  //          OUT pguidAdapter:  GUID of the adapter returned.
  //
  // Returns: None.
  //
  // Notes:
  //
  
  VOID CMuxPhysicalAdapter::GetAdapterGUID (GUID *pguidAdapter)
  {
      TraceMsg( L"-->CMuxPhysicalAdapter::GetAdapterGUID.\n" );
  
      CopyMemory( pguidAdapter,
                  &m_guidAdapter,
                  sizeof(GUID) );
  
      TraceMsg( L"<--CMuxPhysicalAdapter::GetAdapterGUID.\n" );
  }
  
  //+---------------------------------------------------------------------------
  //
  // Function:  CMuxPhysicalAdapter::AddMiniport
  //
  // Purpose:   Puts the miniport instance into the list of newly added miniports.
  //
  // Arguments:
  //          IN pMiniport:  A newly create miniport instance.
  //
  // Returns: S_OK on success, otherwize and error code.
  //
  // Notes:
  //
  
  HRESULT CMuxPhysicalAdapter::AddMiniport (CMuxVirtualMiniport *pMiniport)
  {
      GUID    guidMiniport;
      HRESULT hr;
  
      TraceMsg( L"-->CMuxPhysicalAdapter::AddMiniport.\n" );
  
      pMiniport->GetMiniportGUID( &guidMiniport );
  
      hr = m_MiniportsToAdd.Insert( pMiniport,
                                    guidMiniport );
  
      TraceMsg( L"<--CMuxPhysicalAdapter::AddMiniport(HRESULT = %x).\n",
                hr );
  
      return hr;
  }
  
  //+---------------------------------------------------------------------------
  //
  // Function:  CMuxPhysicalAdapter::RemoveMiniport
  //
  // Purpose:   Remove a specified miniport instance from the list and
  //            uninstalls the corresponding virtual miniport.
  //
  // Arguments:
  //          IN pguidMiniportToRemove: GUID of the miniport to be removed
  //                                    and uninstalled. If it is NULL then,
  //                                    the first miniport instance is removed.
  //
  // Returns: S_OK on success, otherwize and error code.
  //
  // Notes:
  //
  
  HRESULT CMuxPhysicalAdapter::RemoveMiniport (GUID *pguidMiniportToRemove)
  {
      CMuxVirtualMiniport  *pMiniport;
      GUID                 guidMiniport;
      HRESULT              hr;
  
      TraceMsg( L"-->CMuxPhysicalAdapter::RemoveMiniport.\n" );
  
      //
      // If miniport GUID specified then, delete that one.
      //
  
      if ( pguidMiniportToRemove ) {
  
          hr = m_MiniportList.RemoveByKey( *pguidMiniportToRemove,
                                        &pMiniport );
      }
      else {
  
          //
          // No GUID specified, so we just delete the first one.
          //
  
          hr = m_MiniportList.Remove( &pMiniport );
      }
  
      if ( hr == S_OK ) {
  
          pMiniport->GetMiniportGUID( &guidMiniport );
  
          m_MiniportsToRemove.Insert( pMiniport,
                                      guidMiniport );
          pMiniport->DeInstall();
      }
  
      TraceMsg( L"<--CMuxPhysicalAdapter::RemoveMiniport(HRESULT = %x).\n",
                hr );
  
      return hr;
  }
  
  //+---------------------------------------------------------------------------
  //
  // Function:  CMuxPhysicalAdapter::Remove
  //
  // Purpose:   Uninstall all the instances of virtual miniports.
  //
  // Arguments: None
  //
  // Returns: S_OK.
  //
  // Notes:
  //
  
  HRESULT CMuxPhysicalAdapter::Remove (VOID)
  {
      CMuxVirtualMiniport  *pMiniport;
      GUID                    guidMiniport;
      DWORD                   dwMiniportCount;
      DWORD                   i;
  
      TraceMsg( L"-->CMuxPhysicalAdapter::Remove.\n" );
  
      dwMiniportCount = m_MiniportList.ListCount();
  
      TraceMsg ( L"   Removing %d miniports.\n",
                 dwMiniportCount );
  
      for (i=0; i < dwMiniportCount; ++i) {
  
          m_MiniportList.Remove( &pMiniport );
          pMiniport->GetMiniportGUID( &guidMiniport );
          m_MiniportsToRemove.Insert( pMiniport,
                                      guidMiniport );
          pMiniport->DeInstall();
      }
  
      TraceMsg( L"<--CMuxPhysicalAdapter::Remove(HRESULT = %x).\n",
                S_OK );
  
      return S_OK;
  }
  
  //+---------------------------------------------------------------------------
  //
  // Function:  CMuxPhysicalAdapter::ApplyRegistryChanges
  //
  // Purpose:   Update the registry depending on the actions performed.
  //
  // Arguments:
  //          IN eApplyAction:  Action that was last performed.
  //                            
  //
  // Returns: S_OK.
  //
  // Notes:
  //        More than one action could have been performed by the user
  //        but this function is called only once at the end. So, the argument
  //        only denotes the very last action performed. For example, if the 
  //        user deletes one miniport and adds two miniports then, the argument
  //        will denote an add action.
  //
  
  HRESULT CMuxPhysicalAdapter::ApplyRegistryChanges (ConfigAction eApplyAction)
  {
      HKEY                    hkeyAdapterList;
      HKEY                    hkeyAdapterGuid;
      WCHAR                   szAdapterGuid[MAX_PATH+1];
      CMuxVirtualMiniport     *pMiniport = NULL;
      DWORD                   dwMiniportCount;
      DWORD                   dwDisp;
      DWORD                   i;
      LONG                    lResult;
      HRESULT                 hr;
  
  
      TraceMsg( L"-->CMuxPhysicalAdapter::ApplyRegistryChanges.\n" );
  
      //
      // Open/create and then close the registry key to ensure that it does exist.
      //
  
      StringFromGUID2( m_guidAdapter,
                       szAdapterGuid,
                       MAX_PATH+1 );
  
      lResult = RegCreateKeyExW( HKEY_LOCAL_MACHINE,
                                 c_szAdapterList,
                                 0,
                                 NULL,
                                 REG_OPTION_NON_VOLATILE,
                                 KEY_ALL_ACCESS,
                                 NULL,
                                 &hkeyAdapterList,
                                 &dwDisp);
  
  
      if ( lResult == ERROR_SUCCESS ) {
  
          lResult = RegCreateKeyExW( hkeyAdapterList,
                                     szAdapterGuid,
                                     0,
                                     NULL,
                                     REG_OPTION_NON_VOLATILE,
                                     KEY_ALL_ACCESS,
                                     NULL,
                                     &hkeyAdapterGuid,
                                     &dwDisp);
  
          if ( lResult == ERROR_SUCCESS ) {
  
              RegCloseKey( hkeyAdapterGuid );
          }
          else {
              TraceMsg( L"   Failed to create/open the registry key: %s\\%s.\n",
                        c_szAdapterList, szAdapterGuid );
          }
  
          RegCloseKey( hkeyAdapterList );
      }
      else {
  
          TraceMsg( L"   Failed to open the registry key: %s.\n",
                    c_szAdapterList );
      }
  
      //
      // Update the registry in case there were new miniports installed.
      //
  
      hr = HRESULT_FROM_WIN32( lResult );
  
      dwMiniportCount = m_MiniportsToAdd.ListCount();
  
      TraceMsg( L"   Applying registry changes when %d miniports added.\n",
                dwMiniportCount );
  
      for (i=0; i < dwMiniportCount; ++i) {
  
          m_MiniportsToAdd.Find( i,
                                 &pMiniport );
  
          //
          // Do virtual miniport specific registry changes.
          //
          // We need to tell the miniport instance explicitly what the action
          // is.
          //
  
          hr = pMiniport->ApplyRegistryChanges( eActAdd );
  
          if ( hr != S_OK ) {
  
              TraceMsg( L"   Failed to apply registry changes to miniport(%d).\n",
                        i );
  
          }
      }
  
  
  
      //
      // Update the registry in case one or more miniports were uninstalled.
      //
  
      dwMiniportCount = m_MiniportsToRemove.ListCount();
  
      TraceMsg( L"   Applying registry changes when %d miniports removed.\n",
                dwMiniportCount );
  
      for (i=0; i < dwMiniportCount; ++i) {
  
          m_MiniportsToRemove.Find( i,
                                    &pMiniport );
  
          //
          // Do virtual miniport specific registry changes.
          //
          // We need to tell the miniport instance explicitly what the action
          // is.
          //
  
          hr = pMiniport->ApplyRegistryChanges( eActRemove );
  
          if ( hr != S_OK ) {
  
              TraceMsg( L"   Failed to apply registry changes to miniport(%d).\n",
                        i );
  
          }
      }
  
      //
      // If the adapter is being removed or the protocol is being uninstalled,
      // delete the adatper registry key.
      //
  
      if ( eApplyAction == eActRemove ) {
  
          //
          // Delete the adapter key.
          //
  
          lResult = RegCreateKeyExW( HKEY_LOCAL_MACHINE,
                                     c_szAdapterList,
                                     0,
                                     NULL,
                                     REG_OPTION_NON_VOLATILE,
                                     KEY_ALL_ACCESS,
                                     NULL,
                                     &hkeyAdapterList,
                                     &dwDisp);
  
          if ( lResult == ERROR_SUCCESS ) {
  
              TraceMsg( L"   Deleting the registry key: %s.\n", szAdapterGuid );
  
              RegDeleteKeyW( hkeyAdapterList,
                             szAdapterGuid );
          }
      }
  
      TraceMsg( L"<--CMuxPhysicalAdapter::ApplyRegistryChanges(HRESULT = %x).\n",
              S_OK );
  
      return S_OK;
  }
  
  //+---------------------------------------------------------------------------
  //
  // Function:  CMuxPhysicalAdapter::ApplyPnpChanges
  //
  // Purpose:   Apply the PnP changes depending on the actions performed.
  //
  // Arguments:
  //          IN pfCallback  :  SendPnpConfig Callback interface.
  //          IN eApplyAction:  Action that was last performed.
  //                            
  //
  // Returns: S_OK.
  //
  // Notes:
  //        More than one action could have been performed by the user
  //        but this function is called only once at the end. So, the argument
  //        only denotes the very last action performed. For example, if the 
  //        user deletes one miniport and adds two miniports then, the argument
  //        will denote an add action.
  //
  
  HRESULT CMuxPhysicalAdapter::ApplyPnpChanges(
                      INetCfgPnpReconfigCallback *pfCallback,
                      ConfigAction eApplyAction)
  {
      CMuxVirtualMiniport     *pMiniport;
      GUID                    guidMiniport;
      WCHAR                   szMiniportGuid[MAX_PATH+1];
      LPWSTR                  lpDevice;
      DWORD                   dwMiniportCount;
      DWORD                   i;
      DWORD                   dwBytes;
      INetCfgComponent        *pncc;
      HRESULT                 hr;
  
  #ifdef CUSTOM_EVENTS    
      LPWSTR                  lpszBindName;
      PNOTIFY_CUSTOM_EVENT       lppnpEvent;
  #endif
  
      TraceMsg( L"-->CMuxPhysicalAdapter::ApplyPnpChanges.\n" );
  
  #ifdef CUSTOM_EVENTS    
  
      //
      // Find the instance of the adapter to get its bindname.
      //
  
      hr = HrFindInstance( m_pnc,
                           m_guidAdapter,
                           &pncc );
  
      if ( hr == S_OK ) {
  
          hr = pncc->GetBindName( &lpszBindName );
  
          if ( hr != S_OK ) {
              TraceMsg( L"  GetBindName failed.(HRESULT = %x). PnP changes will not "
                        L"be applied and the driver will not be notified.\n",
                        hr );
          }
  
          ReleaseObj( pncc );
      }
      else {
          TraceMsg( L"  PnP changes will not "
                 L"be applied and the driver will not be notified.\n",
                 hr );
      }
  
  #endif    
  
      dwMiniportCount = m_MiniportsToAdd.ListCount();
  
      TraceMsg( L"   Applying PnP changes to %d new miniports.\n",
              dwMiniportCount );
  
      for (i=0; i < dwMiniportCount; ++i) {
  
          m_MiniportsToAdd.Remove( &pMiniport );
  
          pMiniport->GetMiniportGUID( &guidMiniport );
  
          m_MiniportList.Insert( pMiniport,
                                 guidMiniport );
  
          //
          // Do miniport specific Pnp Changes when they are added.
          //
  
          hr = pMiniport->ApplyPnpChanges( pfCallback,
                                           eActAdd );
  
  #ifdef CUSTOM_EVENTS
  
  
          //
          // Notify the driver that one or more virtual miniports have been added.
          //
  
          StringFromGUID2( guidMiniport,
                           szMiniportGuid,
                           MAX_PATH+1 );
          lpDevice = AddDevicePrefix( szMiniportGuid );
  
          if ( lpDevice ) {
  
              dwBytes = sizeof(NOTIFY_CUSTOM_EVENT) +
                        ((wcslen(lpDevice) + 1) * sizeof(WCHAR));
  
              lppnpEvent = (PNOTIFY_CUSTOM_EVENT)malloc( dwBytes );
  
              if ( lppnpEvent ) {
  
                  lppnpEvent->uSignature = NOTIFY_SIGNATURE;
                  lppnpEvent->uEvent = MUX_CUSTOM_EVENT;
                  wcscpy( lppnpEvent->szMiniport,
                         lpDevice );
  
                  hr = pfCallback->SendPnpReconfig( NCRL_NDIS,
                                                   c_szMuxService,
                                                   lpszBindName,
                                                   (PVOID)lppnpEvent,
                                                   dwBytes );
  
                  TraceMsg( L"   INetCfgPnpReconfigCallback->SendPnpReconfig returned "
                           L"%#x.\n",
                           hr );
  
                  if ( hr != S_OK ) {
  
                    TraceMsg( L"   Failed to apply Pnp changes, miniport(%d).\n",
                              i );
  
                  }
  
                  free( lppnpEvent );
              }
              free( lpDevice );
          }
  #endif        
      }
  
      dwMiniportCount = m_MiniportsToRemove.ListCount();
  
      TraceMsg( L"   Applying PnP changes to %d removed miniports.\n",
              dwMiniportCount );
  
      for (i=0; i < dwMiniportCount; ++i) {
  
          m_MiniportsToRemove.Remove( &pMiniport );
  
          pMiniport->GetMiniportGUID( &guidMiniport );
  
          //
          // Do miniport specific Pnp Changes when they are uninstalled.
          //
  
          hr = pMiniport->ApplyPnpChanges( pfCallback,
                                           eActRemove );
  
          delete pMiniport;
  
  #ifdef CUSTOM_EVENTS
  
          //
          // Notify the driver that one or more virtual miniports have been
          // uninstalled.
          //
          // We can't notify the driver in case the adapter or the protocol is
          // being uninstalled because the binding handle doesn't exist.
          //
  
          if ( eApplyAction != eActRemove ) {
  
              StringFromGUID2( guidMiniport,
                               szMiniportGuid,
                               MAX_PATH+1 );
              lpDevice = AddDevicePrefix( szMiniportGuid );
  
              if ( lpDevice ) {
  
                  dwBytes = sizeof(NOTIFY_CUSTOM_EVENT) +
                           ((wcslen(lpDevice) + 1) * sizeof(WCHAR));
  
                  lppnpEvent = (PNOTIFY_CUSTOM_EVENT)malloc( dwBytes );
  
                  if ( lppnpEvent ) {
  
                      lppnpEvent->uSignature = NOTIFY_SIGNATURE;
                      lppnpEvent->uEvent = MUX_CUSTOM_EVENT;
                      wcscpy( lppnpEvent->szMiniport,
                            lpDevice );
  
                      hr = pfCallback->SendPnpReconfig( NCRL_NDIS,
                                                      c_szMuxService,
                                                      lpszBindName,
                                                      (PVOID)lppnpEvent,
                                                      dwBytes );
                      TraceMsg( L"   INetCfgPnpReconfigCallback->SendPnpReconfig returned "
                              L"%#x.\n",
                              hr );
  
                      if ( hr != S_OK ) {
  
                          TraceMsg( L"   Failed to apply Pnp changes, miniport(%d).\n",
                                 i );
  
                      }
  
                      free( lppnpEvent );
                  }
  
                  free( lpDevice );
              }
          }
  #endif         
  
      }
  
  #ifdef CUSTOM_EVENTS    
      CoTaskMemFree( lpszBindName );
  #endif
  
      TraceMsg( L"<--CMuxPhysicalAdapter::ApplyPnpChanges(HRESULT = %x).\n",
              S_OK );
  
      return S_OK;
  }
  
  //+---------------------------------------------------------------------------
  //
  // Function:  CMuxPhysicalAdapter::CancelChanges
  //
  // Purpose:   Cancel any changes made.
  //
  // Arguments: None
  //                            
  //
  // Returns: S_OK.
  //
  // Notes:
  //
  
  HRESULT CMuxPhysicalAdapter::CancelChanges (VOID)
  {
      TraceMsg( L"-->CMuxPhysicalAdapter::CancelChanges.\n" );
  
      TraceMsg( L"<--CMuxPhysicalAdapter::CancelChanges(HRESULT = %x).\n",
              S_OK );
  
      return S_OK;
  }
  
  //+---------------------------------------------------------------------------
  //
  // Function:  CMuxPhysicalAdapter::AllMiniportsRemoved
  //
  // Purpose:   Find out if there is no miniport installed on the adapter.
  //
  // Arguments: None
  //                            
  //
  // Returns: TRUE if all the miniports associated with this adapter have been
  //          uninstalled and there is none pending to be added, otherwise FALSE.
  //
  // Notes:
  //
  
  BOOL  CMuxPhysicalAdapter::AllMiniportsRemoved (VOID)
  {
    return (m_MiniportList.ListCount() + m_MiniportsToAdd.ListCount()) == 0;
  }
  