windows-nt/Source/XPSP1/NT/net/config/upgrade/netupgrd/conflict.cpp

919 lines
26 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1997.
//
// File: C O N F L I C T . C P P
//
// Contents: Code to handle and display software/hardware conflicts
// during upgrade
//
// Notes:
//
// Author: kumarp 04/12/97 17:17:27
//
//----------------------------------------------------------------------------
#include "pch.h"
#pragma hdrstop
#include "conflict.h"
#include "infmap.h"
#include "kkreg.h"
#include "kkstl.h"
#include "kkutils.h"
#include "ncreg.h"
#include "netreg.h"
#include "nustrs.h"
#include "nuutils.h"
#include "oemupg.h"
#include "ncsvc.h"
// ----------------------------------------------------------------------
// External string constants
//
extern const WCHAR c_szRegValServiceName[];
extern const WCHAR c_szParameters[];
// ----------------------------------------------------------------------
// String constants
//
const WCHAR sz_DLC[] = L"DLC";
const WCHAR sz_NBF[] = L"NBF";
const WCHAR sz_SNAServerKey[] = L"SOFTWARE\\Microsoft\\Sna Server\\CurrentVersion";
const WCHAR sz_SNAServerVersion[] = L"SNAVersion";
// ----------------------------------------------------------------------
TPtrList* g_pplNetComponents=NULL;
// ----------------------------------------------------------------------
//
// Function: UpgradeConflictsFound
//
// Purpose: Find out if upgrade conflicts have been detected
//
// Arguments: None
//
// Returns: TRUE if found, FALSE otherwise
//
// Author: kumarp 17-December-97
//
// Notes:
//
BOOL UpgradeConflictsFound()
{
return (g_pplNetComponents && g_pplNetComponents->size());
}
// ----------------------------------------------------------------------
//
// Function: CNetComponent::CNetComponent
//
// Purpose: constructor for class CNetComponent
//
// Arguments:
// pszPreNT5InfId [in] pre NT5 InfID (e.g. IEEPRO)
// pszPreNT5Instance [in] pre NT5 instance name (e.g. IEEPRO2)
// pszDescription [in] description
// eType [in] type (software / hardware)
//
// Author: kumarp 17-December-97
//
// Notes:
//
CNetComponent::CNetComponent(PCWSTR pszPreNT5InfId,
PCWSTR pszPreNT5Instance,
PCWSTR pszDescription,
EComponentType eType)
: m_strPreNT5InfId(pszPreNT5InfId),
m_strServiceName(pszPreNT5Instance),
m_strDescription(pszDescription),
m_eType(eType)
{
}
// ----------------------------------------------------------------------
//
// Function: AddToComponentList
//
// Purpose: Construct and add a CNetComponent to the specified list
//
// Arguments:
// pplComponents [in] pointer to list of
// pszPreNT5InfId [in] pre NT5 InfID (e.g. IEEPRO)
// pszPreNT5Instance [in] pre NT5 instance name (e.g. IEEPRO2)
// pszDescription [in] description
// eType [in] type (software / hardware)
//
// Returns: None
//
// Author: kumarp 17-December-97
//
// Notes:
//
void AddToComponentList(IN TPtrList* pplComponents,
IN PCWSTR pszPreNT5InfId,
IN PCWSTR pszPreNT5Instance,
IN PCWSTR pszDescription,
EComponentType eType)
{
AssertValidReadPtr(pplComponents);
AssertValidReadPtr(pszPreNT5InfId);
AssertValidReadPtr(pszPreNT5Instance);
AssertValidReadPtr(pszDescription);
if (pplComponents)
{
CNetComponent* pnc;
pnc = new CNetComponent(pszPreNT5InfId, pszPreNT5Instance,
pszDescription, eType);
if (pnc)
{
pplComponents->push_back(pnc);
}
}
#ifdef ENABLETRACE
tstring strMessage;
GetUnsupportedMessageBool((eType == CT_Hardware), pszPreNT5InfId,
pszDescription, &strMessage);
TraceTag(ttidNetUpgrade, "%S", strMessage.c_str());
#endif
}
// ----------------------------------------------------------------------
//
// Function: AddToConflictsList
//
// Purpose: Construct and add a CNetComponent to the conflict list
//
// Arguments:
// pszPreNT5InfId [in] pre NT5 InfID (e.g. IEEPRO)
// pszPreNT5Instance [in] pre NT5 instance name (e.g. IEEPRO2)
// pszDescription [in] description
// eType [in] type (software / hardware)
//
// Returns: None
//
// Author: kumarp 17-December-97
//
// Notes:
//
void AddToConflictsList(IN PCWSTR pszPreNT5InfId,
IN PCWSTR pszPreNT5Instance,
IN PCWSTR pszDescription,
EComponentType eType)
{
if (!g_pplNetComponents)
{
g_pplNetComponents = new TPtrList;
}
AddToComponentList(g_pplNetComponents, pszPreNT5InfId,
pszPreNT5Instance, pszDescription, eType);
}
// ----------------------------------------------------------------------
//
// Function: HrGetAdapterParamsKeyFromInstance
//
// Purpose: Get handle of Parameters key using adapter instace key
//
// Arguments:
// hkeyAdapterInstance [in] handle of
// phkeyAdapterParams [out] pointer to handle of
//
// Returns: S_OK on success, otherwise an error code
//
// Author: kumarp 17-December-97
//
// Notes:
//
HRESULT HrGetAdapterParamsKeyFromInstance(IN HKEY hkeyAdapterInstance,
OUT HKEY* phkeyAdapterParams)
{
DefineFunctionName("HrGetAdapterParamsKeyFromInstance");
Assert(hkeyAdapterInstance);
AssertValidWritePtr(phkeyAdapterParams);
HRESULT hr=S_OK;
tstring strServiceName;
hr = HrRegQueryString(hkeyAdapterInstance, c_szRegValServiceName,
&strServiceName);
if (S_OK == hr)
{
hr = HrRegOpenServiceSubKey(strServiceName.c_str(), c_szParameters,
KEY_READ, phkeyAdapterParams);
}
TraceError(__FUNCNAME__, hr);
return hr;
}
// ----------------------------------------------------------------------
//
// Function: HrGenerateHardwareConflictList
//
// Purpose: Detect upgrade conflicts for h/w components
//
// Arguments: None
//
// Returns: S_OK on success, otherwise an error code
//
// Author: kumarp 17-December-97
//
// Notes:
//
HRESULT HrGenerateHardwareConflictList()
{
DefineFunctionName("HrGenerateHardwareConflictList");
HKEY hkeyAdapters;
HKEY hkeyAdapter;
DWORD dwHidden;
tstring strAdapterDescription;
tstring strPreNT5InfId;
tstring strServiceName;
tstring strNT5InfId;
tstring strAdapterType;
BOOL fIsOemAdapter;
BOOL fRealNetCard = FALSE;
HRESULT hr=S_OK;
hr = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegKeyAdapterHome,
KEY_READ, &hkeyAdapters);
if (S_OK == hr)
{
WCHAR szBuf[MAX_PATH];
FILETIME time;
DWORD dwSize = celems(szBuf);
DWORD dwRegIndex = 0;
while(S_OK == (hr = HrRegEnumKeyEx(hkeyAdapters, dwRegIndex++, szBuf,
&dwSize, NULL, NULL, &time)))
{
dwSize = celems(szBuf);
Assert(*szBuf);
hr = HrRegOpenKeyEx(hkeyAdapters, szBuf, KEY_READ, &hkeyAdapter);
if (hr == S_OK)
{
hr = HrRegQueryDword(hkeyAdapter, c_szHidden, &dwHidden);
// for REAL netcards, "Hidden" is absent or if present the value is 0
if (S_OK == hr)
{
fRealNetCard = (dwHidden == 0);
}
else if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
{
fRealNetCard = TRUE;
hr = S_OK;
}
if ((S_OK == hr) && fRealNetCard)
{
hr = HrGetPreNT5InfIdAndDesc(hkeyAdapter, &strPreNT5InfId,
&strAdapterDescription, &
strServiceName);
if (S_OK == hr)
{
HKEY hkeyAdapterParams;
hr = HrGetAdapterParamsKeyFromInstance(hkeyAdapter,
&hkeyAdapterParams);
if (S_OK == hr)
{
hr = HrMapPreNT5NetCardInfIdToNT5InfId(hkeyAdapterParams,
strPreNT5InfId.c_str(),
&strNT5InfId,
&strAdapterType,
&fIsOemAdapter,
NULL);
if (S_FALSE == hr)
{
AddToConflictsList(strPreNT5InfId.c_str(),
strServiceName.c_str(),
strAdapterDescription.c_str(),
CT_Hardware);
}
}
RegCloseKey(hkeyAdapterParams);
}
}
RegCloseKey(hkeyAdapter);
}
}
if ((HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr) ||
(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr))
{
hr = S_OK;
}
RegCloseKey(hkeyAdapters);
}
TraceError(__FUNCNAME__, hr);
return hr;
}
// ----------------------------------------------------------------------
//
// Function: ShouldIgnoreComponentForInstall
//
// Purpose: Determine if a components should be ignored when
// we are checking to see for obsolesence
//
// Arguments:
// pszComponentName [in] name of component
//
// Returns: TRUE on success, FALSE otherwise
//
// Author: deonb 22-September-2000
//
BOOL
ShouldIgnoreComponentForInstall (
IN PCWSTR pszComponentName)
{
BOOL fRet=FALSE;
if (
// We ignore NETBEUI since it's checked by DOSNET.INF already (NTBUG9: 181798)
(lstrcmpiW(pszComponentName, sz_NBF) == 0) ||
// We ignore DLC since it's checked by HrGenerateNT5ConflictList (NTBUG9: 187135)
(lstrcmpiW(pszComponentName, sz_DLC) == 0)
)
{
fRet = TRUE;
}
else
{
fRet = FALSE;
}
return fRet;
}
// ----------------------------------------------------------------------
//
// Function: HrGenerateSoftwareConflictListForProvider
//
// Purpose: Detect upgrade conflicts for s/w components of a provider
//
// Arguments:
// pszSoftwareProvider [in] name of software provider (e.g. Microsoft)
//
// Returns: S_OK on success, otherwise an error code
//
// Author: kumarp 17-December-97
//
// Notes:
//
HRESULT HrGenerateSoftwareConflictListForProvider(IN PCWSTR pszSoftwareProvider)
{
DefineFunctionName("HrGenerateSoftwareConflictList");
HRESULT hr=S_OK;
HKEY hkeyProvider;
HKEY hkeyProductCurrentVersion;
tstring strProvider;
strProvider = c_szRegKeySoftware;
AppendToPath(&strProvider, pszSoftwareProvider);
hr = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE, strProvider.c_str(),
KEY_READ, &hkeyProvider);
if (S_OK == hr)
{
tstring strPreNT5InfId;
tstring strNT5InfId;
tstring strProductCurrentVersion;
tstring strDescription;
tstring strServiceName;
tstring strSoftwareType;
WCHAR szBuf[MAX_PATH];
FILETIME time;
DWORD dwSize = celems(szBuf);
DWORD dwRegIndex = 0;
BOOL fIsOemComponent;
while(S_OK == (hr = HrRegEnumKeyEx(hkeyProvider, dwRegIndex++, szBuf,
&dwSize, NULL, NULL, &time)))
{
dwSize = celems(szBuf);
Assert(*szBuf);
if (!ShouldIgnoreComponentForInstall(szBuf))
{
strProductCurrentVersion = szBuf;
AppendToPath(&strProductCurrentVersion, c_szRegKeyCurrentVersion);
// Look for Component\CurrentVersion
hr = HrRegOpenKeyEx(hkeyProvider, strProductCurrentVersion.c_str(),
KEY_READ, &hkeyProductCurrentVersion);
if (hr == S_OK)
{
// Under Component\CurrentVersion, look for "SoftwareType" value
hr = HrRegQueryString(hkeyProductCurrentVersion,
c_szRegValSoftwareType,
&strSoftwareType);
if (!lstrcmpiW(strSoftwareType.c_str(), c_szSoftwareTypeDriver))
{
// ignore components of type "driver"
hr = S_OK;
}
else
{
hr = HrGetPreNT5InfIdAndDesc(hkeyProductCurrentVersion,
&strPreNT5InfId, &strDescription,
&strServiceName);
if (S_OK == hr)
{
hr = HrMapPreNT5NetComponentInfIDToNT5InfID(
strPreNT5InfId.c_str(), &strNT5InfId,
&fIsOemComponent, NULL, NULL);
if (S_FALSE == hr)
{
AddToConflictsList(strPreNT5InfId.c_str(),
strServiceName.c_str(),
strDescription.c_str(),
CT_Software);
}
}
RegCloseKey(hkeyProductCurrentVersion);
}
}
}
}
if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr)
{
hr = S_OK;
}
RegCloseKey(hkeyProvider);
}
TraceError(__FUNCNAME__, hr);
return hr;
}
// ----------------------------------------------------------------------
//
// Function: HrGenerateSoftwareConflictList
//
// Purpose: Detect upgrade conflicts for s/w components of all providers
//
// Arguments: None
//
// Returns: S_OK on success, otherwise an error code
//
// Author: kumarp 17-December-97
//
// Notes:
//
HRESULT HrGenerateSoftwareConflictList()
{
DefineFunctionName("HrGenerateSoftwareConflictList");
HRESULT hr=S_OK;
HKEY hkeySoftware;
hr = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegKeySoftware,
KEY_READ, &hkeySoftware);
if (S_OK == hr)
{
WCHAR szBuf[MAX_PATH];
FILETIME time;
DWORD dwSize = celems(szBuf);
DWORD dwRegIndex = 0;
while(S_OK == (hr = HrRegEnumKeyEx(hkeySoftware, dwRegIndex++, szBuf,
&dwSize, NULL, NULL, &time)))
{
dwSize = celems(szBuf);
Assert(*szBuf);
hr = HrGenerateSoftwareConflictListForProvider(szBuf);
}
if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr)
{
hr = S_OK;
}
RegCloseKey(hkeySoftware);
}
TraceError(__FUNCNAME__, hr);
return hr;
}
// ----------------------------------------------------------------------
//
// Function: HrGenerateNT5ConflictList
//
// Purpose: Detect upgrade conflicts from Windows 2000
//
// Arguments: None
//
// Returns: S_OK on success, otherwise an error code
//
// Author: deonb 20-September 2000
//
// Notes:
//
HRESULT HrGenerateNT5ConflictList()
{
HRESULT hr = S_OK;
tstring strDescription;
BOOL fInstalled;
if ( ShouldRemoveDLC(&strDescription, &fInstalled) )
{
// Add to conflict list only if DLC is installed, otherwise user will see
// a warning message even if it is uninstalled.
//
if ( fInstalled )
{
if (strDescription.empty())
{
// Couldn't find a description from previous O/S. Oh well - just use "DLC".
AddToConflictsList(sz_DLC, sz_DLC, sz_DLC, CT_Software);
}
else
{
AddToConflictsList(sz_DLC, sz_DLC, strDescription.c_str(), CT_Software);
}
}
}
return hr;
}
// ----------------------------------------------------------------------
//
// Function: ShouldRemoveDLC
//
// Purpose: Determine if DLC should be removed during the upgrade irrespective of whether
// it is currently installed or not.
//
// Arguments:
// strDLCDesc [out] pointer to DLC Description string.
// pfInstalled [out] pointer to a boolean indicating if DLC is currently installed.
// Valid for X86 only.
//
// Returns: TRUE if DLC should be removed.
//
// Author: asinha 3/27/2001
//
// Notes:
//
BOOL ShouldRemoveDLC (tstring *strDLCDesc,
BOOL *pfInstalled)
{
HRESULT hr;
BOOL fDlcRemove = FALSE;
if ( pfInstalled )
{
*pfInstalled = FALSE;
}
// Check if DLC is installed (only for x86 - for IA64 we don't care (NTBUG9:186001) )
#ifdef _X86_
CServiceManager sm;
CService srv;
if ( pfInstalled )
{
hr = sm.HrOpenService(&srv, sz_DLC);
if (SUCCEEDED(hr)) // DLC Service is installed
{
*pfInstalled = TRUE;
LPQUERY_SERVICE_CONFIG pConfig;
HRESULT hr = srv.HrQueryServiceConfig (&pConfig);
if (S_OK == hr)
{
if ( strDLCDesc )
{
*strDLCDesc = pConfig->lpDisplayName;
}
MemFree (pConfig);
}
srv.Close();
}
else
{
*pfInstalled = FALSE;
}
}
fDlcRemove = TRUE;
OSVERSIONINFO osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
// If SNA Server is installed (on NT5) - this is ok
if (GetVersionEx(&osvi)) // Can't use VerifyVersionInfo - we have to run on NT4.
{
if ( (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
(osvi.dwMajorVersion == 5) &&
(osvi.dwMinorVersion == 0) )
{
// If SNA Server is installed - we still allow this
hr = sm.HrOpenService(&srv, L"SnaServr");
if (SUCCEEDED(hr)) // Service is installed
{
srv.Close();
// Only if SNA version is 5.0 or more.
HKEY hkeySnaServer;
hr = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE, sz_SNAServerKey, KEY_READ, &hkeySnaServer);
if (S_OK == hr)
{
tstring tstr;
hr = HrRegQueryString(hkeySnaServer, sz_SNAServerVersion, &tstr);
if (S_OK == hr)
{
int nSnaVersion = _wtoi(tstr.c_str());
if (nSnaVersion >= 5)
{
fDlcRemove = FALSE;
}
}
RegCloseKey(hkeySnaServer);
}
}
}
// Never complain about DLC if upgrading from Whistler+
if ( (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
( ( (osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion >= 1) ) ||
(osvi.dwMajorVersion >= 6) ) )
{
fDlcRemove = FALSE;
}
}
#endif
return fDlcRemove;
}
// ----------------------------------------------------------------------
//
// Function: HrGenerateConflictList
//
// Purpose: Generate upgrade conflict list for s/w and h/w components
//
// Arguments: None
//
// Returns: S_OK on success, otherwise an error code
//
// Author: kumarp 17-December-97
//
// Notes:
//
HRESULT HrGenerateConflictList(OUT UINT* pcNumConflicts)
{
DefineFunctionName("HrGenerateConflictList");
HRESULT hr=S_OK;
(void) HrGenerateHardwareConflictList();
(void) HrGenerateSoftwareConflictList();
(void) HrGenerateNT5ConflictList();
if (g_pplNetComponents && g_pplNetComponents->size())
{
*pcNumConflicts = g_pplNetComponents->size();
}
else
{
*pcNumConflicts = 0;
}
TraceError(__FUNCNAME__, hr);
return hr;
}
// ----------------------------------------------------------------------
//
// Function: UninitConflictList
//
// Purpose: Uninitialize and destroy global lists holding upgrade conflicts
//
// Arguments: None
//
// Returns: None
//
// Author: kumarp 17-December-97
//
// Notes:
//
void UninitConflictList()
{
if (g_pplNetComponents)
{
CNetComponent* pnc;
TPtrListIter pos;
for (pos = g_pplNetComponents->begin();
pos != g_pplNetComponents->end(); pos++)
{
pnc = (CNetComponent*) *pos;
delete pnc;
}
g_pplNetComponents->erase(g_pplNetComponents->begin(),
g_pplNetComponents->end());
delete g_pplNetComponents;
g_pplNetComponents = NULL;
}
}
// ----------------------------------------------------------------------
//
// Function: HrResolveConflictsFromList
//
// Purpose: Use the specified netmap.inf file to find out if any
// of the components currently detected as unsupported can
// be mapped. If such components are found, then delete them
// from pplComponents if fDeleteResolvedItemsFromList is TRUE
//
// Arguments:
// fDeleteResolvedItemsFromList [in] flag (see above)
// pplComponents [in] pointer to list of unsupported components
// hinfNetMap [in] handle of netmap.inf file
// pdwNumConflictsResolved [out] number of components mapped using the
// specified netmap.inf
//
// Returns: S_OK on success, otherwise an error code
//
// Author: kumarp 17-December-97
//
// Notes:
//
HRESULT HrResolveConflictsFromList(IN BOOL fDeleteResolvedItemsFromList,
IN TPtrList* pplComponents,
IN HINF hinfNetMap,
OUT DWORD* pdwNumConflictsResolved,
OUT BOOL* pfHasUpgradeHelpInfo)
{
DefineFunctionName("HrResolveConflictsFromList");
Assert(hinfNetMap);
AssertValidWritePtr(pdwNumConflictsResolved);
HRESULT hr=S_OK;
*pdwNumConflictsResolved = 0;
if (pplComponents && (pplComponents->size() > 0))
{
TPtrListIter pos;
TPtrListIter tpos;
tstring strPreNT5InfId;
CNetComponent* pnc;
HKEY hkeyAdapterParams;
BOOL fIsOemComponent;
tstring strNT5InfId;
pos = pplComponents->begin();
while(pos != pplComponents->end())
{
pnc = (CNetComponent*) *pos;
strNT5InfId = c_szEmpty;
if (pnc->m_eType == CT_Hardware)
{
hr = HrRegOpenServiceSubKey(pnc->m_strServiceName.c_str(), c_szParameters,
KEY_READ, &hkeyAdapterParams);
if (S_OK == hr)
{
fIsOemComponent = FALSE;
hr = HrMapPreNT5NetCardInfIdInInf(hinfNetMap, hkeyAdapterParams,
pnc->m_strPreNT5InfId.c_str(),
&strNT5InfId,
NULL, &fIsOemComponent);
RegCloseKey(hkeyAdapterParams);
}
}
else
{
hr = HrMapPreNT5NetComponentInfIDInInf(hinfNetMap,
pnc->m_strPreNT5InfId.c_str(),
&strNT5InfId,
NULL,
&fIsOemComponent);
if ((S_FALSE == hr) && !strNT5InfId.empty())
{
*pfHasUpgradeHelpInfo = TRUE;
}
}
tpos = pos;
pos++;
// if we found a map, remove the entry
if (S_OK == hr)
{
(*pdwNumConflictsResolved)++;
if (fDeleteResolvedItemsFromList)
{
delete pnc;
pplComponents->erase(tpos);
}
}
}
}
else
{
hr = S_FALSE;
}
TraceErrorOptional(__FUNCNAME__, hr, (S_FALSE == hr));
return hr;
}
// ----------------------------------------------------------------------
//
// Function: HrUpdateConflictList
//
// Purpose: Use the specified netmap.inf file to find out if any
// of the components currently detected as unsupported can
// be mapped. If such components are found, then delete them
// from pplComponents if fDeleteResolvedItemsFromList is TRUE
//
// Arguments:
// fDeleteResolvedItemsFromList [in] flag (see above)
// hinfNetMap [in] handle of netmap.inf file
// pdwNumConflictsResolved [out] number of components mapped using the
// specified netmap.inf
// Returns: S_OK on success, otherwise an error code
//
// Author: kumarp 17-December-97
//
// Notes:
//
HRESULT HrUpdateConflictList(IN BOOL fDeleteResolvedItemsFromList,
IN HINF hinfNetMap,
OUT DWORD* pdwNumConflictsResolved,
OUT BOOL* pfHasUpgradeHelpInfo)
{
DefineFunctionName("HrUpdateConflictList");
HRESULT hr=S_OK;
hr = HrResolveConflictsFromList(fDeleteResolvedItemsFromList,
g_pplNetComponents, hinfNetMap,
pdwNumConflictsResolved,
pfHasUpgradeHelpInfo);
TraceErrorOptional(__FUNCNAME__, hr, (S_FALSE == hr));
return hr;
}
HRESULT HrGetConflictsList(OUT TPtrList** ppplNetComponents)
{
HRESULT hr=S_FALSE;
if (g_pplNetComponents)
{
hr = S_OK;
*ppplNetComponents = g_pplNetComponents;
}
return hr;
}