windows-nt/Source/XPSP1/NT/net/config/netoc/netoc.cpp
2020-09-26 16:20:57 +08:00

2613 lines
81 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1997.
//
// File: N E T O C . C P P
//
// Contents: Functions for handling installation and removal of optional
// networking components.
//
// Notes:
//
// Author: danielwe 28 Apr 1997
//
//----------------------------------------------------------------------------
#include "pch.h"
#pragma hdrstop
#include "lancmn.h"
#include "ncacs.h"
#include "ncatlui.h"
#include "ncbeac.h"
#include "nccm.h"
#include "ncdhcps.h"
#include "ncias.h"
#include "ncmisc.h"
#include "ncmsz.h"
#include "ncnetcfg.h"
#include "ncnetcon.h"
#include "ncoc.h"
#include "ncperms.h"
#include "ncreg.h"
#include "ncsetup.h"
#include "ncsfm.h"
#include "ncstring.h"
#include "ncsvc.h"
#include "ncxbase.h"
#include "netcfgn.h"
#include "netcon.h"
#include "netoc.h"
#include "netocp.h"
#include "netocx.h"
#include "resource.h"
//
// External component install functions.
// Add an entry in this table for each component that requires additional,
// non-common installation support.
//
// NOTE: The component name should match the section name in the INF.
//
#pragma BEGIN_CONST_SECTION
static const OCEXTPROCS c_aocepMap[] =
{
{ L"MacSrv", HrOcExtSFM },
{ L"DHCPServer", HrOcExtDHCPServer },
{ L"NetCM", HrOcExtCM },
{ L"WINS", HrOcExtWINS },
{ L"DNS", HrOcExtDNS },
{ L"ACS", HrOcExtACS },
{ L"SNMP", HrOcExtSNMP },
{ L"IAS", HrOcExtIAS },
{ L"BEACON", HrOcExtBEACON },
};
#pragma END_CONST_SECTION
static const INT c_cocepMap = celems(c_aocepMap);
// generic strings
static const WCHAR c_szUninstall[] = L"Uninstall";
static const WCHAR c_szServices[] = L"StartServices";
static const WCHAR c_szDependOnComp[] = L"DependOnComponents";
static const WCHAR c_szVersionSection[] = L"Version";
static const WCHAR c_szProvider[] = L"Provider";
static const WCHAR c_szDefManu[] = L"Unknown";
static const WCHAR c_szInfRef[] = L"SubCompInf";
static const WCHAR c_szDesc[] = L"OptionDesc";
static const WCHAR c_szNoDepends[] = L"NoDepends";
// static-IP verification
static const WCHAR c_szTcpipInterfacesPath[] =
L"System\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
static const WCHAR c_szEnableDHCP[] = L"EnableDHCP";
extern const WCHAR c_szOcMainSection[];
static const DWORD c_dwUpgradeMask = SETUPOP_WIN31UPGRADE |
SETUPOP_WIN95UPGRADE |
SETUPOP_NTUPGRADE;
OCM_DATA g_ocmData;
typedef list<NETOCDATA*> ListOcData;
ListOcData g_listOcData;
//+---------------------------------------------------------------------------
//
// Function: PnocdFindComponent
//
// Purpose: Looks for the given component name in the list of known
// components.
//
// Arguments:
// pszComponent [in] Name of component to lookup.
//
// Returns: Pointer to component's data.
//
// Author: danielwe 23 Feb 1998
//
// Notes:
//
NETOCDATA *PnocdFindComponent(PCWSTR pszComponent)
{
ListOcData::iterator iterList;
for (iterList = g_listOcData.begin();
iterList != g_listOcData.end();
iterList++)
{
NETOCDATA * pnocd;
pnocd = *iterList;
if (!lstrcmpiW(pnocd->pszComponentId, pszComponent))
{
return pnocd;
}
}
return NULL;
}
//+---------------------------------------------------------------------------
//
// Function: DeleteAllComponents
//
// Purpose: Removes all components from our list and frees all associated
// data.
//
// Arguments:
// (none)
//
// Returns: Nothing.
//
// Author: danielwe 23 Feb 1998
//
// Notes:
//
VOID DeleteAllComponents()
{
ListOcData::iterator iterList;
for (iterList = g_listOcData.begin();
iterList != g_listOcData.end();
iterList++)
{
NETOCDATA * pnocd;
pnocd = (*iterList);
if (pnocd->hinfFile)
{
SetupCloseInfFile(pnocd->hinfFile);
}
delete pnocd;
}
g_listOcData.erase(g_listOcData.begin(), g_listOcData.end());
}
//+---------------------------------------------------------------------------
//
// Function: AddComponent
//
// Purpose: Adds a component to our list.
//
// Arguments:
// pszComponent [in] Name of component to add.
// pnocd [in] Data to associate with component.
//
// Returns: Nothing.
//
// Author: danielwe 23 Feb 1998
//
// Notes:
//
VOID AddComponent(PCWSTR pszComponent, NETOCDATA *pnocd)
{
Assert(pszComponent);
Assert(pnocd);
pnocd->pszComponentId = SzDupSz(pszComponent);
g_listOcData.push_back(pnocd);
}
//+---------------------------------------------------------------------------
//
// Function: NetOcSetupProcHelper
//
// Purpose: Main entry point for optional component installs
//
// Arguments:
// pvComponentId [in] Component Id (string)
// pvSubcomponentId [in] Sub component Id (string)
// uFunction [in] Function being performed
// uParam1 [in] First param to function
// pvParam2 [in, out] Second param to function
//
// Returns: Win32 error if failure
//
// Author: danielwe 17 Dec 1997
//
// Notes:
//
DWORD NetOcSetupProcHelper(LPCVOID pvComponentId, LPCVOID pvSubcomponentId,
UINT uFunction, UINT uParam1, LPVOID pvParam2)
{
TraceFileFunc(ttidNetOc);
HRESULT hr = S_OK;
switch (uFunction)
{
case OC_PREINITIALIZE:
return OCFLAG_UNICODE;
case OC_QUERY_CHANGE_SEL_STATE:
TraceTag(ttidNetOc, "OC_QUERY_CHANGE_SEL_STATE: %S, %ld, 0x%08X.",
pvSubcomponentId ? pvSubcomponentId : L"null", uParam1,
pvParam2);
if (FHasPermission(NCPERM_AddRemoveComponents))
{
hr = HrOnQueryChangeSelState(reinterpret_cast<PCWSTR>(pvSubcomponentId),
uParam1, reinterpret_cast<UINT_PTR>(pvParam2));
if (S_OK == hr)
{
return TRUE;
}
}
else
{
NcMsgBox(g_ocmData.hwnd, IDS_OC_CAPTION, IDS_OC_NO_PERMS,
MB_ICONSTOP | MB_OK);
}
return FALSE;
case OC_QUERY_SKIP_PAGE:
TraceTag(ttidNetOc, "OC_QUERY_SKIP_PAGE: %ld", uParam1);
return FOnQuerySkipPage(static_cast<OcManagerPage>(uParam1));
case OC_WIZARD_CREATED:
TraceTag(ttidNetOc, "OC_WIZARD_CREATED: 0x%08X", pvParam2);
OnWizardCreated(reinterpret_cast<HWND>(pvParam2));
break;
case OC_INIT_COMPONENT:
TraceTag(ttidNetOc, "OC_INIT_COMPONENT: %S", pvSubcomponentId ?
pvSubcomponentId : L"null");
hr = HrOnInitComponent(reinterpret_cast<PSETUP_INIT_COMPONENT>(pvParam2));
break;
case OC_ABOUT_TO_COMMIT_QUEUE:
TraceTag(ttidNetOc, "OC_ABOUT_TO_COMMIT_QUEUE: %S", pvSubcomponentId ?
pvSubcomponentId : L"null");
hr = HrOnPreCommitFileQueue(reinterpret_cast<PCWSTR>(pvSubcomponentId));
break;
case OC_CALC_DISK_SPACE:
// Ignore return value for now. This is not fatal anyway.
(VOID) HrOnCalcDiskSpace(reinterpret_cast<PCWSTR>(pvSubcomponentId),
uParam1, reinterpret_cast<HDSKSPC>(pvParam2));
break;
case OC_QUERY_STATE:
return DwOnQueryState(reinterpret_cast<PCWSTR>(pvSubcomponentId),
uParam1 == OCSELSTATETYPE_FINAL);
case OC_QUEUE_FILE_OPS:
TraceTag(ttidNetOc, "OC_QUEUE_FILE_OPS: %S, 0x%08X", pvSubcomponentId ?
pvSubcomponentId : L"null",
pvParam2);
hr = HrOnQueueFileOps(reinterpret_cast<PCWSTR>(pvSubcomponentId),
reinterpret_cast<HSPFILEQ>(pvParam2));
break;
case OC_COMPLETE_INSTALLATION:
TraceTag(ttidNetOc, "OC_COMPLETE_INSTALLATION: %S, %S", pvComponentId ?
pvComponentId : L"null",
pvSubcomponentId ? pvSubcomponentId : L"null");
hr = HrOnCompleteInstallation(reinterpret_cast<PCWSTR>(pvComponentId),
reinterpret_cast<PCWSTR>(pvSubcomponentId));
break;
case OC_QUERY_STEP_COUNT:
return DwOnQueryStepCount(reinterpret_cast<PCWSTR>(pvSubcomponentId));
case OC_CLEANUP:
OnCleanup();
break;
default:
break;
}
if (g_ocmData.sic.HelperRoutines.SetReboot && (NETCFG_S_REBOOT == hr))
{
// Request a reboot. Note we don't return the warning as the OCM call
// below handles it. Fall through and return NO_ERROR.
//
g_ocmData.sic.HelperRoutines.SetReboot(
g_ocmData.sic.HelperRoutines.OcManagerContext,
FALSE);
}
else if (FAILED(hr))
{
if (!g_ocmData.fErrorReported)
{
PCWSTR pszSubComponentId = reinterpret_cast<PCWSTR>(pvSubcomponentId);
TraceError("NetOcSetupProcHelper", hr);
if (pszSubComponentId)
{
NETOCDATA * pnocd;
pnocd = PnocdFindComponent(pszSubComponentId);
Assert(pnocd);
if (HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr)
{
ReportErrorHr(hr, UiOcErrorFromHr(hr), g_ocmData.hwnd,
pnocd->strDesc.c_str());
}
}
}
TraceError("NetOcSetupProcHelper", hr);
return DwWin32ErrorFromHr(hr);
}
return NO_ERROR;
}
//+---------------------------------------------------------------------------
//
// Function: HrOnInitComponent
//
// Purpose: Handles the OC_INIT_COMPONENT function message.
//
// Arguments:
// psic [in] Setup data. (see OCManager spec)
//
// Returns: S_OK if success, Win32 error otherwise
//
// Author: danielwe 23 Feb 1998
//
// Notes:
//
HRESULT HrOnInitComponent (PSETUP_INIT_COMPONENT psic)
{
HRESULT hr = S_OK;
if (OCMANAGER_VERSION <= psic->OCManagerVersion)
{
psic->ComponentVersion = OCMANAGER_VERSION;
CopyMemory(&g_ocmData.sic, (LPVOID)psic, sizeof(SETUP_INIT_COMPONENT));
}
else
{
hr = HRESULT_FROM_WIN32(ERROR_CALL_NOT_IMPLEMENTED);
}
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: OnWizardCreated
//
// Purpose: Handles the OC_WIZARD_CREATED function message.
//
// Arguments:
// hwnd [in] HWND of wizard (may not be NULL)
//
// Returns: Nothing.
//
// Author: danielwe 23 Feb 1998
//
// Notes:
//
VOID OnWizardCreated(HWND hwnd)
{
g_ocmData.hwnd = hwnd;
AssertSz(g_ocmData.hwnd, "Parent HWND is NULL!");
}
//+---------------------------------------------------------------------------
//
// Function: HrOnCalcDiskSpace
//
// Purpose: Handles the OC_CALC_DISK_SPACE function message.
//
// Arguments:
// pszSubComponentId [in] Name of component.
// fAdd [in] TRUE if disk space should be added to total
// FALSE if removed from total.
// hdskspc [in] Handle to diskspace struct.
//
// Returns: S_OK if success, Win32 error otherwise
//
// Author: danielwe 23 Feb 1998
//
// Notes:
//
HRESULT HrOnCalcDiskSpace(PCWSTR pszSubComponentId, BOOL fAdd,
HDSKSPC hdskspc)
{
HRESULT hr = S_OK;
DWORD dwErr;
NETOCDATA * pnocd;
pnocd = PnocdFindComponent(pszSubComponentId);
Assert(pnocd);
TraceTag(ttidNetOc, "Calculating disk space for %S...",
pszSubComponentId);
hr = HrEnsureInfFileIsOpen(pszSubComponentId, *pnocd);
if (SUCCEEDED(hr))
{
if (fAdd)
{
dwErr = SetupAddInstallSectionToDiskSpaceList(hdskspc,
pnocd->hinfFile,
NULL,
pszSubComponentId,
0, 0);
}
else
{
dwErr = SetupRemoveInstallSectionFromDiskSpaceList(hdskspc,
pnocd->hinfFile,
NULL,
pszSubComponentId,
0, 0);
}
if (!dwErr)
{
hr = HrFromLastWin32Error();
}
}
TraceError("HrOnCalcDiskSpace", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: DwOnQueryState
//
// Purpose: Handles the OC_QUERY_STATE function message.
//
// Arguments:
// pszSubComponentId [in] Name of component.
// fFinal [in] TRUE if this is the final state query, FALSE
// if not
//
// Returns: SubcompOn - component should be checked "on"
// SubcompUseOcManagerDefault - use whatever OCManage thinks is
// the default
//
// Author: danielwe 23 Feb 1998
//
// Notes:
//
DWORD DwOnQueryState(PCWSTR pszSubComponentId, BOOL fFinal)
{
HRESULT hr = S_OK;
if (pszSubComponentId)
{
NETOCDATA * pnocd;
EINSTALL_TYPE eit;
pnocd = PnocdFindComponent(pszSubComponentId);
if (!pnocd)
{
pnocd = new NETOCDATA;
if(pnocd)
{
AddComponent(pszSubComponentId, pnocd);
}
}
if(pnocd)
{
if (fFinal)
{
if (pnocd->fFailedToInstall)
{
TraceTag(ttidNetOc, "OC_QUERY_STATE: %S failed to install so "
"we are turning it off", pszSubComponentId);
return SubcompOff;
}
}
else
{
hr = HrGetInstallType(pszSubComponentId, *pnocd, &eit);
if (SUCCEEDED(hr))
{
pnocd->eit = eit;
if ((eit == IT_INSTALL) || (eit == IT_UPGRADE))
{
TraceTag(ttidNetOc, "OC_QUERY_STATE: %S is ON",
pszSubComponentId);
return SubcompOn;
}
else if (eit == IT_REMOVE)
{
TraceTag(ttidNetOc, "OC_QUERY_STATE: %S is OFF",
pszSubComponentId);
return SubcompOff;
}
}
}
}
}
TraceTag(ttidNetOc, "OC_QUERY_STATE: %S is using default",
pszSubComponentId);
return SubcompUseOcManagerDefault;
}
//+---------------------------------------------------------------------------
//
// Function: HrEnsureInfFileIsOpen
//
// Purpose: Ensures that the INF file for the given component is open.
//
// Arguments:
// pszSubComponentId [in] Name of component.
// nocd [in, ref] Data associated with component.
//
// Returns: S_OK if success, Win32 error otherwise
//
// Author: danielwe 23 Feb 1998
//
// Notes:
//
HRESULT HrEnsureInfFileIsOpen(PCWSTR pszSubComponentId, NETOCDATA &nocd)
{
HRESULT hr = S_OK;
tstring strInf;
if (!nocd.hinfFile)
{
// Get component INF file name
hr = HrSetupGetFirstString(g_ocmData.sic.ComponentInfHandle,
pszSubComponentId, c_szInfRef,
&strInf);
if (SUCCEEDED(hr))
{
TraceTag(ttidNetOc, "Opening INF file %S...", strInf.c_str());
hr = HrSetupOpenInfFile(strInf.c_str(), NULL,
INF_STYLE_WIN4, NULL, &nocd.hinfFile);
if (SUCCEEDED(hr))
{
// Append in the layout.inf file
(VOID) SetupOpenAppendInfFile(NULL, nocd.hinfFile, NULL);
}
}
// This is a good time to cache away the component description as
// well.
(VOID) HrSetupGetFirstString(g_ocmData.sic.ComponentInfHandle,
pszSubComponentId, c_szDesc,
&nocd.strDesc);
}
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrOnPreCommitFileQueue
//
// Purpose: Handles the OC_ABOUT_TO_COMMIT_QUEUE function message.
//
// Arguments:
// pszSubComponentId [in] Name of component.
//
// Returns: S_OK if success, Win32 error otherwise
//
// Author: danielwe 9 Dec 1998
//
// Notes:
//
HRESULT HrOnPreCommitFileQueue(PCWSTR pszSubComponentId)
{
HRESULT hr = S_OK;
NETOCDATA * pnocd;
if (pszSubComponentId)
{
EINSTALL_TYPE eit;
pnocd = PnocdFindComponent(pszSubComponentId);
hr = HrGetInstallType(pszSubComponentId, *pnocd, &eit);
if (SUCCEEDED(hr))
{
pnocd->eit = eit;
if (pnocd->eit == IT_REMOVE)
{
// Always use main install section
hr = HrStartOrStopAnyServices(pnocd->hinfFile,
pszSubComponentId, FALSE);
if (FAILED(hr))
{
// Don't report errors for non-existent services
if (HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) != hr)
{
// Don't bail removal if services couldn't be stopped.
if (!g_ocmData.fErrorReported)
{
// Report an error and continue the removal.
ReportErrorHr(hr,
IDS_OC_STOP_SERVICE_FAILURE,
g_ocmData.hwnd,
pnocd->strDesc.c_str());
}
}
hr = S_OK;
}
// We need to unregister DLLs before they get commited to the
// queue, otherwise we try to unregister a non-existent DLL.
if (SUCCEEDED(hr))
{
tstring strUninstall;
// Get the name of the uninstall section first
hr = HrSetupGetFirstString(pnocd->hinfFile,
pszSubComponentId,
c_szUninstall, &strUninstall);
if (SUCCEEDED(hr))
{
PCWSTR pszInstallSection;
pszInstallSection = strUninstall.c_str();
// Run the INF but only call the unregister function
//
hr = HrSetupInstallFromInfSection(g_ocmData.hwnd,
pnocd->hinfFile,
pszInstallSection,
SPINST_UNREGSVR,
NULL, NULL, 0, NULL,
NULL, NULL, NULL);
}
else
{
// Uninstall may not be present
hr = S_OK;
}
}
}
}
}
TraceError("HrOnPreCommitFileQueue", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrOnQueueFileOps
//
// Purpose: Handles the OC_QUEUE_FILE_OPS function message.
//
// Arguments:
// pszSubComponentId [in] Name of component.
// hfq [in] Handle to file queue struct.
//
// Returns: S_OK if success, Win32 error otherwise
//
// Author: danielwe 23 Feb 1998
//
// Notes:
//
HRESULT HrOnQueueFileOps(PCWSTR pszSubComponentId, HSPFILEQ hfq)
{
HRESULT hr = S_OK;
NETOCDATA * pnocd;
if (pszSubComponentId)
{
EINSTALL_TYPE eit;
pnocd = PnocdFindComponent(pszSubComponentId);
hr = HrGetInstallType(pszSubComponentId, *pnocd, &eit);
if (SUCCEEDED(hr))
{
pnocd->eit = eit;
if ((pnocd->eit == IT_INSTALL) || (pnocd->eit == IT_UPGRADE) ||
(pnocd->eit == IT_REMOVE))
{
BOOL fSuccess = TRUE;
PCWSTR pszInstallSection;
tstring strUninstall;
AssertSz(hfq, "No file queue?");
hr = HrEnsureInfFileIsOpen(pszSubComponentId, *pnocd);
if (SUCCEEDED(hr))
{
if (pnocd->eit == IT_REMOVE)
{
// Get the name of the uninstall section first
hr = HrSetupGetFirstString(pnocd->hinfFile,
pszSubComponentId,
c_szUninstall,
&strUninstall);
if (SUCCEEDED(hr))
{
pszInstallSection = strUninstall.c_str();
}
else
{
if (hr == HRESULT_FROM_SETUPAPI(ERROR_LINE_NOT_FOUND))
{
// Uninstall section is not required.
hr = S_OK;
fSuccess = FALSE;
}
}
}
else
{
pszInstallSection = pszSubComponentId;
}
}
if (SUCCEEDED(hr) && fSuccess)
{
hr = HrCallExternalProc(pnocd, NETOCM_QUEUE_FILES,
(WPARAM)hfq, 0);
}
if (SUCCEEDED(hr))
{
TraceTag(ttidNetOc, "Queueing files for %S...",
pszSubComponentId);
hr = HrSetupInstallFilesFromInfSection(pnocd->hinfFile,
NULL, hfq,
pszInstallSection,
NULL, 0);
}
}
}
}
TraceError("HrOnQueueFileOps", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrOnCompleteInstallation
//
// Purpose: Handles the OC_COMPLETE_INSTALLATION function message.
//
// Arguments:
// pszComponentId [in] Top-level component name (will always be
// "NetOC" or NULL.
// pszSubComponentId [in] Name of component.
//
// Returns: S_OK if success, Win32 error otherwise
//
// Author: danielwe 23 Feb 1998
// omiller 28 March 2000 Added code to move the progress
// bar one tick for every component
// installed or removed.
//
// Notes:
//
HRESULT HrOnCompleteInstallation(PCWSTR pszComponentId,
PCWSTR pszSubComponentId)
{
HRESULT hr = S_OK;
// Make sure they're different. If not, it's the top level item and
// we don't want to do anything
if (pszSubComponentId && lstrcmpiW(pszSubComponentId, pszComponentId))
{
NETOCDATA * pnocd;
pnocd = PnocdFindComponent(pszSubComponentId);
Assert(pnocd);
pnocd->fCleanup = FALSE;
if (pnocd->eit == IT_INSTALL || pnocd->eit == IT_REMOVE ||
pnocd->eit == IT_UPGRADE)
{
pnocd->pszSection = pszSubComponentId;
// Get component description
#if DBG
if (pnocd->eit == IT_INSTALL)
{
TraceTag(ttidNetOc, "Installing network OC %S...",
pszSubComponentId);
}
else if (pnocd->eit == IT_UPGRADE)
{
TraceTag(ttidNetOc, "Upgrading network OC %S...",
pszSubComponentId);
}
else if (pnocd->eit == IT_REMOVE)
{
TraceTag(ttidNetOc, "Removing network OC %S...",
pszSubComponentId);
}
#endif
hr = HrDoOCInstallOrUninstall(pnocd);
if (FAILED(hr) && pnocd->eit == IT_INSTALL)
{
// A failure during install means we have to clean up by doing
// an uninstall now. Report the appropriate error and do the
// remove. Note - Don't report the error if it's ERROR_CANCELLED,
// because they KNOW that they cancelled, and it's not really
// an error.
//
if (HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr)
{
// Don't report the error a second time if the component
// has already put up error UI (and set this flag)
//
if (!g_ocmData.fErrorReported)
{
ReportErrorHr(hr, UiOcErrorFromHr(hr),
g_ocmData.hwnd,
pnocd->strDesc.c_str());
}
}
g_ocmData.fErrorReported = TRUE;
// Now we're removing
pnocd->eit = IT_REMOVE;
pnocd->fCleanup = TRUE;
pnocd->fFailedToInstall = TRUE;
// eat the error. Haven't we troubled them enough? :(
(VOID) HrDoOCInstallOrUninstall(pnocd);
}
else
{
// Every time a component is installed,upgraded or removed, the progress
// bar is advanced by one tick. For every component that is being
// installed/removed/upgraded the OC manager asked netoc for how many ticks
// that component counts (OC_QUERY_STEP_COUNT). From this information
// the OC manger knows the relationship between tick and progress bar
// advancement.
g_ocmData.sic.HelperRoutines.TickGauge(g_ocmData.sic.HelperRoutines.OcManagerContext);
}
}
}
TraceError("HrOnCompleteInstallation", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: DwOnQueryStepCount
//
// Purpose: Handles the OC_QUERY_STEP_COUNT message.
// The OC manager is asking us how many ticks a component is worth.
// The number of ticks determines the distance the progress bar gets
// moved. For netoc all components installed/removed are one tick and
// all components that are unchanged are 0 ticks.
//
// Arguments:
// pszSubComponentId [in] Name of component.
//
// Returns: Number of ticks for progress bar to move
//
// Author: omiller 28 March 2000
//
//
DWORD DwOnQueryStepCount(PCWSTR pvSubcomponentId)
{
NETOCDATA * pnocd;
// Get the component
pnocd = PnocdFindComponent(reinterpret_cast<PCWSTR>(pvSubcomponentId));
if( pnocd )
{
// Check if the status of the component has changed.
if (pnocd->eit == IT_INSTALL || pnocd->eit == IT_REMOVE ||
pnocd->eit == IT_UPGRADE)
{
// Status of component has changed. For this component the OC manager
// will move the status bar by one tick.
return 1;
}
}
// The component has not changed. The progress bar will not move for this component.
return 0;
}
//+---------------------------------------------------------------------------
//
// Function: HrOnQueryChangeSelState
//
// Purpose: Handles the OC_QUERY_CHANGE_SEL_STATE function message.
// Enables and disables the next button. If no changes has
// been made to the selections the next button is disabled.
//
// Arguments:
// pszSubComponentId [in] Name of component.
// fSelected [in] TRUE if component was checked "on", FALSE if
// checked "off"
// uiFlags [in] Flags defined in ocmgr.doc
//
// Returns: S_OK if success, Win32 error otherwise
//
// Author: danielwe 23 Feb 1998
//
// Notes:
//
HRESULT HrOnQueryChangeSelState(PCWSTR pszSubComponentId, BOOL fSelected,
UINT uiFlags)
{
HRESULT hr = S_OK;
static int nItemsChanged=0;
NETOCDATA * pnocd;
if (fSelected && pszSubComponentId)
{
pnocd = PnocdFindComponent(pszSubComponentId);
if (pnocd)
{
// "NetOc" may be a subcomponent and we don't want to call this
// for it.
hr = HrCallExternalProc(pnocd, NETOCM_QUERY_CHANGE_SEL_STATE,
(WPARAM)(!!(uiFlags & OCQ_ACTUAL_SELECTION)),
0);
}
}
TraceError("HrOnQueryChangeSelState", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: FOnQuerySkipPage
//
// Purpose: Handles the OC_QUERY_SKIP_PAGE function message.
//
// Arguments:
// ocmPage [in] Which page we are asked to possibly skip.
//
// Returns: TRUE if component list page should be skipped, FALSE if not.
//
// Author: danielwe 23 Feb 1998
//
// Notes:
//
BOOL FOnQuerySkipPage(OcManagerPage ocmPage)
{
BOOL fUnattended;
BOOL fGuiSetup;
BOOL fWorkstation;
fUnattended = !!(g_ocmData.sic.SetupData.OperationFlags & SETUPOP_BATCH);
fGuiSetup = !(g_ocmData.sic.SetupData.OperationFlags & SETUPOP_STANDALONE);
fWorkstation = g_ocmData.sic.SetupData.ProductType == PRODUCT_WORKSTATION;
if ((fUnattended || fWorkstation) && fGuiSetup)
{
// We're in GUI mode setup and... we're unattended -OR- this is
// a workstation install
if (ocmPage == OcPageComponentHierarchy)
{
TraceTag(ttidNetOc, "NETOC: Skipping component list page "
"during GUI mode setup...");
TraceTag(ttidNetOc, "fUnattended = %s, fGuiSetup = %s, "
"fWorkstation = %s",
fUnattended ? "yes" : "no",
fGuiSetup ? "yes" : "no",
fWorkstation ? "yes" : "no");
// Make sure we never show the component list page during setup
return TRUE;
}
}
TraceTag(ttidNetOc, "Using component list page.");
TraceTag(ttidNetOc, "fUnattended = %s, fGuiSetup = %s, "
"fWorkstation = %s",
fUnattended ? "yes" : "no",
fGuiSetup ? "yes" : "no",
fWorkstation ? "yes" : "no");
return FALSE;
}
//+---------------------------------------------------------------------------
//
// Function: OnCleanup
//
// Purpose: Handles the OC_CLEANUP function message.
//
// Arguments:
// (none)
//
// Returns: Nothing
//
// Author: danielwe 23 Feb 1998
//
// Notes:
//
VOID OnCleanup()
{
TraceTag(ttidNetOc, "Cleaning up");
if (g_ocmData.hinfAnswerFile)
{
SetupCloseInfFile(g_ocmData.hinfAnswerFile);
TraceTag(ttidNetOc, "Closed answer file");
}
DeleteAllComponents();
}
//+---------------------------------------------------------------------------
//
// Function: HrGetSelectionState
//
// Purpose:
//
// Arguments:
// pszSubComponentId [in] Name of subcomponent
// uStateType [in] In OCManager doc.
//
// Returns: S_OK if component is selected, S_FALSE if not, or Win32 error
// otheriwse
//
// Author: danielwe 17 Dec 1997
//
// Notes:
//
HRESULT HrGetSelectionState(PCWSTR pszSubComponentId, UINT uStateType)
{
HRESULT hr = S_OK;
BOOL fInstall;
fInstall = g_ocmData.sic.HelperRoutines.
QuerySelectionState(g_ocmData.sic.HelperRoutines.OcManagerContext,
pszSubComponentId, uStateType);
if (!fInstall)
{
// Still not sure of the state
hr = HrFromLastWin32Error();
if (SUCCEEDED(hr))
{
// Ok now we know
hr = S_FALSE;
}
}
else
{
hr = S_OK;
}
TraceError("HrGetSelectionState", (S_FALSE == hr) ? S_OK : hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrGetInstallType
//
// Purpose: Determines whether the given component is being installed or
// removed and stores the result in the given structure.
//
// Arguments:
// pszSubComponentId [in] Component being queried
// nocd [in, ref] Net OC Data.
// peit [out] Returns the install type
//
// Returns: S_OK if success, Win32 error otherwise
//
// Author: danielwe 16 Dec 1997
//
// Notes: If the function fails, the eit member is unreliable
//
HRESULT HrGetInstallType(PCWSTR pszSubComponentId, NETOCDATA &nocd,
EINSTALL_TYPE *peit)
{
HRESULT hr = S_OK;
Assert(peit);
Assert(pszSubComponentId);
*peit = IT_UNKNOWN;
if (g_ocmData.sic.SetupData.OperationFlags & SETUPOP_BATCH)
{
// In batch mode (upgrade or unattended install), install flag is
// determined from answer file not from selection state.
// assume no change
*peit = IT_NO_CHANGE;
if (!g_ocmData.hinfAnswerFile)
{
// Open the answer file
hr = HrSetupOpenInfFile(g_ocmData.sic.SetupData.UnattendFile, NULL,
INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL,
&g_ocmData.hinfAnswerFile);
}
if (SUCCEEDED(hr))
{
DWORD dwValue = 0;
// First query for a special value called "NoDepends" which, if
// present, means that the DependOnComponents line will be IGNORED
// for ALL network optional components for this install. This is
// because NetCfg may invoke the OC Manager to install an optional
// component and if that component has DependOnComponents, it will
// turn around and try to instantiate another INetCfg and that
// will fail because one instance is already running. This case
// is rare, though.
//
hr = HrSetupGetFirstDword(g_ocmData.hinfAnswerFile,
c_szOcMainSection, c_szNoDepends,
&dwValue);
if (SUCCEEDED(hr) && dwValue)
{
TraceTag(ttidNetOc, "Found the special 'NoDepends'"
" keyword in the answer file. DependOnComponents "
"will be ignored from now on");
g_ocmData.fNoDepends = TRUE;
}
else
{
TraceTag(ttidNetOc, "Didn't find the special 'NoDepends'"
" keyword in the answer file");
hr = S_OK;
}
hr = HrSetupGetFirstDword(g_ocmData.hinfAnswerFile,
c_szOcMainSection, pszSubComponentId,
&dwValue);
if (SUCCEEDED(hr))
{
// This component was installed before, so we should
// return that this component should be checked on
if (dwValue)
{
TraceTag(ttidNetOc, "Optional component %S was "
"previously installed or is being added thru"
" unattended install.", pszSubComponentId);
if (g_ocmData.sic.SetupData.OperationFlags & SETUPOP_NTUPGRADE)
{
// If we're upgrading NT, then this optional component
// does exist but it needs to be upgraded
*peit = IT_UPGRADE;
}
else
{
// Otherwise (even if Win3.1 or Win95 upgrade) it's like
// we're fresh installing the optional component
*peit = IT_INSTALL;
}
}
else
{
// Answer file contains something like WINS=0
hr = HrGetSelectionState(pszSubComponentId,
OCSELSTATETYPE_ORIGINAL);
if (S_OK == hr)
{
// Only set state to remove if the component was
// previously installed.
//
*peit = IT_REMOVE;
}
}
}
}
hr = S_OK;
// If the answer file was opened successfully and if the
// a section was found for the pszSubComponentId, *peit
// will be either IT_INSTALL, IT_UPGRADE or IT_REMOVE.
// Nothing needs to be done for any of these *peit values.
// However, if the answerfile could not be opened or if
// no section existed in the answer file for the pszSubComponentId
// *peit will have the value IT_NO_CHANGE. For this scenario,
// if the corresponding subComponent is currently installed,
// we should upgrade it. The following if addresses this scenario.
if (*peit == IT_NO_CHANGE)
{
// Still not going to install, because this is an upgrade
hr = HrGetSelectionState(pszSubComponentId,
OCSELSTATETYPE_ORIGINAL);
if (S_OK == hr)
{
// If originally selected and not in answer file, this is an
// upgrade of this component
*peit = IT_UPGRADE;
}
}
}
else // This is standalone (post-setup) mode
{
hr = HrGetSelectionState(pszSubComponentId, OCSELSTATETYPE_ORIGINAL);
if (SUCCEEDED(hr))
{
HRESULT hrT;
hrT = HrGetSelectionState(pszSubComponentId,
OCSELSTATETYPE_CURRENT);
if (SUCCEEDED(hrT))
{
if (hrT != hr)
{
// wasn't originally installed so...
*peit = (hrT == S_OK) ? IT_INSTALL : IT_REMOVE;
}
else
{
// was originally checked
*peit = IT_NO_CHANGE;
}
}
else
{
hr = hrT;
}
}
}
AssertSz(FImplies(SUCCEEDED(hr), *peit != IT_UNKNOWN), "Succeeded "
"but we never found out the install type!");
if (SUCCEEDED(hr))
{
hr = S_OK;
#if DBG
const CHAR *szInstallType;
switch (*peit)
{
case IT_NO_CHANGE:
szInstallType = "no change";
break;
case IT_INSTALL:
szInstallType = "install";
break;
case IT_UPGRADE:
szInstallType = "upgrade";
break;
case IT_REMOVE:
szInstallType = "remove";
break;
default:
AssertSz(FALSE, "Unknown install type!");
break;
}
TraceTag(ttidNetOc, "Install type of %S is %s.", pszSubComponentId,
szInstallType);
#endif
}
TraceError("HrGetInstallType", hr);
return hr;
}
#if DBG
PCWSTR SzFromOcUmsg(UINT uMsg)
{
switch (uMsg)
{
case NETOCM_PRE_INF:
return L"NETOCM_PRE_INF";
case NETOCM_POST_INSTALL:
return L"NETOCM_POST_INSTALL";
case NETOCM_QUERY_CHANGE_SEL_STATE:
return L"NETOCM_QUERY_CHANGE_SEL_STATE";
case NETOCM_QUEUE_FILES:
return L"NETOCM_QUEUE_FILES";
default:
return L"**unknown**";
}
}
#else
#define SzFromOcUmsg(x) (VOID)0
#endif
//+---------------------------------------------------------------------------
//
// Function: HrCallExternalProc
//
// Purpose: Calls a component's external function as defined by
// the table at the top of this file. This enables a component
// to perform additional installation tasks that are not common
// to other components.
//
// Arguments:
// pnocd [in] Pointer to Net OC Data
//
// Returns: S_OK if successful, Win32 error code otherwise.
//
// Author: danielwe 5 May 1997
//
// Notes:
//
HRESULT HrCallExternalProc(PNETOCDATA pnocd, UINT uMsg, WPARAM wParam,
LPARAM lParam)
{
HRESULT hr = S_OK;
INT iaocep;
AssertSz(pnocd, "Bad pnocd in HrCallExternalProc");
for (iaocep = 0; iaocep < c_cocepMap; iaocep++)
{
if (!lstrcmpiW(c_aocepMap[iaocep].pszComponentName,
pnocd->pszComponentId))
{
TraceTag(ttidNetOc, "Calling external procedure for %S. uMsg = %S"
" wParam = %08X,"
" lParam = %08X", c_aocepMap[iaocep].pszComponentName,
SzFromOcUmsg(uMsg), wParam, lParam);
// This component has an external proc. Call it now.
hr = c_aocepMap[iaocep].pfnHrOcExtProc(pnocd, uMsg,
wParam, lParam);
// Don't try to call any other functions
break;
}
}
TraceError("HrCallExternalProc", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrInstallOrRemoveNetCfgComponent
//
// Purpose: Utility function for use by optional components that wish to
// install a NetCfg component from within their own install.
//
// Arguments:
// pnocd [in] Pointer to NETOC data
// pszComponentId [in] Component ID of NetCfg component to install.
// This can be found in the netinfid.cpp file.
// pszManufacturer [in] Manufacturer name of component doing the
// installing (*this* component). Should always
// be "Microsoft".
// pszProduct [in] Short name of product for this component.
// Should be something like "MacSrv".
// pszDisplayName [in] Display name of this product. Should be
// something like "Services For Macintosh".
// rguid [in] class GUID of the component being installed
//
// Returns: S_OK if successful, Win32 error code otherwise.
//
// Author: danielwe 6 May 1997
//
// Notes:
//
HRESULT HrInstallOrRemoveNetCfgComponent(PNETOCDATA pnocd,
PCWSTR pszComponentId,
PCWSTR pszManufacturer,
PCWSTR pszProduct,
PCWSTR pszDisplayName,
const GUID& rguid)
{
HRESULT hr = S_OK;
INetCfg * pnc;
NETWORK_INSTALL_PARAMS nip = {0};
BOOL fReboot = FALSE;
nip.dwSetupFlags = FInSystemSetup() ? NSF_PRIMARYINSTALL :
NSF_POSTSYSINSTALL;
hr = HrOcGetINetCfg(pnocd, TRUE, &pnc);
if (SUCCEEDED(hr))
{
if (pnocd->eit == IT_INSTALL || pnocd->eit == IT_UPGRADE)
{
if (*pszComponentId == L'*')
{
// Advance past the *
pszComponentId++;
// Install OBO user instead
TraceTag(ttidNetOc, "Installing %S on behalf of the user",
pszComponentId);
hr = HrInstallComponentOboUser(pnc, &nip, rguid,
pszComponentId, NULL);
}
else
{
TraceTag(ttidNetOc, "Installing %S on behalf of %S",
pszComponentId, pnocd->pszSection);
hr = HrInstallComponentOboSoftware(pnc, &nip,
rguid,
pszComponentId,
pszManufacturer,
pszProduct,
pszDisplayName,
NULL);
}
}
else
{
AssertSz(pnocd->eit == IT_REMOVE, "Invalid install action!");
TraceTag(ttidNetOc, "Removing %S on behalf of %S",
pszComponentId, pnocd->pszSection);
hr = HrRemoveComponentOboSoftware(pnc,
rguid,
pszComponentId,
pszManufacturer,
pszProduct,
pszDisplayName);
if (NETCFG_S_REBOOT == hr)
{
// Save off the fact that we need to reboot
fReboot = TRUE;
}
// Don't care about the return value here. If we can't remove a
// dependent component, we can't do anything about it so we should
// still continue the removal of the OC.
//
else if (FAILED(hr))
{
TraceTag(ttidError, "Failed to remove %S on behalf of %S!! "
"Error is 0x%08X",
pszComponentId, pnocd->pszSection, hr);
hr = S_OK;
}
}
if (SUCCEEDED(hr))
{
hr = pnc->Apply();
}
(VOID) HrUninitializeAndReleaseINetCfg(TRUE, pnc, TRUE);
}
if (SUCCEEDED(hr) && fReboot)
{
// If all went well and we needed to reboot, set hr back.
hr = NETCFG_S_REBOOT;
}
TraceError("HrInstallOrRemoveNetCfgComponent", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrInstallOrRemoveServices
//
// Purpose: Given an install section, installs (or removes) NT services
// from the section.
//
// Arguments:
// hinf [in] Handle to INF file.
// pszSectionName [in] Name of section to use.
//
// Returns: S_OK if successful, WIN32 HRESULT if not.
//
// Author: danielwe 23 Apr 1997
//
// Notes:
//
HRESULT HrInstallOrRemoveServices(HINF hinf, PCWSTR pszSectionName)
{
static const WCHAR c_szDotServices[] = L"."INFSTR_SUBKEY_SERVICES;
HRESULT hr = S_OK;
PWSTR pszServicesSection;
const DWORD c_cchServices = celems(c_szDotServices);
DWORD cchName;
// Look for <szSectionName>.Services to install any NT
// services if they exist.
cchName = c_cchServices + lstrlenW(pszSectionName);
pszServicesSection = new WCHAR [cchName];
if(pszServicesSection)
{
lstrcpyW(pszServicesSection, pszSectionName);
lstrcatW(pszServicesSection, c_szDotServices);
if (!SetupInstallServicesFromInfSection(hinf, pszServicesSection, 0))
{
hr = HrFromLastWin32Error();
if (hr == HRESULT_FROM_SETUPAPI(ERROR_SECTION_NOT_FOUND))
{
// No problem if section was not found
hr = S_OK;
}
}
delete [] pszServicesSection;
}
else
{
hr = E_OUTOFMEMORY;
}
TraceError("HrInstallOrRemoveServices", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrHandleOCExtensions
//
// Purpose: Handles support for all optional component extensions to the
// INF file format.
//
// Arguments:
// hinfFile [in] handle to INF to process
// pszInstallSection [in] Install section to process
//
// Returns: S_OK if success, setup API HRESULT otherwise
//
// Author: danielwe 28 Apr 1997
//
// Notes:
//
HRESULT HrHandleOCExtensions(HINF hinfFile, PCWSTR pszInstallSection)
{
HRESULT hr = S_OK;
// There's now common code to do this, so simply make a call to that code.
//
hr = HrProcessAllINFExtensions(hinfFile, pszInstallSection);
TraceError("HrHandleOCExtensions", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrInstallOrRemoveDependOnComponents
//
// Purpose: Handles installation or removal of any NetCfg components that
// the optional component being installed is dependent upon.
//
// Arguments:
// pnocd [in] Pointer to NETOC data
// hinf [in] Handle to INF file to process.
// pszInstallSection [in] Section name to install from.
// pszDisplayName [in] Display name of component being installed.
//
// Returns: S_OK if success, setup API HRESULT otherwise
//
// Author: danielwe 17 Jun 1997
//
// Notes:
//
HRESULT HrInstallOrRemoveDependOnComponents(PNETOCDATA pnocd,
HINF hinf,
PCWSTR pszInstallSection,
PCWSTR pszDisplayName)
{
HRESULT hr = S_OK;
PWSTR mszDepends;
tstring strManufacturer;
PCWSTR pszManufacturer;
Assert(pnocd);
hr = HrSetupGetFirstString(hinf, c_szVersionSection, c_szProvider,
&strManufacturer);
if (S_OK == hr)
{
pszManufacturer = strManufacturer.c_str();
}
else
{
// No provider found, use default
hr = S_OK;
pszManufacturer = c_szDefManu;
}
hr = HrSetupGetFirstMultiSzFieldWithAlloc(hinf, pszInstallSection,
c_szDependOnComp,
&mszDepends);
if (S_OK == hr)
{
PCWSTR pszComponent;
pszComponent = mszDepends;
while (SUCCEEDED(hr) && *pszComponent)
{
const GUID * pguidClass;
PCWSTR pszComponentActual = pszComponent;
if (*pszComponent == L'*')
{
pszComponentActual = pszComponent + 1;
}
if (FClassGuidFromComponentId(pszComponentActual, &pguidClass))
{
hr = HrInstallOrRemoveNetCfgComponent(pnocd,
pszComponent,
pszManufacturer,
pszInstallSection,
pszDisplayName,
*pguidClass);
}
#ifdef DBG
else
{
TraceTag(ttidNetOc, "Error in INF, Component %S not found!",
pszComponent);
}
#endif
pszComponent += lstrlenW(pszComponent) + 1;
}
delete mszDepends;
}
else if (hr == HRESULT_FROM_SETUPAPI(ERROR_LINE_NOT_FOUND))
{
// Section is not required.
hr = S_OK;
}
TraceError("HrInstallOrRemoveDependOnComponents", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrRunInfSection
//
// Purpose: Runs the given INF section, but doesn't copy files
//
// Arguments:
// hinf [in] Handle to INF to run
// pnocd [in] NetOC Data
// pszInstallSection [in] Install section to run
// dwFlags [in] Install flags (SPINST_*)
//
// Returns: S_OK if success, SetupAPI or Win32 error otherwise
//
// Author: danielwe 16 Dec 1997
//
// Notes:
//
HRESULT HrRunInfSection(HINF hinf, PNETOCDATA pnocd,
PCWSTR pszInstallSection, DWORD dwFlags)
{
HRESULT hr;
// Now we run all sections but CopyFiles and UnregisterDlls because we
// did that earlier
//
hr = HrSetupInstallFromInfSection(g_ocmData.hwnd, hinf,
pszInstallSection,
dwFlags & ~SPINST_FILES & ~SPINST_UNREGSVR,
NULL, NULL, 0, NULL,
NULL, NULL, NULL);
TraceError("HrRunInfSection", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrStartOrStopAnyServices
//
// Purpose: Starts or stops any services the INF has requested via the
// Services value in the main install section.
//
// Arguments:
// hinf [in] handle to INF to process
// pszSection [in] Install section to process
// fStart [in] TRUE to start, FALSE to stop.
//
// Returns: S_OK or Win32 error code.
//
// Author: danielwe 17 Jun 1997
//
// Notes: Services are stopped in the same order they are started.
//
HRESULT HrStartOrStopAnyServices(HINF hinf, PCWSTR pszSection, BOOL fStart)
{
HRESULT hr;
PWSTR mszServices;
hr = HrSetupGetFirstMultiSzFieldWithAlloc(hinf, pszSection,
c_szServices, &mszServices);
if (SUCCEEDED(hr))
{
// Build an array of pointers to strings that point at the
// strings of the multi-sz. This is needed because the API to
// stop and start services takes an array of pointers to strings.
//
UINT cServices;
PCWSTR* apszServices;
hr = HrCreateArrayOfStringPointersIntoMultiSz(
mszServices,
&cServices,
&apszServices);
if (SUCCEEDED(hr))
{
CServiceManager scm;
if (fStart)
{
hr = scm.HrStartServicesAndWait(cServices, apszServices);
}
else
{
hr = scm.HrStopServicesAndWait(cServices, apszServices);
}
MemFree (apszServices);
}
delete mszServices;
}
else if (hr == HRESULT_FROM_SETUPAPI(ERROR_LINE_NOT_FOUND))
{
// this is a totally optional thing
hr = S_OK;
}
TraceError("HrStartOrStopAnyServices", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrDoActualInstallOrUninstall
//
// Purpose: Handles main portion of install or uninstall for an optional
// network component.
//
// Arguments:
// hinf [in] handle to INF to process
// pnocd [in] Pointer to NETOC data (hwnd, poc)
// pszInstallSection [in] Install section to process
//
// Returns: S_OK if success, setup API HRESULT otherwise
//
// Author: danielwe 17 Jun 1997
//
// Notes:
//
HRESULT HrDoActualInstallOrUninstall(HINF hinf,
PNETOCDATA pnocd,
PCWSTR pszInstallSection)
{
HRESULT hr = S_OK;
BOOL fReboot = FALSE;
AssertSz(pszInstallSection, "Install section is NULL!");
AssertSz(pnocd, "Bad pnocd in HrDoActualInstallOrUninstall");
//AssertSz(g_ocmData.hwnd, "Bad g_ocmData.hwnd in HrDoActualInstallOrUninstall");
if (pnocd->eit == IT_REMOVE)
{
hr = HrCallExternalProc(pnocd, NETOCM_PRE_INF, 0, 0);
if (SUCCEEDED(hr))
{
// Now process the component's INF file
//
TraceTag(ttidNetOc, "Running INF section %S", pszInstallSection);
hr = HrRunInfSection(hinf, pnocd, pszInstallSection, SPINST_ALL);
}
}
else
{
hr = HrCallExternalProc(pnocd, NETOCM_PRE_INF, 0, 0);
if (SUCCEEDED(hr))
{
// Process the component's INF file
//
TraceTag(ttidNetOc, "Running INF section %S", pszInstallSection);
hr = HrRunInfSection(hinf, pnocd, pszInstallSection,
SPINST_ALL & ~SPINST_REGSVR);
}
}
if (SUCCEEDED(hr))
{
// Must install or remove services first
TraceTag(ttidNetOc, "Running HrInstallOrRemoveServices for %S",
pszInstallSection);
hr = HrInstallOrRemoveServices(hinf, pszInstallSection);
if (SUCCEEDED(hr))
{
// Bug #383239: Wait till services are installed before
// running the RegisterDlls section
//
hr = HrRunInfSection(hinf, pnocd, pszInstallSection,
SPINST_REGSVR);
}
if (SUCCEEDED(hr))
{
TraceTag(ttidNetOc, "Running HrHandleOCExtensions for %S",
pszInstallSection);
hr = HrHandleOCExtensions(hinf, pszInstallSection);
if (SUCCEEDED(hr))
{
if (!g_ocmData.fNoDepends)
{
// Now install or remove any NetCfg components that this
// component requires
TraceTag(ttidNetOc, "Running "
"HrInstallOrRemoveDependOnComponents for %S",
pnocd->pszSection);
hr = HrInstallOrRemoveDependOnComponents(pnocd,
hinf,
pnocd->pszSection,
pnocd->strDesc.c_str());
if (NETCFG_S_REBOOT == hr)
{
fReboot = TRUE;
}
}
else
{
AssertSz(g_ocmData.sic.SetupData.OperationFlags &
SETUPOP_BATCH, "How can NoDepends be set??");
TraceTag(ttidNetOc, "NOT Running "
"HrInstallOrRemoveDependOnComponents for %S "
"because NoDepends was set in the answer file.",
pnocd->pszSection);
}
if (SUCCEEDED(hr))
{
// Now call any external installation support...
hr = HrCallExternalProc(pnocd, NETOCM_POST_INSTALL,
0, 0);
if (SUCCEEDED(hr))
{
if (pnocd->eit == IT_INSTALL && !FInSystemSetup())
{
// ... and finally, start any services they've
// requested
hr = HrStartOrStopAnyServices(hinf,
pszInstallSection, TRUE);
{
if (FAILED(hr))
{
UINT ids = IDS_OC_START_SERVICE_FAILURE;
if (HRESULT_FROM_WIN32(ERROR_TIMEOUT) == hr)
{
ids = IDS_OC_START_TOOK_TOO_LONG;
}
// Don't bail installation if service
// couldn't be started. Report an error
// and continue the install.
ReportErrorHr(hr, ids, g_ocmData.hwnd,
pnocd->strDesc.c_str());
hr = S_OK;
}
}
}
}
}
}
}
}
if ((S_OK == hr) && (fReboot))
{
hr = NETCFG_S_REBOOT;
}
TraceError("HrDoActualInstallOrUninstall", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrOCInstallOrUninstallFromINF
//
// Purpose: Handles installation of an Optional Component from its INF
// file.
//
// Arguments:
// pnocd [in] Pointer to NETOC data.
//
// Returns: S_OK if success, setup API HRESULT otherwise
//
// Author: danielwe 6 May 1997
//
// Notes:
//
HRESULT HrOCInstallOrUninstallFromINF(PNETOCDATA pnocd)
{
HRESULT hr = S_OK;
tstring strUninstall;
PCWSTR pszInstallSection = NULL;
BOOL fSuccess = TRUE;
Assert(pnocd);
if (pnocd->eit == IT_REMOVE)
{
// Get the name of the uninstall section first
hr = HrSetupGetFirstString(pnocd->hinfFile, pnocd->pszSection,
c_szUninstall, &strUninstall);
if (SUCCEEDED(hr))
{
pszInstallSection = strUninstall.c_str();
}
else
{
if (hr == HRESULT_FROM_SETUPAPI(ERROR_LINE_NOT_FOUND))
{
// Uninstall section is not required.
hr = S_OK;
fSuccess = FALSE;
}
}
}
else
{
pszInstallSection = pnocd->pszSection;
}
if (fSuccess)
{
hr = HrDoActualInstallOrUninstall(pnocd->hinfFile,
pnocd,
pszInstallSection);
}
TraceError("HrOCInstallOrUninstallFromINF", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrDoOCInstallOrUninstall
//
// Purpose: Installs or removes an optional networking component.
//
// Arguments:
// pnocd [in] Pointer to NETOC data
//
// Returns: S_OK for success, SetupAPI HRESULT error code otherwise.
//
// Author: danielwe 6 May 1997
//
// Notes:
//
HRESULT HrDoOCInstallOrUninstall(PNETOCDATA pnocd)
{
HRESULT hr = S_OK;
hr = HrOCInstallOrUninstallFromINF(pnocd);
TraceError("HrDoOCInstallOrUninstall", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: UiOcErrorFromHr
//
// Purpose: Maps a Win32 error code into an understandable error string.
//
// Arguments:
// hr [in] HRESULT to convert
//
// Returns: The resource ID of the string.
//
// Author: danielwe 9 Feb 1998
//
// Notes:
//
UINT UiOcErrorFromHr(HRESULT hr)
{
UINT uid;
AssertSz(FAILED(hr), "Don't call UiOcErrorFromHr if Hr didn't fail!");
switch (hr)
{
case HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND):
case HRESULT_FROM_WIN32(ERROR_PROC_NOT_FOUND):
uid = IDS_OC_REGISTER_PROBLEM;
break;
case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND):
uid = IDS_OC_FILE_PROBLEM;
break;
case NETCFG_E_NEED_REBOOT:
case HRESULT_FROM_WIN32(ERROR_SERVICE_MARKED_FOR_DELETE):
uid = IDS_OC_NEEDS_REBOOT;
break;
case HRESULT_FROM_WIN32(ERROR_CANCELLED):
uid = IDS_OC_USER_CANCELLED;
break;
case HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED):
uid = IDS_OC_NO_PERMISSION;
break;
default:
uid = IDS_OC_ERROR;
break;
}
return uid;
}
//+---------------------------------------------------------------------------
//
// Function: SzErrorToString
//
// Purpose: Converts an HRESULT into a displayable string.
//
// Arguments:
// hr [in] HRESULT value to convert.
//
// Returns: LPWSTR a dynamically allocated string to be freed with LocalFree
//
// Author: mbend 3 Apr 2000
//
// Notes: Attempts to use FormatMessage to convert the HRESULT to a string.
// If that fails, just convert the HRESULT to a hex string.
//
LPWSTR SzErrorToString(HRESULT hr)
{
LPWSTR pszErrorText = NULL;
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, hr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
(WCHAR*)&pszErrorText, 0, NULL);
if (pszErrorText)
{
// Strip off newline characters.
//
LPWSTR pchText = pszErrorText;
while (*pchText && (*pchText != L'\r') && (*pchText != L'\n'))
{
pchText++;
}
*pchText = 0;
return pszErrorText;
}
// We did't find anything so format the hex value
WCHAR szBuf[128];
wsprintfW(szBuf, L"0x%08x", hr);
WCHAR * szRet = reinterpret_cast<WCHAR*>(LocalAlloc(LMEM_FIXED, lstrlenW(szBuf) * sizeof(WCHAR)));
if(szRet)
{
lstrcpyW(szRet, szBuf);
}
return szRet;
}
//+---------------------------------------------------------------------------
//
// Function: ReportErrorHr
//
// Purpose: Pops up a message box related to error passed in.
//
// Arguments:
// hr [in] HRESULT value to report.
// ids [in] Resource ID of string to display.
// hwnd [in] HWND of parent window.
// pszDesc [in] Description of component involved.
//
// Returns: Nothing.
//
// Author: danielwe 28 Apr 1997
//
// Notes: The string resource in ids must contain a %1 and %2 where %1
// is the name of the component, and %2 is the error code.
//
VOID ReportErrorHr(HRESULT hr, INT ids, HWND hwnd, PCWSTR pszDesc)
{
BOOL bCleanup = TRUE;
WCHAR * szText = SzErrorToString(hr);
if(!szText)
{
szText = L"Out of memory!";
bCleanup = FALSE;
}
NcMsgBox(hwnd, IDS_OC_CAPTION, ids, MB_ICONSTOP | MB_OK, pszDesc, szText);
if(bCleanup)
{
LocalFree(szText);
}
}
//+---------------------------------------------------------------------------
//
// Function: HrVerifyStaticIPPresent
//
// Purpose: Verify that at least one adapter has a static IP address.
// Both DHCP Server and WINS need to know this, as they need
// to bring up UI if this isn't the case. This function is, of
// course, a complete hack until we can get a properties
// interface hanging off of the components.
//
// Arguments:
// pnc [in] INetCfg interface to use
//
// Returns: S_OK, or valid Win32 error code.
//
// Author: jeffspr 19 Jun 1997
//
// Notes:
//
HRESULT HrVerifyStaticIPPresent(INetCfg *pnc)
{
HRESULT hr = S_OK;
HKEY hkeyInterfaces = NULL;
HKEY hkeyEnum = NULL;
INetCfgComponent* pncc = NULL;
HKEY hkeyTcpipAdapter = NULL;
PWSTR pszBindName = NULL;
Assert(pnc);
// Iterate the adapters in the system looking for non-virtual adapters
//
CIterNetCfgComponent nccIter(pnc, &GUID_DEVCLASS_NET);
while (S_OK == (hr = nccIter.HrNext(&pncc)))
{
DWORD dwFlags = 0;
// Get the adapter characteristics
//
hr = pncc->GetCharacteristics(&dwFlags);
if (SUCCEEDED(hr))
{
DWORD dwEnableValue = 0;
// If we're NOT a virtual adapter, THEN test for
// tcp/ip static IP
if (!(dwFlags & NCF_VIRTUAL))
{
WCHAR szRegPath[MAX_PATH+1];
// Get the component bind name
//
hr = pncc->GetBindName(&pszBindName);
if (FAILED(hr))
{
TraceTag(ttidError,
"Error getting bind name from component "
"in HrVerifyStaticIPPresent()");
goto Exit;
}
// Build the path to the TCP/IP instance key for his adapter
//
wsprintfW(szRegPath, L"%s\\%s",
c_szTcpipInterfacesPath, pszBindName);
// Open the key for this adapter.
//
hr = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE,
szRegPath,
KEY_READ, &hkeyTcpipAdapter);
if (SUCCEEDED(hr))
{
// Read the EnableDHCP value.
//
hr = HrRegQueryDword(hkeyTcpipAdapter, c_szEnableDHCP,
&dwEnableValue);
if (FAILED(hr))
{
TraceTag(ttidError,
"Error reading the EnableDHCP value from "
"the enumerated key in "
"HrVerifyStaticIPPresent()");
goto Exit;
}
// If we've found a non-DHCP-enabled adapter.
//
if (0 == dwEnableValue)
{
// We have our man. Take a hike, and return S_OK,
// meaning that we had at least one good adapter.
// The enumerated key will get cleaned up at exit.
hr = S_OK;
goto Exit;
}
RegSafeCloseKey(hkeyTcpipAdapter);
hkeyTcpipAdapter = NULL;
}
else
{
// If the key wasn't found, we just don't have a
// binding to TCP/IP. This is fine, but we don't need
// to continue plodding down this path.
//
if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
{
hr = S_OK;
}
else
{
TraceTag(ttidError,
"Error opening adapter key in "
"HrVerifyStaticIPPresent()");
goto Exit;
}
}
}
}
if (pszBindName)
{
CoTaskMemFree(pszBindName);
pszBindName = NULL;
}
ReleaseObj (pncc);
pncc = NULL;
}
// If we haven't found an adapter, we'll have an S_FALSE returned from
// the HrNext. This is fine, because if we haven't found an adapter
// with a static IP address, this is exactly what we want to return.
// If we'd found one, we'd have set hr = S_OK, and dropped out of the
// loop.
Exit:
RegSafeCloseKey(hkeyTcpipAdapter);
if (pszBindName)
{
CoTaskMemFree(pszBindName);
pszBindName = NULL;
}
ReleaseObj(pncc);
TraceError("HrVerifyStaticIPPresent()", (hr == S_FALSE) ? S_OK : hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrCountConnections
//
// Purpose: Determines the number of LAN connections present and returns
// a pointer to an INetConnection object if only one connection
// is present.
//
// Arguments:
// ppconn [out] If only one connection is present, this returns it
//
// Returns: S_OK if no errors were found and at least one connection
// exists, S_FALSE if no connections exist, or a Win32 or OLE
// error code otherwise
//
// Author: danielwe 28 Jul 1998
//
// Notes:
//
HRESULT HrCountConnections(INetConnection **ppconn)
{
HRESULT hr = S_OK;
INetConnectionManager * pconMan;
Assert(ppconn);
*ppconn = NULL;
// Iterate all LAN connections
//
hr = HrCreateInstance(
CLSID_LanConnectionManager,
CLSCTX_SERVER | CLSCTX_NO_CODE_DOWNLOAD,
&pconMan);
TraceHr(ttidError, FAL, hr, FALSE, "HrCreateInstance");
if (SUCCEEDED(hr))
{
CIterNetCon ncIter(pconMan, NCME_DEFAULT);
INetConnection * pconn = NULL;
INetConnection * pconnCur = NULL;
INT cconn = 0;
while (SUCCEEDED(hr) && (S_OK == (ncIter.HrNext(&pconn))))
{
ReleaseObj(pconnCur);
cconn++;
AddRefObj(pconnCur = pconn);
ReleaseObj(pconn);
}
if (cconn > 1)
{
// if more than one connection found, release last one we had
ReleaseObj(pconnCur);
hr = S_OK;
}
else if (cconn == 0)
{
ReleaseObj(pconnCur);
hr = S_FALSE;
}
else // conn == 1
{
*ppconn = pconnCur;
hr = S_OK;
}
ReleaseObj(pconMan);
}
TraceError("HrCountConnections", (hr == S_FALSE) ? S_OK : hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrHandleStaticIpDependency
//
// Purpose: Handles the need that some components have that requires
// at least one adapter using a static IP address before they
// can be installed properly.
//
// Arguments:
// pnocd [in] Pointer to NETOC data
//
// Returns: S_OK if success, Win32 HRESULT error code otherwise.
//
// Author: danielwe 19 Jun 1997
//
// Notes:
//
HRESULT HrHandleStaticIpDependency(PNETOCDATA pnocd)
{
HRESULT hr = S_OK;
static BOOL fFirstInvocation = TRUE;
// bug 25841. This function is called during installation of DNS, DHCP,
// and WINS. If all three are being installed togetther then this ends
// up showing the same error message thrice when one would suffice.
if( fFirstInvocation )
{
fFirstInvocation = FALSE;
}
else
{
return hr;
}
// Can't do anything about this if not in "attended" setup mode
if (!(g_ocmData.sic.SetupData.OperationFlags & SETUPOP_BATCH))
{
BOOL fChangesApplied = FALSE;
INetCfg * pnc = NULL;
Assert(pnocd);
//Assert(g_ocmData.hwnd);
hr = HrOcGetINetCfg(pnocd, FALSE, &pnc);
if (SUCCEEDED(hr))
{
hr = HrVerifyStaticIPPresent(pnc);
if (hr == S_FALSE)
{
INetConnectionCommonUi * pcommUi;
INetConnection * pconn = NULL;
hr = HrCountConnections(&pconn);
if (S_OK == hr)
{
// One or more connections found
// Display message to user indicating that she has to
// configure at least one adapter with a static IP address
// before we can continue.
NcMsgBox(g_ocmData.hwnd, IDS_OC_CAPTION,
IDS_OC_NEED_STATIC_IP,
MB_ICONINFORMATION | MB_OK,
pnocd->strDesc.c_str());
hr = CoCreateInstance(CLSID_ConnectionCommonUi, NULL,
CLSCTX_INPROC | CLSCTX_NO_CODE_DOWNLOAD,
IID_INetConnectionCommonUi,
reinterpret_cast<LPVOID *>(&pcommUi));
TraceHr(ttidError, FAL, hr, FALSE, "CoCreateInstance");
if (SUCCEEDED(hr))
{
if (pconn)
{
// Exactly one connection found
hr = pcommUi->ShowConnectionProperties(g_ocmData.hwnd,
pconn);
if (S_OK == hr)
{
fChangesApplied = TRUE;
}
else if (FAILED(hr))
{
// Eat the error since we can't do anything about it
// anyway.
TraceError("HrHandleStaticIpDependency - "
"ShowConnectionProperties", hr);
hr = S_OK;
}
}
else
{
// More than one connection found
if (SUCCEEDED(hr))
{
NETCON_CHOOSECONN chooseCon = {0};
chooseCon.lStructSize = sizeof(NETCON_CHOOSECONN);
chooseCon.hwndParent = g_ocmData.hwnd;
chooseCon.dwTypeMask = NCCHT_LAN;
chooseCon.dwFlags = NCCHF_DISABLENEW;
hr = pcommUi->ChooseConnection(&chooseCon, NULL);
if (SUCCEEDED(hr))
{
fChangesApplied = TRUE;
}
else
{
// Eat the error since we can't do anything about it
// anyway.
TraceError("HrHandleStaticIpDependency - "
"ChooseConnection", hr);
hr = S_OK;
}
}
}
ReleaseObj(pcommUi);
}
ReleaseObj(pconn);
if (SUCCEEDED(hr))
{
// Don't bother checking again if they never
// made any changes
if (!fChangesApplied ||
(S_FALSE == (hr = HrVerifyStaticIPPresent(pnc))))
{
// Geez, still no static IP address available.
// Put up another message box scolding the user for
// not following directions.
NcMsgBox(g_ocmData.hwnd, IDS_OC_CAPTION,
IDS_OC_STILL_NO_STATIC_IP,
MB_ICONSTOP | MB_OK,
pnocd->strDesc.c_str());
hr = S_OK;
}
}
}
}
hr = HrUninitializeAndReleaseINetCfg(TRUE, pnc, FALSE);
}
}
else
{
TraceTag(ttidNetOc, "Not handling static IP dependency for %S "
"because we're in unattended mode", pnocd->strDesc.c_str());
}
TraceError("HrHandleStaticIpDependency", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrOcGetINetCfg
//
// Purpose: Obtains an INetCfg to work with
//
// Arguments:
// pnocd [in] OC Data
// fWriteLock [in] TRUE if write lock should be acquired, FALSE if
// not.
// ppnc [out] Returns INetCfg pointer
//
// Returns: S_OK if success, OLE or Win32 error if failed. ERROR_CANCELLED
// is returned if INetCfg is locked and the users cancels.
//
// Author: danielwe 18 Dec 1997
//
// Notes:
//
HRESULT HrOcGetINetCfg(PNETOCDATA pnocd, BOOL fWriteLock, INetCfg **ppnc)
{
HRESULT hr = S_OK;
PWSTR pszDesc;
BOOL fInitCom = TRUE;
Assert(ppnc);
*ppnc = NULL;
top:
AssertSz(!*ppnc, "Can't have valid INetCfg here!");
hr = HrCreateAndInitializeINetCfg(&fInitCom, ppnc, fWriteLock, 0,
SzLoadIds(IDS_OC_CAPTION), &pszDesc);
if ((hr == NETCFG_E_NO_WRITE_LOCK) && !pnocd->fCleanup)
{
int nRet;
nRet = NcMsgBox(g_ocmData.hwnd, IDS_OC_CAPTION, IDS_OC_CANT_GET_LOCK,
MB_RETRYCANCEL | MB_DEFBUTTON1 | MB_ICONWARNING,
pnocd->strDesc.c_str(),
pszDesc ? pszDesc : SzLoadIds(IDS_OC_GENERIC_COMP));
CoTaskMemFree(pszDesc);
if (IDRETRY == nRet)
{
goto top;
}
else
{
hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
}
}
TraceError("HrOcGetINetCfg", hr);
return hr;
}