|
|
//+---------------------------------------------------------------------------
//
// 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 "ncatlui.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"
#include "netocmsg.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"NetCMAK", HrOcExtCMAK }, { L"NetCPS", HrOcExtCPS }, { L"WINS", HrOcExtWINS }, { L"DNS", HrOcExtDNS }, { L"SNMP", HrOcExtSNMP }, { L"IAS", HrOcExtIAS }, }; #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: S_OK if success, failure HRESULT otherwise
//
// Author: danielwe 23 Feb 1998
//
// Notes:
//
HRESULT AddComponent(PCWSTR pszComponent, NETOCDATA *pnocd) { HRESULT hr = S_OK;
Assert(pszComponent); Assert(pnocd);
pnocd->pszComponentId = SzDupSz(pszComponent); if (pnocd->pszComponentId) { try { g_listOcData.push_back(pnocd); } catch (bad_alloc) { MemFree(pnocd->pszComponentId); hr = E_OUTOFMEMORY; } } else { hr = E_OUTOFMEMORY; }
TraceError("AddComponent", hr); return hr; }
//+---------------------------------------------------------------------------
//
// Function: ParseAdditionalArguments
//
// Purpose: Parse additional commands following the /z option
// of sysocmgr.
//
// Arguments: Nothing.
//
// Returns: Nothing.
//
// Author: roelfc 19 Jul 2001
//
// Notes:
//
VOID ParseAdditionalArguments() {
LPTSTR lpCmdLine = GetCommandLine(); TCHAR szTokens[] = TEXT("-/"); LPCTSTR lpszToken = NULL;
if (lpCmdLine) { // Search for additional parameters
lpszToken = wcspbrk(lpCmdLine, szTokens); while (lpszToken != NULL) { // Check the correct option
switch (lpszToken[1]) { case TEXT('z'): case TEXT('Z'): if ((lpszToken[2] == TEXT(':')) && (_wcsnicmp(&lpszToken[3], SHOW_UNATTENDED_MESSAGES, wcslen(SHOW_UNATTENDED_MESSAGES)) == 0) && (!iswgraph(lpszToken[3 + wcslen(SHOW_UNATTENDED_MESSAGES)]))) { // Set the show unattended messages flag
g_ocmData.fShowUnattendedMessages = TRUE; TraceTag(ttidNetOc, "Flag set to show messages in unattended mode"); } break;
default: break; }
// Skip the last token found to find the next one
lpszToken = wcspbrk(&lpszToken[1], szTokens); } }
}
//+---------------------------------------------------------------------------
//
// Function: RegisterNetEventSource
//
// Purpose: Add netoc source name to the registry for
// event reporting.
//
// Arguments: Nothing.
//
// Returns: TRUE if success, FALSE otherwise
//
// Author: roelfc 21 May 2001
//
// Notes:
//
BOOL RegisterNetEventSource() { HKEY hk; BOOL fSuccess = TRUE; // Check if the key already exists
if (ERROR_SUCCESS != RegOpenKey(HKEY_LOCAL_MACHINE, NETOC_REGISTRY_NAME NETOC_SERVICE_NAME, &hk)) { DWORD dwData; WCHAR szBuf[80];
// Create the key as a subkey under the Application
// key in the EventLog registry key.
if (RegCreateKey(HKEY_LOCAL_MACHINE, NETOC_REGISTRY_NAME NETOC_SERVICE_NAME, &hk)) { TraceTag(ttidNetOc, "RegisterEventSource: Could not create the registry key."); return FALSE; } // Set the name of the message file.
lstrcpyW(szBuf, NETOC_DLL_NAME); // Add the name to the EventMessageFile subkey.
if (RegSetValueEx(hk, // subkey handle
L"EventMessageFile", // value name
0, // must be zero
REG_EXPAND_SZ, // value type
(LPBYTE) szBuf, // pointer to value data
(2 * lstrlenW(szBuf)) + 1)) // length of value data
{ TraceTag(ttidNetOc, "RegisterEventSource: Could not set the event message file."); fSuccess = FALSE; goto RegisterExit; } // Set the supported event types in the TypesSupported subkey.
dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; if (RegSetValueEx(hk, // subkey handle
L"TypesSupported", // value name
0, // must be zero
REG_DWORD, // value type
(LPBYTE) &dwData, // pointer to value data
sizeof(DWORD))) // length of value data
{ TraceTag(ttidNetOc, "RegisterEventSource: Could not set the supported types."); fSuccess = FALSE; }
}
RegisterExit: RegCloseKey(hk);
// Return result
return fSuccess;
}
//+---------------------------------------------------------------------------
//
// 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; UINT uiFlags;
switch (uFunction) { case OC_PREINITIALIZE: return HrOnPreInitializeComponent(uParam1);
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)) { uiFlags = PtrToUlong(pvParam2); hr = HrOnQueryChangeSelState(reinterpret_cast<PCWSTR>(pvSubcomponentId), uParam1, uiFlags);
if (S_OK == hr) { return TRUE; }
} else { ReportErrorHr(hr, IDS_OC_NO_PERMS, g_ocmData.hwnd, SzLoadIds(IDS_OC_GENERIC_COMP)); } 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);
if (HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr) { ReportErrorHr(hr, UiOcErrorFromHr(hr), g_ocmData.hwnd, (pnocd)?(pnocd->strDesc.c_str()): (SzLoadIds(IDS_OC_GENERIC_COMP))); } } }
TraceError("NetOcSetupProcHelper", hr); return DwWin32ErrorFromHr(hr); }
return NO_ERROR; }
//+---------------------------------------------------------------------------
//
// Function: HrOnPreInitializeComponent
//
// Purpose: Handles the OC_PREINITIALIZE function message.
//
// Arguments:
// uModesSupported [in] Modes supported by OCM (see OCManager spec)
//
// Returns: Flag indicating mode supported by netoc
//
// Author: roelfc 19 Jul 2001
//
// Notes:
//
DWORD HrOnPreInitializeComponent (UINT uModesSupported) {
RegisterNetEventSource();
// Parse the additional command line arguments specific for netoc
ParseAdditionalArguments();
return OCFLAG_UNICODE; }
//+---------------------------------------------------------------------------
//
// 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); if (!pnocd) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); }
if (SUCCEEDED(hr)) { 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) { hr = AddComponent(pszSubComponentId, pnocd); if (FAILED(hr)) { TraceTag(ttidNetOc, "OC_QUERY_STATE: Failed to add component %s.", pszSubComponentId); delete pnocd; pnocd = NULL; } } }
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); if (!pnocd) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); }
if (SUCCEEDED(hr)) { 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); if (!pnocd) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); }
if (SUCCEEDED(hr)) { 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); if (!pnocd) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); }
if (SUCCEEDED(hr)) {
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; BOOL fFound = FALSE;
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);
fFound = TRUE; // Don't try to call any other functions
break; } }
if (FALSE == fFound) { TraceTag(ttidNetOc, "HrCallExternalProc - did not find a matching Proc for %S", pnocd->pszComponentId); }
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) + 1) * sizeof(WCHAR))); if(szRet) { lstrcpyW(szRet, szBuf); } return szRet; }
//+---------------------------------------------------------------------------
//
// Function: NcMsgBoxMc
//
// Purpose: Displays a message box using resource strings from
// the message resource file and using replaceable
// parameters.
//
// Arguments:
// hwnd [in] parent window handle
// unIdCaption [in] resource id of caption string
// (from .RC file)
// unIdFormat [in] resource id of text string (with %1, %2, etc.)
// (from .MC file)
// unStyle [in] standard message box styles
// ... [in] replaceable parameters (optional)
// (these must be PCWSTRs as that is all
// FormatMessage handles.)
//
// Returns: The return value of MessageBox()
//
// Author: roelfc 7 June 2001
//
// Notes: FormatMessage is used to do the parameter substitution.
// The unIdFormat resource id MUST be specified in the
// .MC resource file with a severity of either informational,
// warning or error.
//
NOTHROW int WINAPIV NcMsgBoxMc(HWND hwnd, UINT unIdCaption, UINT unIdFormat, UINT unStyle, ...) { PCWSTR pszCaption = SzLoadIds(unIdCaption);
// We report only valid message resources to prevent event log failures
AssertSz(STATUS_SEVERITY_VALUE(unIdFormat) != STATUS_SEVERITY_SUCCESS, "Either the severity code is not set (information, warning or error)," " or you passed a .RC resource id instead of a .MC resource id.");
PWSTR pszText = NULL; va_list val; va_start (val, unStyle); FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE, _Module.GetResourceInstance(), unIdFormat, 0, (PWSTR)&pszText, 0, &val); va_end (val); if(!pszText) { // This is what MessageBox returns if it fails.
return 0; }
INT nRet = MessageBox (hwnd, pszText, pszCaption, unStyle); LocalFree (pszText);
return nRet; }
//+---------------------------------------------------------------------------
//
// Function: ReportEventHrString
//
// Purpose: Reports an error, warning or informative message
// to the event log from an error description string.
//
// Arguments:
// pszErr [in] Error description string.
// ids [in] Resource ID of string to display.
// pszDesc [in] Description of component involved.
//
// Returns: S_OK, or valid Win32 error code.
//
// Author: roelfc 18 May 2001
//
// Notes: This function works slow since it calls the open and close
// eventlog source everytime. This should not have an effect
// since it only happens during errors.
// 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.
// The resource ID of the string to display MUST be defined
// in the .MC file, with a severity of either informational,
// warning or error. The Assert below prevents the incorrect
// use of .RC strings which will fail during event logging.
//
HRESULT ReportEventHrString(PCWSTR pszErr, INT ids, PCWSTR pszDesc) {
HANDLE hEventLog; WORD elt; HRESULT hr = S_OK; PCWSTR plpszSubStrings[2];
plpszSubStrings[0] = pszDesc; plpszSubStrings[1] = pszErr;
// We report only valid message resources to prevent event log failures
AssertSz(STATUS_SEVERITY_VALUE(ids) != STATUS_SEVERITY_SUCCESS, "Either the severity code is not set (information, warning or error)," " or you passed a .RC resource id instead of a .MC resource id.");
// Determine the event log type
switch (STATUS_SEVERITY_VALUE(ids)) { case STATUS_SEVERITY_WARNING: elt = EVENTLOG_WARNING_TYPE; break;
case STATUS_SEVERITY_ERROR: elt = EVENTLOG_ERROR_TYPE; break;
default: // Default to informational
elt = EVENTLOG_INFORMATION_TYPE; break; }
hEventLog = RegisterEventSource(NULL, NETOC_SERVICE_NAME); Assert(hEventLog);
if (hEventLog) { if (!ReportEvent(hEventLog, elt, 0, // Event category
ids, // Message file full id
NULL, sizeof(plpszSubStrings) / sizeof(plpszSubStrings[0]), 0, plpszSubStrings, NULL)) { hr = HRESULT_FROM_WIN32(GetLastError()); }
DeregisterEventSource(hEventLog); } else { hr = HRESULT_FROM_WIN32(GetLastError()); }
return hr; }
//+---------------------------------------------------------------------------
//
// Function: ReportEventHrResult
//
// Purpose: Reports an error, warning or informative message
// to the event log from a result value.
//
// Arguments:
// hrv [in] HRESULT value to report.
// ids [in] Resource ID of string to display.
// pszDesc [in] Description of component involved.
//
// Returns: S_OK, or valid Win32 error code.
//
// Author: roelfc 18 May 2001
//
// Notes:
//
HRESULT ReportEventHrResult(HRESULT hrv, INT ids, PCWSTR pszDesc) {
HRESULT hr = S_OK; BOOL bCleanup = TRUE; WCHAR * szText = SzErrorToString(hrv);
if(!szText) { szText = L"Out of memory!"; bCleanup = FALSE; }
hr = ReportEventHrString(szText, ids, pszDesc); if(bCleanup) { LocalFree(szText); }
return hr; }
//+---------------------------------------------------------------------------
//
// Function: ReportErrorHr
//
// Purpose: Reports an error, warning or informative message
// to the user or event log.
//
// Arguments:
// hrv [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: S_OK, or valid Win32 error code.
//
// 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.
// The resource ID of the string to display MUST be defined
// in the .MC file, with a severity of either informational,
// warning or error. The Assert below prevents the incorrect
// use of .RC strings which will fail during event logging.
//
HRESULT ReportErrorHr(HRESULT hrv, INT ids, HWND hwnd, PCWSTR pszDesc) {
DWORD dwRt; HRESULT hr = S_OK;
// We report only valid message resources to prevent event log failures
AssertSz(STATUS_SEVERITY_VALUE(ids) != STATUS_SEVERITY_SUCCESS, "Either the severity code is not set (information, warning or error)," " or you passed a .RC resource id instead of a .MC resource id.");
// We can only display a message box in "attended" setup mode
// or when the caller overide with the /z:netoc_show_unattended_messages option,
// else we log the problem to the event log.
if ((g_ocmData.sic.SetupData.OperationFlags & SETUPOP_BATCH) && (!g_ocmData.fShowUnattendedMessages)) { // In batch mode ("unattended") we need to report the error in the event log
hr = ReportEventHrResult(hrv, ids, pszDesc); } else { BOOL bCleanup = TRUE; WCHAR * szText = SzErrorToString(hrv);
if(!szText) { szText = L"Out of memory!"; bCleanup = FALSE; }
// Get the right icon from the type of message
switch (STATUS_SEVERITY_VALUE(ids)) { case STATUS_SEVERITY_WARNING: dwRt = MB_ICONWARNING; break;
case STATUS_SEVERITY_ERROR: dwRt = MB_ICONERROR; break;
default: // Default to informational
dwRt = MB_ICONINFORMATION; break; } // We can display the error to the user
NcMsgBoxMc(hwnd, IDS_OC_CAPTION, ids, dwRt | MB_OK, pszDesc, szText);
if(bCleanup) { LocalFree(szText); } }
return hr; }
//+---------------------------------------------------------------------------
//
// 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; }
// We handle "attended" and "unattended" setup mode through ReportErrorHr
{ 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
// Report message to user indicating that she has to
// configure at least one adapter with a static IP address
// before we can continue.
ReportErrorHr(hr, IDS_OC_NEED_STATIC_IP, g_ocmData.hwnd, pnocd->strDesc.c_str());
// Try to fix it if we are in "attended" mode or
// we have the /z:netoc_show_unattended_messages options flag set.
if ((!(g_ocmData.sic.SetupData.OperationFlags & SETUPOP_BATCH)) || (g_ocmData.fShowUnattendedMessages)) { 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.
// Report another message scolding the user for
// not following directions.
ReportErrorHr(hr, IDS_OC_STILL_NO_STATIC_IP, g_ocmData.hwnd, pnocd->strDesc.c_str()); hr = S_OK; } } } else { // Just report the error as would have happened when the
// user did not correct it.
ReportErrorHr(hr, IDS_OC_STILL_NO_STATIC_IP, g_ocmData.hwnd, pnocd->strDesc.c_str()); TraceTag(ttidNetOc, "Not handling static IP dependency for %S " "because we're in unattended mode", pnocd->strDesc.c_str()); } } }
hr = HrUninitializeAndReleaseINetCfg(TRUE, pnc, FALSE); } }
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) { // See if we are in "attended" mode or
// we have the /z:netoc_show_unattended_messages options flag set.
if ((g_ocmData.sic.SetupData.OperationFlags & SETUPOP_BATCH) && (!g_ocmData.fShowUnattendedMessages)) { // "Unattended" mode, just report error
ReportEventHrString(pnocd->strDesc.c_str(), IDS_OC_CANT_GET_LOCK, pszDesc ? pszDesc : SzLoadIds(IDS_OC_GENERIC_COMP));
CoTaskMemFree(pszDesc);
hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); } else { // "Attended mode", so interact with user
int nRet;
nRet = NcMsgBoxMc(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; }
|