1259 lines
30 KiB
C++
1259 lines
30 KiB
C++
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Microsoft Windows
|
||
|
// Copyright (C) Microsoft Corporation, 1999.
|
||
|
//
|
||
|
// File: I N E T C F G . C P P
|
||
|
//
|
||
|
// Contents: Implements the COM interfaces on the top-level NetCfg object.
|
||
|
// These interfaces are: INetCfg and INetCfgLock. Also
|
||
|
// implements a base C++ class inherited by sub-level NetCfg
|
||
|
// objects which hold a reference to the top-level object.
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
// Author: shaunco 15 Jan 1999
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
#include <pch.h>
|
||
|
#pragma hdrstop
|
||
|
#include "classinst.h"
|
||
|
#include "iclass.h"
|
||
|
#include "icomp.h"
|
||
|
#include "ienum.h"
|
||
|
#include "inetcfg.h"
|
||
|
#include "ncperms.h"
|
||
|
#include "ncras.h"
|
||
|
#include "ncreg.h"
|
||
|
#include "ncui.h"
|
||
|
#include "ncvalid.h"
|
||
|
#include "ndispnp.h"
|
||
|
#include "netcfg.h"
|
||
|
#include "obotoken.h"
|
||
|
#include "resource.h"
|
||
|
|
||
|
// static
|
||
|
HRESULT
|
||
|
CImplINetCfg::HrCreateInstance (
|
||
|
CNetConfig* pNetConfig,
|
||
|
CImplINetCfg** ppINetCfg)
|
||
|
{
|
||
|
Assert (pNetConfig);
|
||
|
Assert (ppINetCfg);
|
||
|
|
||
|
HRESULT hr = E_OUTOFMEMORY;
|
||
|
|
||
|
CImplINetCfg* pObj;
|
||
|
pObj = new CComObject <CImplINetCfg>;
|
||
|
if (pObj)
|
||
|
{
|
||
|
// Initialize our members.
|
||
|
//
|
||
|
pObj->m_pNetConfig = pNetConfig;
|
||
|
Assert (!pObj->m_fOwnNetConfig);
|
||
|
|
||
|
// Do the standard CComCreator::CreateInstance stuff.
|
||
|
//
|
||
|
pObj->SetVoid (NULL);
|
||
|
pObj->InternalFinalConstructAddRef ();
|
||
|
hr = pObj->FinalConstruct ();
|
||
|
pObj->InternalFinalConstructRelease ();
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
AddRefObj (pObj->GetUnknown());
|
||
|
*ppINetCfg = pObj;
|
||
|
}
|
||
|
|
||
|
if (S_OK != hr)
|
||
|
{
|
||
|
delete pObj;
|
||
|
}
|
||
|
}
|
||
|
TraceHr (ttidError, FAL, hr, FALSE,
|
||
|
"CImplINetCfg::HrCreateInstance");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
CImplINetCfg::HrCoCreateWrapper (
|
||
|
IN REFCLSID rclsid,
|
||
|
IN LPUNKNOWN punkOuter,
|
||
|
IN DWORD dwClsContext,
|
||
|
IN REFIID riid,
|
||
|
OUT LPVOID FAR* ppv)
|
||
|
{
|
||
|
/*
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
if (!m_fComInitialized)
|
||
|
{
|
||
|
m_fComInitialized = TRUE;
|
||
|
|
||
|
hr = CoInitializeEx (
|
||
|
NULL,
|
||
|
COINIT_DISABLE_OLE1DDE | COINIT_APARTMENTTHREADED);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
m_fUninitCom = TRUE;
|
||
|
hr = S_OK; // mask S_FALSE
|
||
|
}
|
||
|
else if (RPC_E_CHANGED_MODE == hr)
|
||
|
{
|
||
|
hr = S_OK;
|
||
|
Assert (!m_fUninitCom);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
hr = CoCreateInstance (rclsid, punkOuter, dwClsContext, riid, ppv);
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
HRESULT hr;
|
||
|
hr = CoCreateInstance (rclsid, punkOuter, dwClsContext, riid, ppv);
|
||
|
|
||
|
TraceHr (ttidError, FAL, hr, FALSE,
|
||
|
"CImplINetCfg::HrCoCreateWrapper");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
CImplINetCfg::HrCheckForReentrancy (
|
||
|
IN DWORD dwFlags)
|
||
|
{
|
||
|
Assert (FImplies(dwFlags & IF_ALLOW_INSTALL_OR_REMOVE,
|
||
|
dwFlags & IF_REFUSE_REENTRANCY));
|
||
|
|
||
|
if (dwFlags & IF_ALLOW_INSTALL_OR_REMOVE)
|
||
|
{
|
||
|
if (m_LastAllowedSetupRpl != m_CurrentRpl)
|
||
|
{
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
}
|
||
|
else if (0 != m_CurrentRpl)
|
||
|
{
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
CImplINetCfg::HrIsValidInterface (
|
||
|
IN DWORD dwFlags)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
// Check if we need to refuse re-entrancy.
|
||
|
//
|
||
|
if (dwFlags & IF_REFUSE_REENTRANCY)
|
||
|
{
|
||
|
hr = HrCheckForReentrancy (dwFlags);
|
||
|
if (S_OK != hr)
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check if initialized/uninitalized as required.
|
||
|
//
|
||
|
if ((dwFlags & IF_NEED_UNINITIALIZED) && m_pNetConfig)
|
||
|
{
|
||
|
return NETCFG_E_ALREADY_INITIALIZED;
|
||
|
}
|
||
|
else if (!(dwFlags & IF_NEED_UNINITIALIZED) && !m_pNetConfig)
|
||
|
{
|
||
|
return NETCFG_E_NOT_INITIALIZED;
|
||
|
}
|
||
|
|
||
|
// Check for the write lock.
|
||
|
//
|
||
|
if (dwFlags & IF_NEED_WRITE_LOCK)
|
||
|
{
|
||
|
if (!m_WriteLock.FIsOwnedByMe ())
|
||
|
{
|
||
|
return NETCFG_E_NO_WRITE_LOCK;
|
||
|
}
|
||
|
|
||
|
// Needing the write lock means we need the modify context to
|
||
|
// be prepared (unless the caller specified
|
||
|
// IF_DONT_PREPARE_MODIFY_CONTEXT).
|
||
|
//
|
||
|
if (!m_pNetConfig->ModifyCtx.m_fPrepared &&
|
||
|
!(dwFlags & IF_DONT_PREPARE_MODIFY_CONTEXT))
|
||
|
{
|
||
|
hr = m_pNetConfig->ModifyCtx.HrPrepare ();
|
||
|
if (S_OK != hr)
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!(dwFlags & IF_UNINITIALIZING))
|
||
|
{
|
||
|
// Check for an error that occured during the current modification
|
||
|
// that has not been rolled back yet. i.e. keep people out until
|
||
|
// we unwind enough to cleanup our modify context.
|
||
|
//
|
||
|
if (m_pNetConfig && (S_OK != m_pNetConfig->ModifyCtx.m_hr))
|
||
|
{
|
||
|
return m_pNetConfig->ModifyCtx.m_hr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Assert (FImplies(!m_pNetConfig, (dwFlags & IF_NEED_UNINITIALIZED)));
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
CImplINetCfg::LowerRpl (
|
||
|
IN RPL_FLAGS Flags)
|
||
|
{
|
||
|
if (RPL_ALLOW_INSTALL_REMOVE == Flags)
|
||
|
{
|
||
|
Assert (m_LastAllowedSetupRpl > 0);
|
||
|
m_LastAllowedSetupRpl--;
|
||
|
}
|
||
|
|
||
|
Assert (m_CurrentRpl > 0);
|
||
|
m_CurrentRpl--;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
CImplINetCfg::RaiseRpl (
|
||
|
IN RPL_FLAGS Flags)
|
||
|
{
|
||
|
m_CurrentRpl++;
|
||
|
|
||
|
if (RPL_ALLOW_INSTALL_REMOVE == Flags)
|
||
|
{
|
||
|
m_LastAllowedSetupRpl++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
CImplINetCfg::HrLockAndTestForValidInterface (
|
||
|
DWORD dwFlags)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
Lock();
|
||
|
|
||
|
hr = HrIsValidInterface (dwFlags);
|
||
|
|
||
|
if (S_OK != hr)
|
||
|
{
|
||
|
Unlock();
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// INetCfg -
|
||
|
//
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::Initialize (
|
||
|
IN PVOID pvReserved)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
ULONG* pReserved = (ULONG*)pvReserved;
|
||
|
|
||
|
// Validate parameters.
|
||
|
//
|
||
|
if (FBadInPtrOptional(pReserved))
|
||
|
{
|
||
|
hr = E_POINTER;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = HrLockAndTestForValidInterface (
|
||
|
IF_NEED_UNINITIALIZED | IF_REFUSE_REENTRANCY);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
Assert (!m_pNetConfig);
|
||
|
|
||
|
hr = CNetConfig::HrCreateInstance (
|
||
|
this,
|
||
|
&m_pNetConfig);
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
Assert (m_pNetConfig);
|
||
|
|
||
|
m_fOwnNetConfig = TRUE;
|
||
|
}
|
||
|
Unlock();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TraceHr (ttidError, FAL, hr, FALSE, "CImplINetCfg::Initialize");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::Uninitialize ()
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
hr = HrLockAndTestForValidInterface (
|
||
|
IF_REFUSE_REENTRANCY | IF_UNINITIALIZING);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
Assert (m_pNetConfig);
|
||
|
Assert (m_fOwnNetConfig);
|
||
|
|
||
|
delete m_pNetConfig;
|
||
|
|
||
|
// CGlobalNotifyInterface::ReleaseINetCfg (called via the above
|
||
|
// delete call) will set m_pNetConfig to NULL for us.
|
||
|
// Verify it is so.
|
||
|
//
|
||
|
Assert (!m_pNetConfig);
|
||
|
|
||
|
// Release our cache of INetCfgClass pointers.
|
||
|
//
|
||
|
ReleaseIUnknownArray (celems(m_apINetCfgClass), (IUnknown**)m_apINetCfgClass);
|
||
|
ZeroMemory (m_apINetCfgClass, sizeof(m_apINetCfgClass));
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
|
||
|
TraceHr (ttidError, FAL, hr, NETCFG_E_NOT_INITIALIZED == hr,
|
||
|
"CImplINetCfg::Uninitialize");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::Validate ()
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
hr = HrLockAndTestForValidInterface (IF_REFUSE_REENTRANCY);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
|
||
|
TraceHr (ttidError, FAL, hr, FALSE, "CImplINetCfg::Validate");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::Cancel ()
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
hr = HrLockAndTestForValidInterface (IF_REFUSE_REENTRANCY);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
// Only cancel the changes if we have a prepared modify context.
|
||
|
//
|
||
|
if (m_pNetConfig->ModifyCtx.m_fPrepared)
|
||
|
{
|
||
|
hr = m_pNetConfig->ModifyCtx.HrApplyIfOkOrCancel (FALSE);
|
||
|
}
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
|
||
|
TraceHr (ttidError, FAL, hr, FALSE, "CImplINetCfg::Cancel");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::Apply ()
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
// We need the write lock to Apply, but we don't want to prepare the
|
||
|
// modify context if it has not been prepared. (This case amounts to
|
||
|
// applying no changes.) Hence we use the IF_DONT_PREPARE_MODIFY_CONTEXT
|
||
|
// flag.
|
||
|
//
|
||
|
hr = HrLockAndTestForValidInterface (
|
||
|
IF_NEED_WRITE_LOCK | IF_REFUSE_REENTRANCY |
|
||
|
IF_DONT_PREPARE_MODIFY_CONTEXT);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
// Only apply the changes if we have a prepared modify context.
|
||
|
//
|
||
|
if (m_pNetConfig->ModifyCtx.m_fPrepared)
|
||
|
{
|
||
|
hr = m_pNetConfig->ModifyCtx.HrApplyIfOkOrCancel (TRUE);
|
||
|
}
|
||
|
|
||
|
// If there is nothing to apply, but we've previously applied
|
||
|
// something that indicated a reboot was recommened or required,
|
||
|
// return an indication.
|
||
|
//
|
||
|
else if (m_pNetConfig->ModifyCtx.m_fRebootRecommended ||
|
||
|
m_pNetConfig->ModifyCtx.m_fRebootRequired)
|
||
|
{
|
||
|
hr = NETCFG_S_REBOOT;
|
||
|
}
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
|
||
|
TraceHr (ttidError, FAL, hr, NETCFG_S_REBOOT == hr,
|
||
|
"CImplINetCfg::Apply");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::EnumComponents (
|
||
|
IN const GUID* pguidClass OPTIONAL,
|
||
|
OUT IEnumNetCfgComponent** ppIEnum)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
NETCLASS Class;
|
||
|
|
||
|
// Validate parameters.
|
||
|
//
|
||
|
if (FBadInPtrOptional(pguidClass) || FBadOutPtr(ppIEnum))
|
||
|
{
|
||
|
hr = E_POINTER;
|
||
|
}
|
||
|
else if (pguidClass &&
|
||
|
(NC_INVALID == (Class = NetClassEnumFromGuid(*pguidClass))))
|
||
|
{
|
||
|
hr = E_INVALIDARG;
|
||
|
*ppIEnum = NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*ppIEnum = NULL;
|
||
|
|
||
|
hr = HrLockAndTestForValidInterface (IF_DEFAULT);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
hr = CImplIEnumNetCfgComponent::HrCreateInstance (
|
||
|
this,
|
||
|
(pguidClass) ? Class : NC_INVALID,
|
||
|
ppIEnum);
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TraceHr (ttidError, FAL, hr, FALSE, "CImplINetCfg::EnumComponents");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::FindComponent (
|
||
|
IN PCWSTR pszInfId,
|
||
|
OUT INetCfgComponent** ppIComp OPTIONAL)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
// Validate parameters.
|
||
|
//
|
||
|
if (FBadInPtr(pszInfId) || FBadOutPtrOptional(ppIComp))
|
||
|
{
|
||
|
hr = E_POINTER;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (ppIComp)
|
||
|
{
|
||
|
*ppIComp = NULL;
|
||
|
}
|
||
|
|
||
|
hr = HrLockAndTestForValidInterface (IF_DEFAULT);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
CComponent* pComponent;
|
||
|
|
||
|
pComponent = m_pNetConfig->Core.Components.
|
||
|
PFindComponentByInfId (pszInfId, NULL);
|
||
|
|
||
|
// Don't return interfaces to components that have had
|
||
|
// problem loading.
|
||
|
//
|
||
|
if (pComponent &&
|
||
|
pComponent->Ext.FLoadedOkayIfLoadedAtAll())
|
||
|
{
|
||
|
hr = S_OK;
|
||
|
|
||
|
if (ppIComp)
|
||
|
{
|
||
|
hr = pComponent->HrGetINetCfgComponentInterface (
|
||
|
this, ppIComp);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = S_FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TraceHr (ttidError, FAL, hr, (S_FALSE == hr),
|
||
|
"CImplINetCfg::FindComponent");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::QueryNetCfgClass (
|
||
|
IN const GUID* pguidClass,
|
||
|
IN REFIID riid,
|
||
|
OUT VOID** ppv)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
NETCLASS Class;
|
||
|
|
||
|
// Validate parameters.
|
||
|
//
|
||
|
if (FBadInPtr(pguidClass) || FBadInPtr(&riid) || FBadOutPtr(ppv))
|
||
|
{
|
||
|
hr = E_POINTER;
|
||
|
}
|
||
|
else if (NC_INVALID == (Class = NetClassEnumFromGuid(*pguidClass)))
|
||
|
{
|
||
|
hr = E_INVALIDARG;
|
||
|
*ppv = NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*ppv = NULL;
|
||
|
|
||
|
hr = HrLockAndTestForValidInterface (IF_DEFAULT);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
// Get the INetCfgClass interface from our cache.
|
||
|
//
|
||
|
Assert(Class < celems(m_apINetCfgClass));
|
||
|
INetCfgClass* pIClass = m_apINetCfgClass[Class];
|
||
|
|
||
|
// If we don't have it yet, create it.
|
||
|
//
|
||
|
if (!pIClass)
|
||
|
{
|
||
|
hr = CImplINetCfgClass::HrCreateInstance (
|
||
|
this,
|
||
|
Class,
|
||
|
&pIClass);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
pIClass = m_apINetCfgClass[Class] = pIClass;
|
||
|
Assert(pIClass);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Give the caller the requested interface.
|
||
|
//
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
hr = pIClass->QueryInterface (riid, ppv);
|
||
|
}
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TraceHr (ttidError, FAL, hr, FALSE, "CImplINetCfg::QueryNetCfgClass");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// INetCfgLock -
|
||
|
//
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::AcquireWriteLock (
|
||
|
IN DWORD cmsTimeout,
|
||
|
IN PCWSTR pszClientDescription,
|
||
|
OUT PWSTR* ppszClientDescription OPTIONAL)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
// Validate parameters.
|
||
|
//
|
||
|
if (FBadInPtr (pszClientDescription) ||
|
||
|
FBadOutPtrOptional (ppszClientDescription))
|
||
|
{
|
||
|
hr = E_POINTER;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceTag (ttidNetcfgBase, "%S is asking for the write lock",
|
||
|
pszClientDescription);
|
||
|
|
||
|
// Initialize the optional output parameter.
|
||
|
//
|
||
|
if (ppszClientDescription)
|
||
|
{
|
||
|
*ppszClientDescription = NULL;
|
||
|
}
|
||
|
|
||
|
// Only administrators and netconfig operators can make changes requiring the write lock.
|
||
|
//
|
||
|
if (!FIsUserAdmin() && !FIsUserNetworkConfigOps())
|
||
|
{
|
||
|
hr = E_ACCESSDENIED;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = HrLockAndTestForValidInterface (
|
||
|
IF_NEED_UNINITIALIZED | IF_REFUSE_REENTRANCY);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
// Wait for the mutex to become available.
|
||
|
//
|
||
|
if (m_WriteLock.WaitToAcquire (cmsTimeout,
|
||
|
pszClientDescription, ppszClientDescription))
|
||
|
{
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = S_FALSE;
|
||
|
}
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TraceHr (ttidError, FAL, hr, S_FALSE == hr,
|
||
|
"CImplINetCfg::AcquireWriteLock");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::ReleaseWriteLock ()
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
// This method that can be called whether we are initialized or
|
||
|
// not. That is why we don't call HrLockAndTestForValidInterface.
|
||
|
//
|
||
|
Lock ();
|
||
|
|
||
|
// Check if we need to refuse re-entrancy.
|
||
|
//
|
||
|
hr = HrCheckForReentrancy (IF_DEFAULT);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
m_WriteLock.ReleaseIfOwned ();
|
||
|
}
|
||
|
|
||
|
Unlock();
|
||
|
|
||
|
TraceHr (ttidError, FAL, hr, FALSE,
|
||
|
"CImplINetCfg::ReleaseWriteLock");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::IsWriteLocked (
|
||
|
OUT PWSTR* ppszClientDescription)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
// Validate parameters.
|
||
|
//
|
||
|
if (FBadOutPtrOptional (ppszClientDescription))
|
||
|
{
|
||
|
hr = E_POINTER;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (ppszClientDescription)
|
||
|
{
|
||
|
*ppszClientDescription = NULL;
|
||
|
}
|
||
|
|
||
|
// This method that can be called whether we are initialized or
|
||
|
// not. That is why we don't call HrLockAndTestForValidInterface.
|
||
|
//
|
||
|
Lock ();
|
||
|
|
||
|
// Check if we need to refuse re-entrancy.
|
||
|
//
|
||
|
hr = HrCheckForReentrancy (IF_DEFAULT);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
hr = (m_WriteLock.FIsLockedByAnyone (ppszClientDescription))
|
||
|
? S_OK : S_FALSE;
|
||
|
}
|
||
|
|
||
|
Unlock ();
|
||
|
}
|
||
|
|
||
|
TraceHr (ttidError, FAL, hr, S_FALSE == hr,
|
||
|
"CImplINetCfg::IsWriteLocked");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// INetCfgInternalSetup -
|
||
|
//
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::BeginBatchOperation ()
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
hr = HrLockAndTestForValidInterface (IF_NEED_WRITE_LOCK);
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
hr = m_pNetConfig->ModifyCtx.HrBeginBatchOperation ();
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
|
||
|
TraceHr (ttidError, FAL, hr, FALSE, "BeginBatchOperation");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::CommitBatchOperation ()
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
hr = HrLockAndTestForValidInterface (IF_NEED_WRITE_LOCK);
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
hr = m_pNetConfig->ModifyCtx.HrEndBatchOperation (EBO_COMMIT_NOW);
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
|
||
|
TraceHr (ttidError, FAL, hr, FALSE, "CommitBatchOperation");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::SelectWithFilterAndInstall (
|
||
|
IN HWND hwndParent,
|
||
|
IN const GUID* pClassGuid,
|
||
|
IN OBO_TOKEN* pOboToken OPTIONAL,
|
||
|
IN const CI_FILTER_INFO* pcfi OPTIONAL,
|
||
|
OUT INetCfgComponent** ppIComp OPTIONAL)
|
||
|
{
|
||
|
Assert (pClassGuid);
|
||
|
HRESULT hr;
|
||
|
NETCLASS Class;
|
||
|
|
||
|
if (FBadInPtr(pClassGuid))
|
||
|
{
|
||
|
hr = E_POINTER;
|
||
|
}
|
||
|
else if (FIsEnumerated ((Class = NetClassEnumFromGuid(*pClassGuid))))
|
||
|
{
|
||
|
// This fcn is only for selecting non-enumerated components.
|
||
|
//
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
else if (!FOboTokenValidForClass(pOboToken, Class) ||
|
||
|
FBadOutPtrOptional(ppIComp))
|
||
|
{
|
||
|
hr = E_POINTER;
|
||
|
}
|
||
|
else if (hwndParent && !IsWindow(hwndParent))
|
||
|
{
|
||
|
hr = E_INVALIDARG;
|
||
|
}
|
||
|
else if (S_OK == (hr = HrProbeOboToken(pOboToken)))
|
||
|
{
|
||
|
if (ppIComp)
|
||
|
{
|
||
|
*ppIComp = NULL;
|
||
|
}
|
||
|
|
||
|
hr = HrLockAndTestForValidInterface (
|
||
|
IF_NEED_WRITE_LOCK | IF_ALLOW_INSTALL_OR_REMOVE);
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
Assert (m_pNetConfig->ModifyCtx.m_fPrepared);
|
||
|
|
||
|
// if a filter info was specified, and it is for FC_LAN or FC_ATM,
|
||
|
// we need to set up the reserved member of the filter info for
|
||
|
// the class installer.
|
||
|
//
|
||
|
if (pcfi &&
|
||
|
((FC_LAN == pcfi->eFilter) || (FC_ATM == pcfi->eFilter)))
|
||
|
{
|
||
|
// If the pIComp member was NULL, then return invalid
|
||
|
// argument.
|
||
|
//
|
||
|
Assert (pcfi->pIComp);
|
||
|
CImplINetCfgComponent* pICompImpl;
|
||
|
pICompImpl = (CImplINetCfgComponent*)pcfi->pIComp;
|
||
|
|
||
|
hr = pICompImpl->HrIsValidInterface (IF_NEED_COMPONENT_DATA);
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
// The class installer only needs the component's
|
||
|
// range of upper interfaces so we need to store that in the
|
||
|
// reserved field of the filter info
|
||
|
Assert (pICompImpl->m_pComponent);
|
||
|
((CI_FILTER_INFO*)pcfi)->pvReserved = (void*)
|
||
|
pICompImpl->m_pComponent->Ext.PszUpperRange();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
COMPONENT_INSTALL_PARAMS* pParams;
|
||
|
hr = HrCiSelectComponent (Class, hwndParent, pcfi, &pParams);
|
||
|
|
||
|
if (pcfi)
|
||
|
{
|
||
|
// Don't want to return the private data to the client.
|
||
|
//
|
||
|
((CI_FILTER_INFO*)pcfi)->pvReserved = NULL;
|
||
|
}
|
||
|
|
||
|
// Check for installing a NET_SERVICE and active RAS connections
|
||
|
// exist. If so, warn the user that this may disconnect those
|
||
|
// connections. (This assumes that all filter components are
|
||
|
// of class NET_SERVICE.)
|
||
|
//
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
Assert (pParams);
|
||
|
if ((NC_NETSERVICE == pParams->Class) &&
|
||
|
FExistActiveRasConnections ())
|
||
|
{
|
||
|
INT nRet;
|
||
|
|
||
|
nRet = NcMsgBox (
|
||
|
_Module.GetResourceInstance(),
|
||
|
hwndParent,
|
||
|
IDS_WARNING_CAPTION,
|
||
|
IDS_ACTIVE_RAS_CONNECTION_WARNING,
|
||
|
MB_ICONQUESTION | MB_YESNO);
|
||
|
|
||
|
if (IDYES != nRet)
|
||
|
{
|
||
|
hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
Assert(pParams);
|
||
|
CComponent* pComponent;
|
||
|
|
||
|
// Check to see if the user selected a component that
|
||
|
// is already installed. If so, we need to reinstall.
|
||
|
//
|
||
|
pComponent = m_pNetConfig->Core.Components.
|
||
|
PFindComponentByInfId(pParams->pszInfId, NULL);
|
||
|
|
||
|
if (!pComponent)
|
||
|
{
|
||
|
pParams->pOboToken = pOboToken;
|
||
|
hr = m_pNetConfig->ModifyCtx.
|
||
|
HrInstallNewOrReferenceExistingComponent (
|
||
|
*pParams, &pComponent);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// reinstall. call Update.
|
||
|
hr = UpdateNonEnumeratedComponent (
|
||
|
pComponent->GetINetCfgComponentInterface(),
|
||
|
NSF_COMPONENT_UPDATE, 0);
|
||
|
}
|
||
|
|
||
|
// The above may return NETCFG_S_REBOOT so use SUCCEEDED instead
|
||
|
// of checking for S_OK only.
|
||
|
//
|
||
|
if (SUCCEEDED(hr) && ppIComp)
|
||
|
{
|
||
|
pComponent->HrGetINetCfgComponentInterface (
|
||
|
this,
|
||
|
ppIComp);
|
||
|
}
|
||
|
|
||
|
delete pParams;
|
||
|
}
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TraceHr (ttidError, FAL, hr,
|
||
|
(HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr) ||
|
||
|
(NETCFG_S_REBOOT == hr),
|
||
|
"SelectWithFilterAndInstall");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::EnumeratedComponentInstalled (
|
||
|
IN PVOID pv /* type of CComponent */)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
CComponent* pComponent;
|
||
|
|
||
|
pComponent = (CComponent*)pv;
|
||
|
|
||
|
Assert (pComponent);
|
||
|
Assert (FIsEnumerated(pComponent->Class()));
|
||
|
Assert (pComponent->m_pszInfId && *pComponent->m_pszInfId);
|
||
|
Assert (pComponent->m_pszPnpId && *pComponent->m_pszPnpId);
|
||
|
|
||
|
hr = HrLockAndTestForValidInterface (IF_NEED_WRITE_LOCK |
|
||
|
IF_ALLOW_INSTALL_OR_REMOVE);
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
COMPONENT_INSTALL_PARAMS Params;
|
||
|
CComponent* pReturned;
|
||
|
|
||
|
Assert (m_pNetConfig->ModifyCtx.m_fPrepared);
|
||
|
|
||
|
ZeroMemory(&Params, sizeof(Params));
|
||
|
Params.pComponent = (CComponent*)pComponent;
|
||
|
|
||
|
hr = m_pNetConfig->ModifyCtx.HrInstallNewOrReferenceExistingComponent (
|
||
|
Params,
|
||
|
&pReturned);
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
Assert (pComponent == pReturned);
|
||
|
}
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
|
||
|
TraceHr (ttidError, FAL, hr, FALSE, "EnumeratedComponentInstalled");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::EnumeratedComponentUpdated (
|
||
|
IN PCWSTR pszPnpId)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
Assert (pszPnpId && *pszPnpId);
|
||
|
|
||
|
hr = HrLockAndTestForValidInterface (IF_NEED_WRITE_LOCK);
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
CComponent* pComponent;
|
||
|
|
||
|
Assert (m_pNetConfig->ModifyCtx.m_fPrepared);
|
||
|
|
||
|
pComponent = m_pNetConfig->Core.Components.
|
||
|
PFindComponentByPnpId (pszPnpId);
|
||
|
|
||
|
if (pComponent)
|
||
|
{
|
||
|
// Note: Core info may have changed so load core info from driver key.
|
||
|
|
||
|
// If not a remote boot adapter, do a binding analysis to see if
|
||
|
// anything has changed.
|
||
|
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
|
||
|
TraceHr (ttidError, FAL, hr, FALSE, "EnumeratedComponentUpdated");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::UpdateNonEnumeratedComponent (
|
||
|
IN INetCfgComponent* pIComp,
|
||
|
IN DWORD dwSetupFlags,
|
||
|
IN DWORD dwUpgradeFromBuildNo)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
Assert (pIComp);
|
||
|
|
||
|
hr = HrLockAndTestForValidInterface (IF_NEED_WRITE_LOCK |
|
||
|
IF_ALLOW_INSTALL_OR_REMOVE);
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
CImplINetCfgComponent* pICompToUpdate;
|
||
|
|
||
|
Assert (m_pNetConfig->ModifyCtx.m_fPrepared);
|
||
|
|
||
|
pICompToUpdate = (CImplINetCfgComponent*)pIComp;
|
||
|
if (pICompToUpdate == NULL)
|
||
|
{
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
hr = pICompToUpdate->HrIsValidInterface (IF_NEED_COMPONENT_DATA);
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
HKEY hkeyInstance;
|
||
|
CComponent* pComponent = pICompToUpdate->m_pComponent;
|
||
|
|
||
|
|
||
|
hr = pComponent->HrOpenInstanceKey (KEY_READ_WRITE_DELETE, &hkeyInstance,
|
||
|
NULL, NULL);
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
DWORD dwNewCharacter;
|
||
|
COMPONENT_INSTALL_PARAMS Params;
|
||
|
ZeroMemory (&Params, sizeof (Params));
|
||
|
|
||
|
Params.Class = pComponent->Class();
|
||
|
Params.pszInfId = pComponent->m_pszInfId;
|
||
|
|
||
|
hr = HrCiInstallComponent (Params, NULL, &dwNewCharacter);
|
||
|
|
||
|
// The driver could not be selected because
|
||
|
// this component's section or inf is missing.
|
||
|
// We will remove the component in this case.
|
||
|
//
|
||
|
if (SPAPI_E_NO_DRIVER_SELECTED == hr)
|
||
|
{
|
||
|
pComponent->Refs.RemoveAllReferences();
|
||
|
|
||
|
hr = m_pNetConfig->ModifyCtx.
|
||
|
HrRemoveComponentIfNotReferenced (pComponent,
|
||
|
NULL, NULL);
|
||
|
}
|
||
|
else if (S_OK == hr)
|
||
|
{
|
||
|
pComponent->m_dwCharacter = dwNewCharacter;
|
||
|
|
||
|
AddOrRemoveDontExposeLowerCharacteristicIfNeeded (
|
||
|
pComponent);
|
||
|
|
||
|
hr = m_pNetConfig->ModifyCtx.HrUpdateComponent (
|
||
|
pComponent,
|
||
|
dwSetupFlags,
|
||
|
dwUpgradeFromBuildNo);
|
||
|
}
|
||
|
RegCloseKey (hkeyInstance);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
|
||
|
TraceHr (ttidError, FAL, hr, FALSE, "UpdateNonEnumeratedComponent");
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::EnumeratedComponentRemoved (
|
||
|
IN PCWSTR pszPnpId)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
Assert (pszPnpId);
|
||
|
|
||
|
hr = HrLockAndTestForValidInterface (IF_NEED_WRITE_LOCK |
|
||
|
IF_ALLOW_INSTALL_OR_REMOVE);
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
CComponent* pComponent;
|
||
|
|
||
|
Assert (m_pNetConfig->ModifyCtx.m_fPrepared);
|
||
|
|
||
|
pComponent = m_pNetConfig->Core.Components.
|
||
|
PFindComponentByPnpId (pszPnpId);
|
||
|
|
||
|
// If we found it, remove it. Otherwise our work here is done.
|
||
|
//
|
||
|
if (pComponent)
|
||
|
{
|
||
|
hr = m_pNetConfig->ModifyCtx.
|
||
|
HrRemoveComponentIfNotReferenced (pComponent, NULL, NULL);
|
||
|
}
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
|
||
|
TraceHr (ttidError, FAL, hr, FALSE, "EnumeratedComponentRemoved");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// INetCfgSpecialCase -
|
||
|
//
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::GetAdapterOrder (
|
||
|
OUT DWORD* pcAdapters,
|
||
|
OUT INetCfgComponent*** papAdapters,
|
||
|
OUT BOOL* pfWanAdaptersFirst)
|
||
|
{
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::SetAdapterOrder (
|
||
|
IN DWORD cAdapters,
|
||
|
IN INetCfgComponent** apAdapters,
|
||
|
IN BOOL fWanAdaptersFirst)
|
||
|
{
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::GetWanAdaptersFirst (
|
||
|
OUT BOOL* pfWanAdaptersFirst)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
// Validate parameters.
|
||
|
//
|
||
|
if (FBadOutPtr (pfWanAdaptersFirst))
|
||
|
{
|
||
|
hr = E_POINTER;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = HrLockAndTestForValidInterface (IF_DEFAULT);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
*pfWanAdaptersFirst = m_pNetConfig->Core.
|
||
|
StackTable.m_fWanAdaptersFirst;
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
}
|
||
|
TraceHr (ttidError, FAL, hr, FALSE,
|
||
|
"CImplINetCfg::GetWanAdaptersFirst");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::SetWanAdaptersFirst (
|
||
|
IN BOOL fWanAdaptersFirst)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
hr = HrLockAndTestForValidInterface (IF_NEED_WRITE_LOCK);
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
Assert (m_pNetConfig->ModifyCtx.m_fPrepared);
|
||
|
|
||
|
m_pNetConfig->Core.StackTable.SetWanAdapterOrder (!!fWanAdaptersFirst);
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
TraceHr (ttidError, FAL, hr, FALSE,
|
||
|
"CImplINetCfg::SetWanAdaptersFirst");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// INetCfgPnpReconfigCallback -
|
||
|
//
|
||
|
STDMETHODIMP
|
||
|
CImplINetCfg::SendPnpReconfig (
|
||
|
IN NCPNP_RECONFIG_LAYER Layer,
|
||
|
IN PCWSTR pszUpper,
|
||
|
IN PCWSTR pszLower,
|
||
|
IN PVOID pvData,
|
||
|
IN DWORD dwSizeOfData)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
if ((NCRL_NDIS != Layer) && (NCRL_TDI != Layer))
|
||
|
{
|
||
|
hr = E_INVALIDARG;
|
||
|
}
|
||
|
else if (FBadInPtr(pszUpper) || FBadInPtr(pszLower) ||
|
||
|
IsBadReadPtr(pvData, dwSizeOfData))
|
||
|
{
|
||
|
hr = E_POINTER;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
BOOL fOk;
|
||
|
UNICODE_STRING LowerString;
|
||
|
UNICODE_STRING UpperString;
|
||
|
UNICODE_STRING BindList;
|
||
|
WCHAR szLower [_MAX_PATH];
|
||
|
|
||
|
hr = S_OK;
|
||
|
|
||
|
*szLower = 0;
|
||
|
if (*pszLower)
|
||
|
{
|
||
|
wcscpy (szLower, L"\\Device\\");
|
||
|
wcsncat (szLower, pszLower, celems(szLower) - wcslen(szLower));
|
||
|
}
|
||
|
|
||
|
RtlInitUnicodeString (&LowerString, szLower);
|
||
|
RtlInitUnicodeString (&UpperString, pszUpper);
|
||
|
RtlInitUnicodeString (&BindList, NULL);
|
||
|
|
||
|
fOk = NdisHandlePnPEvent (
|
||
|
(NCRL_NDIS == Layer) ? NDIS : TDI,
|
||
|
RECONFIGURE,
|
||
|
&LowerString,
|
||
|
&UpperString,
|
||
|
&BindList,
|
||
|
pvData,
|
||
|
dwSizeOfData);
|
||
|
|
||
|
if (!fOk)
|
||
|
{
|
||
|
hr = HrFromLastWin32Error ();
|
||
|
}
|
||
|
}
|
||
|
TraceHr (ttidError, FAL, hr, FALSE, "CImplINetCfg::SendPnpReconfig");
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
// CImplINetCfgHolder -
|
||
|
//
|
||
|
|
||
|
VOID
|
||
|
CImplINetCfgHolder::HoldINetCfg (
|
||
|
CImplINetCfg* pINetCfg)
|
||
|
{
|
||
|
Assert(pINetCfg);
|
||
|
AddRefObj (pINetCfg->GetUnknown());
|
||
|
m_pINetCfg = pINetCfg;
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
CImplINetCfgHolder::HrLockAndTestForValidInterface (
|
||
|
DWORD dwFlags)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
Lock();
|
||
|
|
||
|
hr = m_pINetCfg->HrIsValidInterface (dwFlags);
|
||
|
|
||
|
if (S_OK != hr)
|
||
|
{
|
||
|
Unlock();
|
||
|
}
|
||
|
|
||
|
TraceHr (ttidError, FAL, hr, FALSE,
|
||
|
"CImplINetCfgHolder::HrLockAndTestForValidInterface");
|
||
|
return hr;
|
||
|
}
|