//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1999. // // File: M O D I F Y . C P P // // Contents: Routines used to setup modifications to the network // configuration. // // Notes: // // Author: shaunco 15 Jan 1999 // //---------------------------------------------------------------------------- #include #pragma hdrstop #include "classinst.h" #include "filtdevs.h" #include "guisetup.h" #include "inetcfg.h" #include "lockdown.h" #include "ncmsz.h" #include "ncreg.h" #include "ncsvc.h" #include "ndispnp.h" #include "netcfg.h" #include "persist.h" #include "pnpbind.h" #include "pszarray.h" #include "util.h" #include "wscfg.h" #include "ncwins.h" #include "ncperms.h" CNetConfig* CModifyContext::PNetConfig () { TraceFileFunc(ttidNetcfgBase); CNetConfig* pNetConfig; Assert ((LONG_PTR)this > FIELD_OFFSET(CNetConfig, ModifyCtx)); // Get our containing CNetConfig pointer. // pNetConfig = CONTAINING_RECORD(this, CNetConfig, ModifyCtx); pNetConfig->Core.DbgVerifyData (); return pNetConfig; } HRESULT CModifyContext::HrDirtyComponent ( IN const CComponent* pComponent) { TraceFileFunc(ttidNetcfgBase); Assert (S_OK == m_hr); Assert (m_fPrepared); Assert (pComponent); m_hr = m_DirtyComponents.HrInsertComponent (pComponent, INS_IGNORE_IF_DUP | INS_SORTED); #if DBG m_fComponentExplicitlyDirtied = TRUE; #endif TraceHr (ttidError, FAL, m_hr, FALSE, "CModifyContext::HrDirtyComponentAndComponentsAbove"); return m_hr; } HRESULT CModifyContext::HrDirtyComponentAndComponentsAbove ( IN const CComponent* pComponent) { TraceFileFunc(ttidNetcfgBase); GCCONTEXT Ctx; Assert (S_OK == m_hr); Assert (m_fPrepared); Assert (pComponent); // And insert the component itself. // m_hr = HrDirtyComponent (pComponent); // Only dirty the ones above if this component doesn't have the // NCF_DONTEXPOSELOWER characteristic. // if ((S_OK == m_hr) && !(pComponent->m_dwCharacter & NCF_DONTEXPOSELOWER)) { // Initialize the members of our context structure for recursion. // ZeroMemory (&Ctx, sizeof(Ctx)); Ctx.pStackTable = &(PNetConfig()->Core.StackTable); Ctx.pComponents = &m_DirtyComponents; // Insert all of the component above. // GetComponentsAboveComponent (pComponent, &Ctx); m_hr = Ctx.hr; } TraceHr (ttidError, FAL, m_hr, FALSE, "CModifyContext::HrDirtyComponentAndComponentsAbove"); return m_hr; } HRESULT CModifyContext::HrApplyIfOkOrCancel ( IN BOOL fApply) { TraceFileFunc(ttidNetcfgBase); HRESULT hr; CNetConfig* pNetConfig; Assert (m_fPrepared); pNetConfig = PNetConfig(); // Only apply if the context result is S_OK. // if (fApply && (S_OK == m_hr)) { // Setupapi calls that we make during ApplyChanges have the // potential to return control to our clients windows message loop. // When this happens, and our clients are poorly written, they // may try to re-enter us on the same thread. This is disaster // waiting to happen, so we need to prevent it by raising our // reentrancy protection level before we start apply changes. // pNetConfig->Notify.PINetCfg()->RaiseRpl (RPL_DISALLOW); ApplyChanges (); pNetConfig->Notify.PINetCfg()->LowerRpl (RPL_DISALLOW); // Delete those components from m_CoreStartedWith that are not // in the current core and reset the modify context. // m_CoreStartedWith.Components.FreeComponentsNotInOtherComponentList ( &pNetConfig->Core.Components); m_CoreStartedWith.Clear(); hr = S_OK; // Return the correct HRESULT to the caller. If we've successfully // applied, but need a reboot, return so. // if (m_fRebootRecommended || m_fRebootRequired) { hr = NETCFG_S_REBOOT; } } else { // Cancel and release all notify objects. Do this for what is // in the core as well as what we started with. (There will // be some overlap, but they will only be released once.) // We need to do both sets so we don't miss releasing components // that have been removed. (Removed components won't be in // current core, but they will be in the core that we started // with.) Likewise, if we just released the core we started with, // we'd miss releasing those components that were added.) // pNetConfig->Notify.ReleaseAllNotifyObjects (pNetConfig->Core.Components, TRUE); pNetConfig->Notify.ReleaseAllNotifyObjects (m_CoreStartedWith.Components, TRUE); // Delete those components from m_CoreStartedWith that are not // in the current core. Then delete all the components in the // current core and reload from our persistent storage. // (This has the nice effect of invalidating all outstanding // INetCfgComponent interfaces.) // m_CoreStartedWith.Components.FreeComponentsNotInOtherComponentList ( &pNetConfig->Core.Components); pNetConfig->Core.Free (); // Eject both cores (didn't you just know this metaphor was coming ;-) // and reload the core from our persisted binary. This magically, // and completely rolls everything back. // m_CoreStartedWith.Clear(); pNetConfig->Core.Clear(); // Return reason for failure through hr. // hr = m_hr; // Reload the configuration and, if successful, it means m_hr // will be S_OK. If unsuccessful, m_hr will be the error and will // prevent subsequent operations. // m_hr = HrLoadNetworkConfigurationFromRegistry (KEY_READ, pNetConfig); } // Very important to set m_fPrepared back to FALSE so that HrPrepare gets // called for the next modifcation and correctly copy the core etc. // m_fPrepared = FALSE; m_AddedBindPaths.Clear(); m_DeletedBindPaths.Clear(); m_DirtyComponents.Clear(); #if DBG m_fComponentExplicitlyDirtied = FALSE; #endif Assert (!m_fPrepared); Assert (m_CoreStartedWith.FIsEmpty()); Assert (m_AddedBindPaths.FIsEmpty()); Assert (m_DeletedBindPaths.FIsEmpty()); Assert (m_DirtyComponents.FIsEmpty()); Assert (0 == m_ulRecursionDepth); Assert (!m_fComponentExplicitlyDirtied); Assert ((S_OK == hr) || (NETCFG_S_REBOOT == hr) || FAILED(hr)); TraceHr (ttidError, FAL, hr, NETCFG_S_REBOOT == hr, "CModifyContext::HrApplyIfOkOrCancel"); return hr; } HRESULT CModifyContext::HrPrepare () { TraceFileFunc(ttidNetcfgBase); Assert (S_OK == m_hr); Assert (!m_fPrepared); Assert (m_CoreStartedWith.FIsEmpty()); Assert (m_AddedBindPaths.FIsEmpty()); Assert (m_DeletedBindPaths.FIsEmpty()); Assert (m_DirtyComponents.FIsEmpty()); Assert (0 == m_ulRecursionDepth); Assert (!m_fComponentExplicitlyDirtied); CNetConfig* pThis; pThis = PNetConfig(); // Prepare the bind context. This will ensure all of the external // data for all components is loaded as well as all ensuring that // all notify objects have been initialized. // m_hr = m_RegBindCtx.HrPrepare (pThis); if (S_OK != m_hr) { goto finished; } // Snapshot the current core so that we know what we started with. // We will use the differences when we apply (if we get that far). // m_hr = m_CoreStartedWith.HrCopyCore (&pThis->Core); if (S_OK != m_hr) { goto finished; } // Reserve room for 64 components in the core. // (64 * 4 = 256 bytes on 32-bit platforms) // m_hr = pThis->Core.Components.HrReserveRoomForComponents (64); if (S_OK != m_hr) { goto finished; } // Reserve room for 64 stack entries in the core. // (64 * (4 + 4) = 512 bytes on 32-bit platforms) // m_hr = pThis->Core.StackTable.HrReserveRoomForEntries (64); if (S_OK != m_hr) { goto finished; } // Reserve room in our added list for 64 bindpaths of 8 components. // (64 * 16 = 1K bytes on 32-bit platforms) // m_hr = m_AddedBindPaths.HrReserveRoomForBindPaths (64); if (S_OK != m_hr) { goto finished; } // Reserve room in our deleted list for 64 bindpaths of 8 components. // (64 * 16 = 1K bytes on 32-bit platforms) // m_hr = m_DeletedBindPaths.HrReserveRoomForBindPaths (64); if (S_OK != m_hr) { goto finished; } // Reserve room for 64 components in the dirty component list. // (64 * 4) = 256 bytes on 32-bit platforms) // m_hr = m_DirtyComponents.HrReserveRoomForComponents (64); if (S_OK != m_hr) { goto finished; } m_fPrepared = TRUE; finished: TraceHr (ttidError, FAL, m_hr, FALSE, "CModifyContext::HrPrepare"); return m_hr; } HRESULT CModifyContext::HrBeginBatchOperation () { TraceFileFunc(ttidNetcfgBase); Assert (S_OK == m_hr); Assert (m_fPrepared); Assert (0 == m_ulRecursionDepth); TraceTag (ttidBeDiag, "Begin batch operation..."); PushRecursionDepth(); return m_hr; } HRESULT CModifyContext::HrEndBatchOperation ( IN EBO_FLAG Flag) { TraceFileFunc(ttidNetcfgBase); HRESULT hr; Assert (m_fPrepared); Assert (1 == m_ulRecursionDepth); if (EBO_COMMIT_NOW == Flag) { TraceTag (ttidBeDiag, "End batch (commiting changes)..."); hr = HrPopRecursionDepth (); } else { Assert (EBO_DEFER_COMMIT_UNTIL_APPLY == Flag); TraceTag (ttidBeDiag, "End batch (deferring commit until Apply)..."); m_ulRecursionDepth = 0; hr = S_OK; } Assert (0 == m_ulRecursionDepth); TraceHr (ttidError, FAL, hr, FALSE, "CModifyContext::HrEndBatchOperation"); return hr; } VOID CModifyContext::PushRecursionDepth () { TraceFileFunc(ttidNetcfgBase); Assert (S_OK == m_hr); Assert (m_fPrepared); m_ulRecursionDepth++; } HRESULT CModifyContext::HrPopRecursionDepth () { TraceFileFunc(ttidNetcfgBase); Assert (m_fPrepared); Assert (m_ulRecursionDepth > 0); m_ulRecursionDepth--; if (0 != m_ulRecursionDepth) { return m_hr; } // We're at the top-level of the install or remove modifcation so // apply or cancel the changes depending on the state of the context // result. // HRESULT hr; hr = HrApplyIfOkOrCancel (S_OK == m_hr); TraceHr (ttidError, FAL, hr, FALSE, "CModifyContext::HrPopRecursionDepth"); return hr; } //---------------------------------------------------------------------------- // This is a convenience method to find and process Winsock Remove // section for a component which is about to be removed. HRESULT CModifyContext::HrProcessWinsockRemove(IN const CComponent *pComponent) { TraceFileFunc(ttidNetcfgBase); HINF hinf = NULL; HKEY hkeyInstance = NULL; HRESULT hr; Assert(pComponent); hr = pComponent->HrOpenInfFile(&hinf); if (S_OK == hr) { static const WCHAR c_szRemoveSectionSuffix[] = L".Remove"; // We get the remove section name and process all relevant sections WCHAR szRemoveSection[_MAX_PATH]; DWORD cbBuffer = sizeof (szRemoveSection); hr = pComponent->HrOpenInstanceKey (KEY_READ, &hkeyInstance, NULL, NULL); if(S_OK == hr) { hr = HrRegQuerySzBuffer (hkeyInstance, REGSTR_VAL_INFSECTION, szRemoveSection, &cbBuffer); if (S_OK == hr) { //HrAddOrRemoveWinsockDependancy processes the winsock //remove section in the given inf file and then calls //MigrateWinsockConfiguration which will cause the //necessary PnP notifications to be issued to the //interested application. wcscat (szRemoveSection, c_szRemoveSectionSuffix); hr = HrAddOrRemoveWinsockDependancy (hinf, szRemoveSection); } RegSafeCloseKey (hkeyInstance); } } TraceHr (ttidError, FAL, hr, FALSE, "CModifyContext::HrProcessWinsockRemove (%S)", pComponent->PszGetPnpIdOrInfId()); return hr; } VOID CModifyContext::ApplyChanges () { TraceFileFunc(ttidNetcfgBase); HRESULT hr; CNetConfig* pNetConfig; CComponentList::const_iterator iter; CComponent* pComponent; CFilterDevices FilterDevices (&PNetConfig()->Core); CPszArray ServiceNames; CServiceManager ServiceManager; PCWSTR pszService; BOOL fRebootNeeded; BOOL fMigrateWinsock; BOOL fModifyFilterDevices; BOOL fSignalNetworkProviderLoaded; BOOL fUserIsNetConfigOps; BOOL fCallCoFreeUnusedLibraries; Assert (S_OK == m_hr); Assert (m_fPrepared); Assert (0 == m_ulRecursionDepth); pNetConfig = PNetConfig(); fMigrateWinsock = FALSE; fModifyFilterDevices = FALSE; fSignalNetworkProviderLoaded = FALSE; fUserIsNetConfigOps = FIsUserNetworkConfigOps(); fCallCoFreeUnusedLibraries = FALSE; //+----------------------------------------------------------------------- // Step 0: Prepare m_AddedBindPaths, m_DeletedBindPaths, and // m_DirtyComponents. // // Add the bindpaths that were once disabled, but are now enabled, to // m_AddedBindPaths. We do this so that PnP notifications are sent for // them. // m_hr = m_AddedBindPaths.HrAddBindPathsInSet1ButNotInSet2 ( &m_CoreStartedWith.DisabledBindings, &pNetConfig->Core.DisabledBindings); if (S_OK != m_hr) { return; } // Add the bindpaths that were once enabled, but are now disabled, to // m_DeletedBindPaths. We do this so that PnP notifications are sent for // them. // m_hr = m_DeletedBindPaths.HrAddBindPathsInSet1ButNotInSet2 ( &pNetConfig->Core.DisabledBindings, &m_CoreStartedWith.DisabledBindings); if (S_OK != m_hr) { return; } // m_fDirtyComponents should be empty unless we've explicitly dirtied // one or more. If m_fDirtyComponents were not empty, it would probably // mean we forgot to clear it after the last Apply or Cancel. // Conversely, m_DirtyComponents better not be empty if we've explicitly // dirtied one or more. // Assert (FIff(!m_fComponentExplicitlyDirtied, m_DirtyComponents.FIsEmpty())); // Dirty the affected components (owners and adapters in bindpaths of // length 2) from the added and deleted bindpaths. We need to write // bindings for these components. // m_hr = m_AddedBindPaths.HrGetAffectedComponentsInBindingSet ( &m_DirtyComponents); if (S_OK != m_hr) { return; } m_hr = m_DeletedBindPaths.HrGetAffectedComponentsInBindingSet ( &m_DirtyComponents); if (S_OK != m_hr) { return; } // Dirty components that exist in the current core, but not in the core // we started with. (These are added components). // m_hr = m_DirtyComponents.HrAddComponentsInList1ButNotInList2 ( &pNetConfig->Core.Components, &m_CoreStartedWith.Components); if (S_OK != m_hr) { return; } // Dirty components that exist in the core we started with, but not in // the current core. (These are removed components). // m_hr = m_DirtyComponents.HrAddComponentsInList1ButNotInList2 ( &m_CoreStartedWith.Components, &pNetConfig->Core.Components); if (S_OK != m_hr) { return; } g_pDiagCtx->Printf (ttidBeDiag, "Step 0: The following components are dirty:\n"); for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent); if (!pNetConfig->Core.Components.FComponentInList (pComponent)) { g_pDiagCtx->Printf (ttidBeDiag, " %-12S (removed)\n", pComponent->PszGetPnpIdOrInfId()); } else if (!m_CoreStartedWith.Components.FComponentInList (pComponent)) { g_pDiagCtx->Printf (ttidBeDiag, " %-12S (installed)\n", pComponent->PszGetPnpIdOrInfId()); } else { g_pDiagCtx->Printf (ttidBeDiag, " %S\n", pComponent->PszGetPnpIdOrInfId()); } } // Reserve room for 32 pointers to service names. We use this buffer // to start and stop services. // m_hr = ServiceNames.HrReserveRoomForPointers (32); if (S_OK != m_hr) { return; } // See if we are going to modify any filter devices. If we are, // we'll go through all of the steps of loading filter devices, removing // any we don't need, installing any new ones, and binding them all up. // We only modify filter devices if the user is a normal admin and not // a netcfgop. // // This test could be further refined to see if we had any filters which // were dirty or if we had any dirty adapters which are filtered. // if (!fUserIsNetConfigOps) { fModifyFilterDevices = pNetConfig->Core.FContainsFilterComponent() || m_CoreStartedWith.FContainsFilterComponent(); } else { Assert(!fModifyFilterDevices); } if (fModifyFilterDevices) { // Allow the filter devices structure to reserve whatever memory it // may need. // m_hr = FilterDevices.HrPrepare (); if (S_OK != m_hr) { return; } } pNetConfig->Core.DisabledBindings.Printf (ttidBeDiag, " The following bindings are currently disabled:\n"); //+----------------------------------------------------------------------- // Step 1: Save the network configuration binary. // g_pDiagCtx->Printf (ttidBeDiag, "Step 1: Save the network configuration binary.\n"); HrSaveNetworkConfigurationToRegistry (pNetConfig); //+----------------------------------------------------------------------- // Step 2: Write the static bindings for all changed components. // g_pDiagCtx->Printf (ttidBeDiag, "Step 2: Write the following static bindings.\n"); for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent); // If any protocols are dirty, we'll want to migrate winsock // configuration later. // if (NC_NETTRANS == pComponent->Class()) { fMigrateWinsock = TRUE; } // If the component is in the core, write its bindings. // If it is not in the core, it means it has been removed and // we should therefore remove its bindings. // if (pNetConfig->Core.Components.FComponentInList (pComponent)) { hr = m_RegBindCtx.HrWriteBindingsForComponent (pComponent); // Remember any errors, but continue. // if (S_OK != hr) { Assert (FAILED(hr)); m_hr = hr; } } else { // Only delete if we're not installing another version of this // component that has a duplicate PnpId. If we had already // written the bindings for the newly installed one, and then // deleted the ones for the removed (yet duplicate PnpId), we'd // effectivly delete the bindings for the new one too. See the // comments at step 6 for how we can get into this case. // if (!FIsEnumerated (pComponent->Class()) || !pNetConfig->Core.Components.PFindComponentByPnpId ( pComponent->m_pszPnpId)) { // There's no reason to fail if we can't delete the bindings. // The entire component is about to be tossed anyway. // (VOID) m_RegBindCtx.HrDeleteBindingsForComponent (pComponent); } } } //+----------------------------------------------------------------------- // Step 3: Notify ApplyRegistryChanges // g_pDiagCtx->Printf (ttidBeDiag, "Step 3: Notify: apply registry changes\n"); for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent); pComponent->Notify.ApplyRegistryChanges ( pNetConfig->Notify.PINetCfg(), &fRebootNeeded); if (fRebootNeeded) { m_fRebootRecommended = TRUE; g_pDiagCtx->Printf (ttidBeDiag, " %S notify object wants a reboot\n", pComponent->m_pszInfId); } } // Migrate Winsock configuration if needed. // Important to do this after the LANA map is written, afer Notify Applys // are called, but before any services are started. // if (fMigrateWinsock) { g_pDiagCtx->Printf (ttidBeDiag, "Migrating winsock configuration.\n"); (VOID) HrMigrateWinsockConfiguration (); } //+----------------------------------------------------------------------- // Step 4: Unbind deleted bindpaths. // g_pDiagCtx->Printf (ttidBeDiag, "Step 4: Unbind the following deleted bindings:\n"); if (!m_DeletedBindPaths.FIsEmpty()) { // We don't need to send UNBIND notifications for bindpaths that // involve adapters that have been removed. They will be unbound // automatically when the adapter is uninstalled. (For the case // when the class installer is notifying us of a removed adapter, // its important NOT to try to send an UNBIND notification because // the adapter has already been uninstalled (hence unbound) and // our notification might come back in error causing us to need // a reboot uneccessary. // // So, remove the bindpaths in m_DeletedBindPaths that involve // adapters that have been removed. // for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent); // If its enumerated, and not in the current core, its a // removed adapter. // if (FIsEnumerated (pComponent->Class()) && !pNetConfig->Core.Components.FComponentInList (pComponent)) { m_DeletedBindPaths.RemoveBindPathsWithComponent (pComponent); } } m_DeletedBindPaths.SortForPnpUnbind (); m_RegBindCtx.PnpBindOrUnbindBindPaths (UNBIND, &m_DeletedBindPaths, &fRebootNeeded); if (fRebootNeeded) { m_fRebootRecommended = TRUE; } } //+----------------------------------------------------------------------- // Step 5: Stop services for removed components. // g_pDiagCtx->Printf (ttidBeDiag, "Step 5: Stop the following services:\n"); Assert (0 == ServiceNames.Count()); for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent); // Ignore enumerated components because they will have their drivers // stopped automatically (if appropriate) when they are removed. // Ignore components that are in the current core (not being removed). // if (FIsEnumerated (pComponent->Class()) || pNetConfig->Core.Components.FComponentInList (pComponent)) { continue; } // Winsock remove section needs to be processed for every // component that is being removed in order to update // Transport key for the winsock registry settings HrProcessWinsockRemove (pComponent); // If its a protcol, send an UNLOAD before trying to stop the service. // if ((NC_NETTRANS == pComponent->Class()) || (NCF_NDIS_PROTOCOL & pComponent->m_dwCharacter)) { // Unload can fail as a lot of drivers do not support it. // Treat it as an 'FYI' indication and don't set the reboot // flag if it fails. // (VOID) HrPnpUnloadDriver (NDIS, pComponent->Ext.PszBindName()); } // Ignore components that don't have any services. if (!pComponent->Ext.PszCoServices()) { continue; } for (pszService = pComponent->Ext.PszCoServices(); *pszService; pszService += wcslen(pszService) + 1) { (VOID)ServiceNames.HrAddPointer (pszService); g_pDiagCtx->Printf (ttidBeDiag, " %S", pszService); } g_pDiagCtx->Printf (ttidBeDiag, "\n"); } if (ServiceNames.Count() > 0) { static const CSFLAGS CsStopFlags = { FALSE, // FALSE means don't start SERVICE_CONTROL_STOP, // use this control instead 15000, // wait up to 15 seconds... SERVICE_STOPPED, // ... for service to reach this state FALSE, // }; hr = ServiceManager.HrControlServicesAndWait ( ServiceNames.Count(), ServiceNames.begin(), &CsStopFlags); if (S_OK != hr) { m_fRebootRequired = TRUE; g_pDiagCtx->Printf (ttidBeDiag, " some service failed to stop (hr = 0x%08X)\n", hr); // Unfortunately, there is no easy way to get back which service // did not stop and then to figure out which component contains // that service. Sooo, we'll just put every component that is // being removed in lockdown. This isn't a big deal when the UI // is doing the removal because it only removes things one at a // time. // for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent); if (FIsEnumerated (pComponent->Class()) || !pComponent->Ext.PszCoServices() || pNetConfig->Core.Components.FComponentInList (pComponent)) { continue; } LockdownComponentUntilNextReboot (pComponent->m_pszInfId); } } ServiceNames.Clear(); } //+----------------------------------------------------------------------- // Step 5a: Uninstall filters first. // if (fModifyFilterDevices) { g_pDiagCtx->Printf(ttidBeDiag, "Step 5a: Remove filter devices:\n"); // Order is of utmost importance here. Remove must be called first // because it initializes some state internal to FilterDevices. Start // must come after Write and Write obviously has to come after all // new filter devices are installed. // FilterDevices.LoadAndRemoveFilterDevicesIfNeeded (); } //+----------------------------------------------------------------------- // Step 6: Uninstall removed components. // g_pDiagCtx->Printf (ttidBeDiag, "Step 6: Uninstall the following components:\n"); for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent); // If the component is in the core, ignore it. // If it is not in the core, it means it has been removed and // we should therefore remove its bindings. // if (pNetConfig->Core.Components.FComponentInList (pComponent)) { continue; } // If this is an enumerated component whose PnpId matches that of // a component in the current core, we've run into a special case. // This can happen when the external data (like the NetCfgInstanceId) // is corrupted and the class installer was told to update the // component. The class installer is really told to "install" the // component, but if it already exists as determined by the presence // of NetCfgInstanceId, the class installer translates it to "update". // Without the key, the class installer thinks its installing // a new one. We detect the duplicate PnpId and remove the "prior" // so we can install the "new". This "prior" instance is what we // are finalizing the remove of, but if we call HrCiRemoveComponent, // it just removes the same PnpId that the class installer told us // to install. By not calling HrCiRemoveComponent for this case, // the "prior" instance key gets reused implicitly by the "new" // instance. // if (FIsEnumerated (pComponent->Class()) && pNetConfig->Core.Components.PFindComponentByPnpId ( pComponent->m_pszPnpId)) { g_pDiagCtx->Printf (ttidBeDiag, " Skip removal of %S because a duplicate was installed\n", pComponent->m_pszPnpId); continue; } g_pDiagCtx->Printf (ttidBeDiag, " %S\n", pComponent->PszGetPnpIdOrInfId()); hr = HrCiRemoveComponent (pComponent, &pComponent->m_strRemoveSection); // We can ignore SPAPI_E_NO_SUCH_DEVINST because the class installer // may have already removed it and is just notifying us. // if ((S_OK != hr) && (SPAPI_E_NO_SUCH_DEVINST != hr)) { m_fRebootRequired = TRUE; g_pDiagCtx->Printf (ttidBeDiag, " ^^^ needs a reboot (hr = 0x%08X)\n", hr); } } //+----------------------------------------------------------------------- // Step 6a: Modify filter devices. // if (fModifyFilterDevices) { g_pDiagCtx->Printf (ttidBeDiag, "Step 6a: Modify filter devices:\n"); FilterDevices.InstallFilterDevicesIfNeeded (); (VOID)m_RegBindCtx.HrWriteBindingsForFilterDevices (&FilterDevices); PruneNdisWanBindPathsIfActiveRasConnections ( &FilterDevices.m_BindPathsToRebind, &fRebootNeeded); if (fRebootNeeded) { m_fRebootRecommended = TRUE; } m_RegBindCtx.PnpBindOrUnbindBindPaths (UNBIND, &FilterDevices.m_BindPathsToRebind, &fRebootNeeded); if (fRebootNeeded) { m_fRebootRecommended = TRUE; } g_pDiagCtx->Printf (ttidBeDiag, "Step 6b: Starting filter devices:\n"); FilterDevices.StartFilterDevices (); FilterDevices.Free (); } //+----------------------------------------------------------------------- // Step 7: Start services for added components. // Assert (0 == ServiceNames.Count()); g_pDiagCtx->Printf (ttidBeDiag, "Step 7: Start the following drivers/services:\n"); for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent); // If the component is in the core we started with, ignore it. // If it is not in the core we started with, it means it is newly // installed we should therefore start its services. // if (m_CoreStartedWith.Components.FComponentInList (pComponent)) { continue; } // If we've added a network client, we'll need to signal the // network provider loaded event after we've started its service. // if (NC_NETCLIENT == pComponent->Class()) { fSignalNetworkProviderLoaded = TRUE; } if (FIsEnumerated (pComponent->Class())) { g_pDiagCtx->Printf (ttidBeDiag, " %S\n", pComponent->m_pszPnpId); hr = pComponent->HrStartOrStopEnumeratedComponent (DICS_START); if (S_OK != hr) { m_fRebootRecommended = TRUE; g_pDiagCtx->Printf (ttidBeDiag, " ^^^ needs a reboot (hr = 0x%08X)\n", hr); } if (FIsPhysicalNetAdapter (pComponent->Class(), pComponent->m_dwCharacter) && FInSystemSetup()) { ProcessAdapterAnswerFileIfExists (pComponent); } } else if (pComponent->Ext.PszCoServices()) { for (pszService = pComponent->Ext.PszCoServices(); *pszService; pszService += wcslen(pszService) + 1) { (VOID)ServiceNames.HrAddPointer (pszService); g_pDiagCtx->Printf (ttidBeDiag, " %S", pszService); } g_pDiagCtx->Printf (ttidBeDiag, "\n"); // If we're in system setup, then exclude whatever services // the component has marked as such. // if (FInSystemSetup()) { ExcludeMarkedServicesForSetup (pComponent, &ServiceNames); } } } if ((ServiceNames.Count() > 0) && !(g_pDiagCtx->Flags() & DF_DONT_START_SERVICES)) { static const CSFLAGS CsStartFlags = { TRUE, // TRUE means start 0, 20000, // wait up to 20 seconds... SERVICE_RUNNING, // ... for service to reach this state TRUE, // ignore demand-start and disabled }; hr = ServiceManager.HrControlServicesAndWait ( ServiceNames.Count(), ServiceNames.begin(), &CsStartFlags); if (S_OK != hr) { m_fRebootRecommended = TRUE; g_pDiagCtx->Printf (ttidBeDiag, " some service failed to start (hr = 0x%08X)\n", hr); } } //+----------------------------------------------------------------------- // Step 8: Bind added bindpaths. // g_pDiagCtx->Printf (ttidBeDiag, "Step 8: Bind the following added bindings:\n"); if (fModifyFilterDevices) { hr = m_AddedBindPaths.HrAppendBindingSet ( &FilterDevices.m_BindPathsToRebind); if (S_OK != hr) { // Well, that's not good, but there is nothing we can do about // it now. (Most likely we ran out of memory.) } } if (!m_AddedBindPaths.FIsEmpty()) { CBindPath* pBindPath; // We don't need to send BIND notifications for bindpaths that // involve adapters that have been newly installed. They will be // bound automatically when the adapter is started. // // Update to the above comment: We THOUGHT that was correct, but it // turns out it isn't. TDI isn't PNP (guess they must have missed // THAT memo) and they are not re-reading the new bind strings // from the registry when lower notifications bubble up. So, we // have to send these BINDS for added adapters too. // // We should remove bindpaths that involve components that have // been removed. These can end up in added bindpaths because way // up in step 0, we added bindpaths that were disabled in the // core we started with and that are no longer disabled in the // current core. Well, when the component is removed, its disabled // bindings are removed, so this case would have caused us to add // the bindpath to this binding set. // for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent); // If its been removed, remove any bindpaths that reference it. // if (!pNetConfig->Core.Components.FComponentInList (pComponent)) { m_AddedBindPaths.RemoveBindPathsWithComponent (pComponent); } } // To prevent TDI from sending duplicate BINDs to its clients, we // have to do a little more work. We need to not send the TDI // BINDs to components that are newly installed. This is because // TDI sent them the BINDs when we started the driver above. // So, for each added bindpath, if it's going to the TDI layer, and // the owner (topmost) component of the bindpath is newly installed, // remove it from the added binding so we won't send a notification // for it below. // pBindPath = m_AddedBindPaths.begin(); while (pBindPath != m_AddedBindPaths.end()) { UINT unLayer = GetPnpLayerForBindPath (pBindPath); if ((TDI == unLayer) && !m_CoreStartedWith.Components.FComponentInList ( pBindPath->POwner())) { m_AddedBindPaths.erase (pBindPath); } else { pBindPath++; } } m_AddedBindPaths.SortForPnpBind (); m_RegBindCtx.PnpBindOrUnbindBindPaths (BIND, &m_AddedBindPaths, &fRebootNeeded); if (fRebootNeeded) { // If BINDs fail, we should recommend a reboot, but one is // not required for subsequent installs or removes. // m_fRebootRecommended = TRUE; } } // Signal the network provider loaded event if needed. // Probably best to do this after we've indiciated the PnP bindings // (above) to the new clients. // if (fSignalNetworkProviderLoaded) { SignalNetworkProviderLoaded (); } //+----------------------------------------------------------------------- // Step 9: Allow notify objects to apply PnP changes // g_pDiagCtx->Printf (ttidBeDiag, "Step 9: Notify: apply PnP changes\n"); for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent); pComponent->Notify.ApplyPnpChanges ( pNetConfig->Notify.PINetCfg(), &fRebootNeeded); if (fRebootNeeded) { g_pDiagCtx->Printf (ttidBeDiag, " %S notify object wants a reboot\n", pComponent->m_pszInfId); // If the component has been removed, treat the reboot // as mandatory. (The reason being that we cannot risk a // failed re-install.) We put the component into lockdown // in this situation. // if (!pNetConfig->Core.Components.FComponentInList (pComponent)) { m_fRebootRequired = TRUE; LockdownComponentUntilNextReboot (pComponent->m_pszInfId); } else { m_fRebootRecommended = TRUE; } } } //+----------------------------------------------------------------------- // Step 10: Release notify objects for removed components and // process any DelFiles in the remove section of their INF. // g_pDiagCtx->Printf (ttidBeDiag, "Step 10: Release notify objects for removed components:\n"); for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent); // Skip enumerated components (they don't have notify objects), and // Skip components that were not removed. // Skip components that don't have their INF open (like unsupported // components that get removed during GUI setup.) // if (FIsEnumerated (pComponent->Class()) || pNetConfig->Core.Components.FComponentInList (pComponent) || !pComponent->GetCachedInfFile()) { continue; } pComponent->Notify.ReleaseNotifyObject(NULL, FALSE); fCallCoFreeUnusedLibraries = TRUE; } if (fCallCoFreeUnusedLibraries) { g_pDiagCtx->Printf (ttidBeDiag, " calling CoFreeUnusedLibraries before running remove sections\n"); // Now ask COM to unload any DLLs hosting COM objects that are no longer // in use. (This is a bit heavy-handed as it affects the entire process, // but there is currently no other way to safely unload the DLLs hosting // the notify objects of the removed components.) // CoFreeUnusedLibrariesEx(0, 0); for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent); // Skip enumerated components (they don't have notify objects), and // Skip components that were not removed. // Skip components that don't have their INF open (like unsupported // components that get removed during GUI setup.) // if (FIsEnumerated (pComponent->Class()) || pNetConfig->Core.Components.FComponentInList (pComponent) || !pComponent->GetCachedInfFile()) { continue; } g_pDiagCtx->Printf (ttidBeDiag, " %S [%S]\n", pComponent->PszGetPnpIdOrInfId(), pComponent->m_strRemoveSection.c_str()); (VOID) HrCiInstallFromInfSection( pComponent->GetCachedInfFile(), pComponent->m_strRemoveSection.c_str(), NULL, NULL, SPINST_FILES); } } /* //+----------------------------------------------------------------------- // Step 11: Reconfigure moved bindings // // If we changed binding order, Send PnP RECONFIGURE for all dirty // components that are neither installed nor removed so we // pickup these order changes. // for (iter = m_DirtyComponents.begin(); iter != m_DirtyComponents.end(); iter++) { pComponent = *iter; Assert (pComponent); // Skip components that have been newly installed or removed. // if (!pNetConfig->Core.Components.FComponentInList (pComponent) || !m_CoreStartedWith.Components.FComponentInList (pComponent)) { continue; } // Note: send RECONFIGURE } */ } HRESULT CModifyContext::HrEnableOrDisableBindPath ( IN DWORD dwChangeFlag, IN CBindPath* pBindPath, IN INetCfgBindingPath* pIPath OPTIONAL) { TraceFileFunc(ttidNetcfgBase); HRESULT hr; CNetConfig* pNetConfig; UINT CountBefore; Assert (this); Assert (S_OK == m_hr); Assert (m_fPrepared); Assert ((dwChangeFlag == NCN_ENABLE) || (dwChangeFlag == NCN_DISABLE)); Assert (pBindPath); hr = S_OK; pNetConfig = PNetConfig(); // Get the count of bindpaths currently disabled. If it changes // after we enable/disable this one, we'll inform notify objects // about it. If the count does not change, it means the state // of the bindpath has not changed. // CountBefore = pNetConfig->Core.DisabledBindings.CountBindPaths(); if (NCN_ENABLE == dwChangeFlag) { pNetConfig->Core.EnableBindPath (pBindPath); Assert (S_OK == hr); } else { hr = pNetConfig->Core.HrDisableBindPath (pBindPath); } if ((S_OK == hr) && (pNetConfig->Core.DisabledBindings.CountBindPaths() != CountBefore)) { // Note: Need to protect against bad notify objects that // switch the state of a bindpath we are notifying for. // This could cause an infinite loop. Solve by adding a // recursion count and bindset to the modify context dedicated // to bindpath enabling/disabling. When the count is zero, // we clear the bindingset, add the bindpath we are about to // notify for, increment the recursion count and call // NotifyBindPath. When the call returns, we decrement the // recursion count, remove the bindpath from the binding set, // and return. Before we call NotifyBindPath when the recursion // count is non-zero, if the bindpath is already in the // bindingset, we don't call. // pNetConfig->Notify.NotifyBindPath (dwChangeFlag, pBindPath, pIPath); } TraceHr (ttidError, FAL, hr, FALSE, "CModifyContext::HrEnableOrDisableBindPath"); return hr; } VOID CModifyContext::InstallOrRemoveRequiredComponents ( IN CComponent* pComponent, IN IOR_ACTION Action) { TraceFileFunc(ttidNetcfgBase); HRESULT hr; HKEY hkeyInstance; PWSTR pszRequiredList; const WCHAR szDelims[] = L", "; Assert (this); Assert (S_OK == m_hr); Assert (m_fPrepared); Assert (pComponent); Assert ((IOR_INSTALL == Action) || (IOR_REMOVE == Action)); pszRequiredList = NULL; // Open the instance key of the component and read the RequireAll value. // This value may not exist, which is okay, it means we have nothing // to do. // hr = pComponent->HrOpenInstanceKey (KEY_READ, &hkeyInstance, NULL, NULL); if (S_OK == hr) { HKEY hkeyNdi; hr = HrRegOpenKeyEx (hkeyInstance, L"Ndi", KEY_READ, &hkeyNdi); if (S_OK == hr) { hr = HrRegQuerySzWithAlloc (hkeyNdi, L"RequiredAll", &pszRequiredList); RegCloseKey (hkeyNdi); } if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) { hr = S_OK; Assert (!pszRequiredList); } RegCloseKey (hkeyInstance); } // If we have a list of required components, install or remove them. // if ((S_OK == hr) && pszRequiredList) { CNetConfig* pNetConfig; INetCfgComponent* pIComp; pNetConfig = PNetConfig(); hr = pComponent->HrGetINetCfgComponentInterface ( pNetConfig->Notify.PINetCfg(), &pIComp); if (S_OK == hr) { PCWSTR pszInfId; PWSTR pszNextInfId; OBO_TOKEN OboToken; CComponent* pComponentToRemove; WCHAR szInfFile [_MAX_PATH]; ZeroMemory (&OboToken, sizeof(OboToken)); OboToken.Type = OBO_COMPONENT; OboToken.pncc = pIComp; // For each INF ID in the comma separate list of required // components... // for (pszInfId = GetNextStringToken (pszRequiredList, szDelims, &pszNextInfId); pszInfId && *pszInfId; pszInfId = GetNextStringToken (NULL, szDelims, &pszNextInfId)) { if (IOR_INSTALL == Action) { NETWORK_INSTALL_PARAMS nip; COMPONENT_INSTALL_PARAMS Params; ZeroMemory (&Params, sizeof(Params)); // Get the Class corresponding to the INF ID. // m_hr = HrCiGetClassAndInfFileOfInfId ( pszInfId, &Params.Class, szInfFile); if (S_OK != m_hr) { break; } //$REVIEW:Should we stick the filename in the // COMPONENT_INSTALL_PARAMS so that we don't grovel // the INF directory to find it again? // If so, store the filename buffer in the modify // context so we don't take up stack space or heap space. // Just need to be sure that we use it to install the // component before we recurse and overwrite it. // Pack the network install parameters and call the common // function. // //$REVIEW: we probably need dwSetupFlags and dwUpgradeFromBuildNo // in the modify context saved when it was called at // recursion depth 0. Otherwise, things installed here // during GUI setup will have wrong parameters. // nip.dwSetupFlags = 0; nip.dwUpgradeFromBuildNo = 0; nip.pszAnswerFile = NULL; nip.pszAnswerSection = NULL; // Setup the component install parameters. // Params.pszInfId = pszInfId; Params.pszInfFile = szInfFile; Params.pOboToken = FIsEnumerated (Params.Class) ? NULL : &OboToken; Params.pnip = &nip; // // THIS MAY CAUSE RECURSION // // (just using pComponentToRemove as a placeholder // for a required parameter.) // HrInstallNewOrReferenceExistingComponent ( Params, &pComponentToRemove); if (S_OK != m_hr) { break; } } else { Assert (IOR_REMOVE == Action); // Search for the component to remove using its INF ID. // pComponentToRemove = pNetConfig->Core.Components. PFindComponentByInfId (pszInfId, NULL); if (pComponentToRemove) { // // THIS MAY CAUSE RECURSION // HrRemoveComponentIfNotReferenced ( pComponentToRemove, FIsEnumerated (pComponentToRemove->Class()) ? NULL : &OboToken, NULL); if (S_OK != m_hr) { break; } } } } ReleaseObj (pIComp); } MemFree (pszRequiredList); } } //---------------------------------------------------------------------------- // Update a component. Do this by generating the bindings which involve // the component and noting them as 'OldBindPaths'. The stack entries which // involve the component are removed and re-generated and the bindings which // involve the component are again noted as 'NewBindPaths'. The old bindings // are compared to the new bindings and the differences are notified to // notify objects as either being removed or added. For any removed bindings, // we also remove them from the core's disabled set if they happen to exist // there too. // // Assumptions: // The INF for pComponent has already been re-run so that the potentially // new values for UpperRange, LowerRange, etc. are present in the registry. // // pComponent has had its external data loaded already. // HRESULT CModifyContext::HrUpdateComponent ( IN CComponent* pComponent, IN DWORD dwSetupFlags, IN DWORD dwUpgradeFromBuildNo) { TraceFileFunc(ttidNetcfgBase); HRESULT hr; CNetConfig* pNetConfig; CBindingSet OldBindPaths; CBindingSet NewBindPaths; CBindPath* pScan; Assert (this); Assert (S_OK == m_hr); Assert (m_fPrepared); Assert (pComponent); pNetConfig = PNetConfig(); // Now that we actually are going to modify something, push a new // recursion depth. // PushRecursionDepth(); Assert (S_OK == m_hr); // Generate the "old" bindings by noting those which involve the // component. // hr = pNetConfig->Core.HrGetBindingsInvolvingComponent ( pComponent, GBF_ONLY_WHICH_CONTAIN_COMPONENT, &OldBindPaths); if (S_OK != hr) { goto finished; } // Reload the external data so we pickup what the possibly updated // INF has changed. // hr = pComponent->Ext.HrReloadExternalData (); if (S_OK != hr) { goto finished; } // Update the stack table entries for the component. // hr = pNetConfig->Core.StackTable.HrUpdateEntriesForComponent ( pComponent, &pNetConfig->Core.Components, INS_SORTED); if (S_OK != hr) { // This is not good. We ripped out the stack entries and failed // putting them back in. The stack table now has no entries for // this component. Prevent this from being applied by setting the // modify context's HRESULT to the error. // m_hr = hr; goto finished; } // Generate the "new" bindings by noting those which involve the // component. // hr = pNetConfig->Core.HrGetBindingsInvolvingComponent ( pComponent, GBF_ONLY_WHICH_CONTAIN_COMPONENT, &NewBindPaths); if (S_OK != hr) { // Probably out of memory. Prevent apply by setting the modify // context's HRESULT to the error. // m_hr = hr; goto finished; } // Notify any bindpaths which have been removed. // for (pScan = OldBindPaths.begin(); pScan != OldBindPaths.end(); pScan++) { if (NewBindPaths.FContainsBindPath (pScan)) { continue; } m_DeletedBindPaths.HrAddBindPath (pScan, INS_IGNORE_IF_DUP | INS_APPEND); pNetConfig->Core.DisabledBindings.RemoveBindPath (pScan); pNetConfig->Notify.NotifyBindPath (NCN_REMOVE, pScan, NULL); } // Notify any bindpaths which have been added. // for (pScan = NewBindPaths.begin(); pScan != NewBindPaths.end(); pScan++) { if (OldBindPaths.FContainsBindPath (pScan)) { continue; } m_AddedBindPaths.HrAddBindPath (pScan, INS_IGNORE_IF_DUP | INS_APPEND); pNetConfig->Notify.NotifyBindPath (NCN_ADD | NCN_ENABLE, pScan, NULL); } // Notify that the component has been updated. // pNetConfig->Notify.ComponentUpdated (pComponent, dwSetupFlags, dwUpgradeFromBuildNo); finished: hr = HrPopRecursionDepth(); TraceHr (ttidError, FAL, hr, FALSE, "CModifyContext::HrUpdateComponent (%S)", pComponent->PszGetPnpIdOrInfId()); return hr; }