//+--------------------------------------------------------------------------- // // 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 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(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(uParam1)); case OC_WIZARD_CREATED: TraceTag(ttidNetOc, "OC_WIZARD_CREATED: 0x%08X", pvParam2); OnWizardCreated(reinterpret_cast(pvParam2)); break; case OC_INIT_COMPONENT: TraceTag(ttidNetOc, "OC_INIT_COMPONENT: %S", pvSubcomponentId ? pvSubcomponentId : L"null"); hr = HrOnInitComponent(reinterpret_cast(pvParam2)); break; case OC_ABOUT_TO_COMMIT_QUEUE: TraceTag(ttidNetOc, "OC_ABOUT_TO_COMMIT_QUEUE: %S", pvSubcomponentId ? pvSubcomponentId : L"null"); hr = HrOnPreCommitFileQueue(reinterpret_cast(pvSubcomponentId)); break; case OC_CALC_DISK_SPACE: // Ignore return value for now. This is not fatal anyway. (VOID) HrOnCalcDiskSpace(reinterpret_cast(pvSubcomponentId), uParam1, reinterpret_cast(pvParam2)); break; case OC_QUERY_STATE: return DwOnQueryState(reinterpret_cast(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(pvSubcomponentId), reinterpret_cast(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(pvComponentId), reinterpret_cast(pvSubcomponentId)); break; case OC_QUERY_STEP_COUNT: return DwOnQueryStepCount(reinterpret_cast(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(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(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 .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(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(&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; }