395 lines
11 KiB
C++
395 lines
11 KiB
C++
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Microsoft Windows
|
||
|
// Copyright (C) Microsoft Corporation, 1999.
|
||
|
//
|
||
|
// File: I N S T A L L . C P P
|
||
|
//
|
||
|
// Contents: Implements actions related to installing components.
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
// Author: shaunco 15 Jan 1999
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
#include <pch.h>
|
||
|
#pragma hdrstop
|
||
|
#include "classinst.h"
|
||
|
#include "lockdown.h"
|
||
|
#include "netcfg.h"
|
||
|
#include "obotoken.h"
|
||
|
#include "util.h"
|
||
|
|
||
|
|
||
|
#if DBG
|
||
|
|
||
|
VOID
|
||
|
DbgVerifyComponentInstallParams (
|
||
|
IN const COMPONENT_INSTALL_PARAMS& Params)
|
||
|
{
|
||
|
if (!Params.pComponent)
|
||
|
{
|
||
|
Assert (FIsValidNetClass (Params.Class));
|
||
|
Assert (Params.pszInfId && *Params.pszInfId);
|
||
|
Assert (FOboTokenValidForClass (Params.pOboToken, Params.Class));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
#define DbgVerifyComponentInstallParams NOP_FUNCTION
|
||
|
|
||
|
#endif
|
||
|
|
||
|
|
||
|
VOID
|
||
|
CModifyContext::InstallAndAddAndNotifyComponent(
|
||
|
IN const COMPONENT_INSTALL_PARAMS& Params,
|
||
|
OUT CComponent** ppComponent)
|
||
|
{
|
||
|
CNetConfig* pNetConfig;
|
||
|
CComponent* pComponent;
|
||
|
UINT cPreviousAddedBindPaths;
|
||
|
|
||
|
Assert (this);
|
||
|
Assert (S_OK == m_hr);
|
||
|
DbgVerifyComponentInstallParams (Params);
|
||
|
Assert (ppComponent);
|
||
|
|
||
|
pNetConfig = PNetConfig();
|
||
|
|
||
|
// Call the class installer to do the grunt work of finding the
|
||
|
// INF, processing it, creating the instance key, etc. If this
|
||
|
// all succeeds, we are returned an allocated CComponent.
|
||
|
//
|
||
|
//$REVIEW: Think about having HrCiInstallComponent return the
|
||
|
// list of required components. This will keep us from having to
|
||
|
// reopen the instance key and ndi key.
|
||
|
//
|
||
|
if (!Params.pComponent)
|
||
|
{
|
||
|
|
||
|
// error test only
|
||
|
//if (0 == _wcsicmp(L"ms_nwipx", Params.pszInfId))
|
||
|
//{
|
||
|
// TraceTag (ttidBeDiag, "Simulating failure for: %S", Params.pszInfId);
|
||
|
// m_hr = E_FAIL;
|
||
|
// return;
|
||
|
//}
|
||
|
|
||
|
m_hr = HrCiInstallComponent (Params, &pComponent, NULL);
|
||
|
if (S_OK != m_hr)
|
||
|
{
|
||
|
Assert(FAILED(m_hr));
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pComponent = Params.pComponent;
|
||
|
}
|
||
|
Assert (pComponent);
|
||
|
|
||
|
// Install all of the components required by this component.
|
||
|
//
|
||
|
// THIS MAY CAUSE RECURSION
|
||
|
//
|
||
|
InstallOrRemoveRequiredComponents (pComponent, IOR_INSTALL);
|
||
|
if (S_OK != m_hr)
|
||
|
{
|
||
|
Assert(FAILED(m_hr));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// If this is an enumerated component, and it has the same PnpId as
|
||
|
// a component already in our core, we will remove the existing component.
|
||
|
// This can happen if an adapter is removed and reinstalled and the
|
||
|
// binding engine could not be notified of both because the write
|
||
|
// lock was held at the time of remove.
|
||
|
//
|
||
|
if (FIsEnumerated(pComponent->Class()))
|
||
|
{
|
||
|
CComponent* pDup;
|
||
|
|
||
|
while (NULL != (pDup = pNetConfig->Core.Components.PFindComponentByPnpId (
|
||
|
pComponent->m_pszPnpId)))
|
||
|
{
|
||
|
TraceTag (ttidBeDiag, "Removing duplicate PnpId: %S",
|
||
|
pComponent->m_pszPnpId);
|
||
|
|
||
|
pDup->Refs.RemoveAllReferences();
|
||
|
(VOID) HrRemoveComponentIfNotReferenced (pDup, NULL, NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// We only insert the component and its stack table entries into our
|
||
|
// list after all of its required components have been installed.
|
||
|
// This is just the concept of "don't consider a component installed
|
||
|
// until all of its requirements are also installed".
|
||
|
// i.e. Atomicity of component installation.
|
||
|
//
|
||
|
m_hr = pNetConfig->Core.HrAddComponentToCore (pComponent, INS_SORTED);
|
||
|
if (S_OK != m_hr)
|
||
|
{
|
||
|
Assert(FAILED(m_hr));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Notify the component's notify object it is being installed.
|
||
|
// This also sends global notifications to other notify objects
|
||
|
// who may be interested.
|
||
|
//
|
||
|
// THIS MAY CAUSE RECURSION
|
||
|
//
|
||
|
m_hr = pNetConfig->Notify.ComponentAdded (pComponent, Params.pnip);
|
||
|
if (S_OK != m_hr)
|
||
|
{
|
||
|
Assert(FAILED(m_hr));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Note the number of bindpaths currently in m_AddedBindPaths.
|
||
|
// We need this so that when we add to the set, we only notify
|
||
|
// for the ones we add.
|
||
|
//
|
||
|
cPreviousAddedBindPaths = m_AddedBindPaths.CountBindPaths ();
|
||
|
|
||
|
// Get the bindpaths that involve the component we are adding.
|
||
|
// Add these to the added bindpaths we are keeping track of.
|
||
|
//
|
||
|
m_hr = pNetConfig->Core.HrGetBindingsInvolvingComponent (
|
||
|
pComponent,
|
||
|
GBF_ADD_TO_BINDSET | GBF_ONLY_WHICH_CONTAIN_COMPONENT,
|
||
|
&m_AddedBindPaths);
|
||
|
if (S_OK != m_hr)
|
||
|
{
|
||
|
Assert(FAILED(m_hr));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Query and notify for these added bindpaths.
|
||
|
//
|
||
|
// THIS MAY CAUSE RECURSION
|
||
|
//
|
||
|
if (m_AddedBindPaths.CountBindPaths() > cPreviousAddedBindPaths)
|
||
|
{
|
||
|
m_hr = pNetConfig->Notify.QueryAndNotifyForAddedBindPaths (
|
||
|
&m_AddedBindPaths,
|
||
|
cPreviousAddedBindPaths);
|
||
|
if (S_OK != m_hr)
|
||
|
{
|
||
|
Assert(FAILED(m_hr));
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Install any components as a convenience to the user
|
||
|
// depending on the component we just installed component.
|
||
|
//
|
||
|
// THIS MAY CAUSE RECURSION
|
||
|
//
|
||
|
InstallConvenienceComponentsForUser (pComponent);
|
||
|
if (S_OK != m_hr)
|
||
|
{
|
||
|
Assert(FAILED(m_hr));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Assign the output pointer.
|
||
|
//
|
||
|
Assert (S_OK == m_hr);
|
||
|
Assert (pComponent);
|
||
|
*ppComponent = pComponent;
|
||
|
}
|
||
|
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// Install components on behalf of the user
|
||
|
//
|
||
|
// Assumptions:
|
||
|
//
|
||
|
VOID
|
||
|
CModifyContext::InstallConvenienceComponentsForUser (
|
||
|
IN const CComponent* pComponent)
|
||
|
{
|
||
|
COMPONENT_INSTALL_PARAMS Params;
|
||
|
OBO_TOKEN UserOboToken;
|
||
|
CComponent* pNewComponent;
|
||
|
|
||
|
Assert (this);
|
||
|
Assert (S_OK == m_hr);
|
||
|
Assert (pComponent);
|
||
|
|
||
|
ZeroMemory (&UserOboToken, sizeof(UserOboToken));
|
||
|
UserOboToken.Type = OBO_USER;
|
||
|
|
||
|
// If the component is an ATM adapter, make sure ATMUNI and ATMLANE are
|
||
|
// installed.
|
||
|
//
|
||
|
if (FSubstringMatch (L"ndisatm", pComponent->Ext.PszUpperRange(),
|
||
|
NULL, NULL))
|
||
|
{
|
||
|
ZeroMemory (&Params, sizeof(Params));
|
||
|
Params.pOboToken = &UserOboToken;
|
||
|
Params.Class = NC_NETTRANS;
|
||
|
|
||
|
Params.pszInfId = L"ms_atmuni";
|
||
|
HrInstallNewOrReferenceExistingComponent (Params, &pNewComponent);
|
||
|
|
||
|
Params.pszInfId = L"ms_atmlane";
|
||
|
HrInstallNewOrReferenceExistingComponent (Params, &pNewComponent);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
CModifyContext::HrInstallNewOrReferenceExistingComponent (
|
||
|
IN const COMPONENT_INSTALL_PARAMS& Params,
|
||
|
OUT CComponent** ppComponent)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
BOOL fInstallNew;
|
||
|
CNetConfig* pNetConfig;
|
||
|
CComponent* pComponent;
|
||
|
|
||
|
Assert (this);
|
||
|
Assert (S_OK == m_hr);
|
||
|
DbgVerifyComponentInstallParams (Params);
|
||
|
Assert (ppComponent);
|
||
|
|
||
|
// Initialize the output parameter.
|
||
|
//
|
||
|
*ppComponent = NULL;
|
||
|
|
||
|
// Assume, for now, that we will be installing a new component.
|
||
|
//
|
||
|
hr = S_OK;
|
||
|
fInstallNew = TRUE;
|
||
|
pNetConfig = PNetConfig();
|
||
|
pComponent = NULL;
|
||
|
|
||
|
// If the user wishes to add a reference if the component is already
|
||
|
// installed...
|
||
|
//
|
||
|
if (Params.pOboToken)
|
||
|
{
|
||
|
// ...then look to see if the component is installed...
|
||
|
//
|
||
|
pComponent = pNetConfig->Core.Components.
|
||
|
PFindComponentByInfId (Params.pszInfId, NULL);
|
||
|
|
||
|
// ...if it is, we won't be installing a new one.
|
||
|
//
|
||
|
if (pComponent)
|
||
|
{
|
||
|
fInstallNew = FALSE;
|
||
|
|
||
|
if (pComponent->m_dwCharacter & NCF_SINGLE_INSTANCE)
|
||
|
{
|
||
|
*ppComponent = NULL;
|
||
|
return HRESULT_FROM_SETUPAPI(ERROR_DEVINST_ALREADY_EXISTS);
|
||
|
}
|
||
|
|
||
|
// If the existing component is already referenced by
|
||
|
// the specified obo token, we can return.
|
||
|
//
|
||
|
if (pComponent->Refs.FIsReferencedByOboToken (Params.pOboToken))
|
||
|
{
|
||
|
*ppComponent = pComponent;
|
||
|
return S_OK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ...otherwise, (it is not in the current core) but if it IS
|
||
|
// in the core we started with, it means someone had previously
|
||
|
// removed it during this modify context and now wants to add it
|
||
|
// back. This is tricky and should probably be implemented later.
|
||
|
// For now, return an error and throw up an assert so we can see
|
||
|
// who needs to do this.
|
||
|
//
|
||
|
else if (m_CoreStartedWith.Components.
|
||
|
PFindComponentByInfId (Params.pszInfId, NULL))
|
||
|
{
|
||
|
AssertSz (FALSE, "Whoa. Someone is trying to install a "
|
||
|
"component that was previously removed during this same "
|
||
|
"modify context. We need to decide if we can support this.");
|
||
|
return E_UNEXPECTED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If we've decided to install a new component, (which can happen
|
||
|
// if an obo token was not specified OR if an obo token was specified
|
||
|
// but the component was not already present) then do the work.
|
||
|
//
|
||
|
if (fInstallNew)
|
||
|
{
|
||
|
// If the component to be installed is locked down, bail out.
|
||
|
// Note we don't put the modify context into error if this situation
|
||
|
// occurs.
|
||
|
// Note too that we only do this if Params.pComponent is not present
|
||
|
// which would indicate the class installer calling us to install an
|
||
|
// enumerated component.
|
||
|
//
|
||
|
if (!Params.pComponent)
|
||
|
{
|
||
|
if (FIsComponentLockedDown (Params.pszInfId))
|
||
|
{
|
||
|
TraceTag (ttidBeDiag, "%S is locked down and cannot be installed "
|
||
|
"until the next reboot.",
|
||
|
Params.pszInfId);
|
||
|
|
||
|
return NETCFG_E_NEED_REBOOT;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Make sure the modify context is setup and keep track of our
|
||
|
// recursion depth.
|
||
|
//
|
||
|
PushRecursionDepth ();
|
||
|
Assert (S_OK == m_hr);
|
||
|
|
||
|
InstallAndAddAndNotifyComponent (Params, ppComponent);
|
||
|
|
||
|
hr = HrPopRecursionDepth ();
|
||
|
|
||
|
// If the component to be installed was not found, the return code
|
||
|
// will be SPAPI_E_NO_DRIVER_SELECTED. We want to return this to the
|
||
|
// caller, but we don't need the context to remain with this error.
|
||
|
// This will allow subsequent calls to install other componetns to
|
||
|
// proceed.
|
||
|
//
|
||
|
if (SPAPI_E_NO_DRIVER_SELECTED == m_hr)
|
||
|
{
|
||
|
m_hr = S_OK;
|
||
|
Assert (SPAPI_E_NO_DRIVER_SELECTED == hr);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Referencing pComponent on behalf of the obo token.
|
||
|
//
|
||
|
Assert (pComponent);
|
||
|
Assert (Params.pOboToken);
|
||
|
|
||
|
// Make sure the modify context is setup and keep track of our
|
||
|
// recursion depth.
|
||
|
//
|
||
|
PushRecursionDepth ();
|
||
|
Assert (S_OK == m_hr);
|
||
|
|
||
|
m_hr = pComponent->Refs.HrAddReferenceByOboToken (Params.pOboToken);
|
||
|
|
||
|
if (S_OK == m_hr)
|
||
|
{
|
||
|
*ppComponent = pComponent;
|
||
|
}
|
||
|
|
||
|
hr = HrPopRecursionDepth ();
|
||
|
}
|
||
|
|
||
|
// If we are returning success, we'd better have our ouput parameter set.
|
||
|
//
|
||
|
Assert (FImplies(SUCCEEDED(hr), *ppComponent));
|
||
|
return hr;
|
||
|
}
|