// // N W C L I O B J . C P P // // Implementation of the CNWClient notify object model // #include "pch.h" #pragma hdrstop #include "ncerror.h" #include "ncperms.h" #include "ncreg.h" #include "ncsetup.h" #include "ncsvc.h" #include "nwcliobj.h" #include extern const WCHAR c_szAfNWCWorkstationParameters[]; extern const WCHAR c_szAfNWCWorkstationShares[]; extern const WCHAR c_szAfNWCWorkstationDrives[]; extern const WCHAR c_szInfId_MS_NWIPX[]; extern const WCHAR c_szInfId_MS_Server[]; //---[ Constants ]------------------------------------------------------------- static const WCHAR c_szNWClientParamPath[] = L"System\\CurrentControlSet\\Services\\NWCWorkstation\\Parameters"; static const WCHAR c_szNWClientSharesPath[] = L"System\\CurrentControlSet\\Services\\NWCWorkstation\\Shares"; static const WCHAR c_szNWClientDrivesPath[] = L"System\\CurrentControlSet\\Services\\NWCWorkstation\\Drives"; static const WCHAR c_szLMServerParamPath[] = L"System\\CurrentControlSet\\Services\\LanmanServer\\Parameters"; static const WCHAR c_szLMServerLinkagePath[] = L"System\\CurrentControlSet\\Services\\LanmanServer\\Linkage"; static const WCHAR c_szEnableSharedNetDrives[] = L"EnableSharedNetDrives"; static const WCHAR c_szOtherDependencies[] = L"OtherDependencies"; static const WCHAR c_szGWEnabledValue[] = L"GatewayEnabled"; extern const WCHAR c_szSvcLmServer[]; // L"LanmanServer"; extern const WCHAR c_szSvcNWCWorkstation[]; // L"NWCWorkstation"; HRESULT HrRefreshEntireNetwork(); HRESULT HrGetEntireNetworkPidl(LPITEMIDLIST *ppidlFolder); // // Constructor // CNWClient::CNWClient() { // Initialize member variables. m_pnc = NULL; m_pncc = NULL; m_eInstallAction = eActUnknown; m_hlibConfig = NULL; m_fUpgrade = FALSE; // Get the product flavor (PF_WORKSTATION or PF_SERVER). Use this // to decide whether or not we need to install the "server" component. // GetProductFlavor(NULL, &m_pf); } CNWClient::~CNWClient() { ReleaseObj(m_pncc); ReleaseObj(m_pnc); // Release KEY handles here. } // // INetCfgNotify // STDMETHODIMP CNWClient::Initialize( INetCfgComponent * pnccItem, INetCfg* pnc, BOOL fInstalling) { Validate_INetCfgNotify_Initialize(pnccItem, pnc, fInstalling); TraceTag(ttidNWClientCfg, "CNWClient::Initialize"); m_pncc = pnccItem; m_pnc = pnc; AssertSz(m_pncc, "m_pncc NULL in CNWClient::Initialize"); AssertSz(m_pnc, "m_pnc NULL in CNWClient::Initialize"); // Addref the config objects // AddRefObj(m_pncc); AddRefObj(m_pnc); return S_OK; } //+--------------------------------------------------------------------------- // // Member: CNWClient::HrRestoreRegistry // // Purpose: Restores the contents of the registry for this component // // Arguments: // (none) // // Returns: Win32 error if failed, otherwise S_OK // // Author: jeffspr 13 Aug 1997 // // Notes: // HRESULT CNWClient::HrRestoreRegistry() { HRESULT hr = S_OK; HKEY hkey = NULL; TOKEN_PRIVILEGES * ptpRestore = NULL; DWORD dwDisp = 0; static const WCHAR c_szSvcDLLName[] = L"%SystemRoot%\\System32\\nwwks.dll"; static const WCHAR c_szServiceDll[] = L"ServiceDll"; TraceTag(ttidNWClientCfg, "CNWClient::HrRestoreRegistry"); if (!m_strParamsRestoreFile.empty() || !m_strDrivesRestoreFile.empty() || !m_strSharesRestoreFile.empty()) { hr = HrEnableAllPrivileges(&ptpRestore); } if (SUCCEEDED(hr) && !m_strParamsRestoreFile.empty()) { // Ensure key is there by creating it hr = HrRegCreateKeyEx(HKEY_LOCAL_MACHINE, c_szNWClientParamPath, 0, KEY_ALL_ACCESS, NULL, &hkey, &dwDisp); if (SUCCEEDED(hr)) { hr = HrRegRestoreKey(hkey, m_strParamsRestoreFile.c_str(), 0); if (FAILED(hr)) { TraceError("CNWClient::HrRestoreRegistry - HrRestoreRegistry for " "Parameters", hr); hr = S_OK; } // // Bug 182442. HrRegRestoreKey above overwrites the ServiceDll value added // from the inf file. So, we manually save it. // hr = HrRegSetValueEx(hkey, c_szServiceDll, REG_EXPAND_SZ, (const BYTE *)c_szSvcDLLName, (wcslen(c_szSvcDLLName) + 1) * sizeof(WCHAR)); if (FAILED(hr)) { TraceError("CNWClient::HrRestoreRegistry - HrRestoreRegistry for " "ServiceDll", hr); hr = S_OK; } RegCloseKey(hkey); hkey = NULL; } } if (!m_strSharesRestoreFile.empty()) { // Ensure key is there by creating it hr = HrRegCreateKeyEx(HKEY_LOCAL_MACHINE, c_szNWClientSharesPath, 0, KEY_ALL_ACCESS, NULL, &hkey, &dwDisp); if (SUCCEEDED(hr)) { hr = HrRegRestoreKey(hkey, m_strSharesRestoreFile.c_str(), 0); if (FAILED(hr)) { TraceError("CNWClient::HrRestoreRegistry - HrRestoreRegistry for " "Shares", hr); hr = S_OK; } RegCloseKey(hkey); hkey = NULL; } } if (!m_strDrivesRestoreFile.empty()) { // Ensure key is there by creating it hr = HrRegCreateKeyEx(HKEY_LOCAL_MACHINE, c_szNWClientDrivesPath, 0, KEY_ALL_ACCESS, NULL, &hkey, &dwDisp); if (SUCCEEDED(hr)) { hr = HrRegRestoreKey(hkey, m_strDrivesRestoreFile.c_str(), 0); if (FAILED(hr)) { TraceError("CNWClient::HrRestoreRegistry - HrRestoreRegistry for " "Drives", hr); hr = S_OK; } RegCloseKey(hkey); hkey = NULL; } } if (ptpRestore) { hr = HrRestorePrivileges(ptpRestore); delete [] reinterpret_cast(ptpRestore); } TraceError("CNWClient::HrRestoreRegistry", hr); return hr; } static const WCHAR c_szDefaultLocation[] = L"DefaultLocation"; static const WCHAR c_szDefaultScriptOptions[] = L"DefaultScriptOptions"; HRESULT CNWClient::HrWriteAnswerFileParams() { HRESULT hr = S_OK; TraceTag(ttidNWClientCfg, "CNWClient::HrWriteAnswerFileParams"); // Don't do anything if we don't have anything to write to the // registry if (!m_strDefaultLocation.empty() || (m_dwLogonScript != 0xFFFFFFFF)) { HKEY hkey; DWORD dwDisp; // Ensure key is there by creating it hr = HrRegCreateKeyEx(HKEY_LOCAL_MACHINE, c_szNWClientParamPath, 0, KEY_ALL_ACCESS, NULL, &hkey, &dwDisp); if (SUCCEEDED(hr)) { if (!m_strDefaultLocation.empty()) { hr = HrRegSetString(hkey, c_szDefaultLocation, m_strDefaultLocation); if (FAILED(hr)) { TraceError("CNWClient::HrWriteAnswerFileParams - Couldn't" " set DefaultLocation", hr); hr = S_OK; } } if (m_dwLogonScript != 0xFFFFFFFF) { // 0x3 is combination of the following: // // #define NW_LOGONSCRIPT_DISABLED 0x00000000 // #define NW_LOGONSCRIPT_ENABLED 0x00000001 // #define NW_LOGONSCRIPT_4X_ENABLED 0x00000002 // hr = HrRegSetDword(hkey, c_szDefaultScriptOptions, m_dwLogonScript ? 0x3 : 0x0); if (FAILED(hr)) { TraceError("CNWClient::HrWriteAnswerFileParams - Couldn't" " set DefaultLocation", hr); hr = S_OK; } } RegCloseKey(hkey); } } TraceError("CNWClient::HrWriteAnswerFileParams", hr); return hr; } static const WCHAR c_szPreferredServer[] = L"PreferredServer"; static const WCHAR c_szDefaultTree[] = L"DefaultTree"; static const WCHAR c_szDefaultContext[] = L"DefaultContext"; static const WCHAR c_szLogonScript[] = L"LogonScript"; //+--------------------------------------------------------------------------- // // Member: CNWClient::HrProcessAnswerFile // // Purpose: Handles necessary processing of contents of the answer file. // // Arguments: // pszAnswerFile [in] Filename of answer file for upgrade. // pszAnswerSection [in] Comma-separated list of sections in the // file appropriate to this component. // // Returns: S_OK if successful, setup API error otherwise. // // Author: jeffspr 8 May 1997 // // Notes: // HRESULT CNWClient::HrProcessAnswerFile( PCWSTR pszAnswerFile, PCWSTR pszAnswerSection) { HRESULT hr; CSetupInfFile csif; TraceTag(ttidNWClientCfg, "CNWClient::HrProcessAnswerFile"); // Open the answer file. hr = csif.HrOpen(pszAnswerFile, NULL, INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL); if (FAILED(hr)) { hr = S_OK; goto Exit; } // Restore portions of the registry based on file names from the answer // file // Get restore file for "Parameters" key hr = csif.HrGetString(pszAnswerSection, c_szAfNWCWorkstationParameters, &m_strParamsRestoreFile); if (FAILED(hr)) { TraceError("CNWClient::HrProcessAnswerFile - Error restoring " "Parameters key", hr); // oh well, just continue hr = S_OK; } // Get restore file for "Shares" key hr = csif.HrGetString(pszAnswerSection, c_szAfNWCWorkstationShares, &m_strSharesRestoreFile); if (FAILED(hr)) { TraceError("CNWClient::HrProcessAnswerFile - Error restoring " "Shares key", hr); // oh well, just continue hr = S_OK; } // Get restore file for "Drives" key hr = csif.HrGetString(pszAnswerSection, c_szAfNWCWorkstationDrives, &m_strDrivesRestoreFile); if (FAILED(hr)) { TraceError("CNWClient::HrProcessAnswerFile - Error restoring " "Drives key", hr); // oh well, just continue hr = S_OK; } // // Read answer file parameters (these are all optional so no errors are // saved) // TraceTag(ttidNWClientCfg, "Reading PreferredServer from answer file"); // Read contents of PreferredServer key. if (FAILED(csif.HrGetString(pszAnswerSection, c_szPreferredServer, &m_strDefaultLocation))) { // Couldn't read PreferredServer key, so we must assume that the other // two values are present tstring strDefaultTree; tstring strDefaultContext; TraceTag(ttidNWClientCfg, "PreferredServer not found so trying " "DefaultTree and DefaultContext instead"); // Read contents of DefaultTree key. if (SUCCEEDED(csif.HrGetString(pszAnswerSection, c_szDefaultTree, &strDefaultTree))) { TraceTag(ttidNWClientCfg, "Got DefaultTree ok: %S", strDefaultTree.c_str()); // Read contents of DefaultContext key. hr = csif.HrGetString(pszAnswerSection, c_szDefaultContext, &strDefaultContext); if (SUCCEEDED(hr)) { TraceTag(ttidNWClientCfg, "Got DefaultContext ok: %S", strDefaultContext.c_str()); // Munge the DefaultLocation value with the DefaultTree and // DefaultContext values read from the answer file m_strDefaultLocation = L"*"; m_strDefaultLocation += strDefaultTree; m_strDefaultLocation += L"\\"; m_strDefaultLocation += strDefaultContext; TraceTag(ttidNWClientCfg, "DefaultLocation is: %S", m_strDefaultLocation.c_str()); } else { TraceError("CNWClient::HrProcessAnswerFile - error reading " "DefaultContext", hr); hr = S_OK; } } } else { TraceTag(ttidNWClientCfg, "DefaultLocation is: %S", m_strDefaultLocation.c_str()); } // Init to impossible value so we know whether we read it or not m_dwLogonScript = 0xFFFFFFFF; // Read contents of LogonScript key. (VOID) csif.HrGetStringAsBool(pszAnswerSection, c_szLogonScript, reinterpret_cast(&m_dwLogonScript)); TraceTag(ttidNWClientCfg, "LogonScript is: %ld", m_dwLogonScript); Exit: TraceError("CNWClient::HrProcessAnswerFile", hr); return hr; } STDMETHODIMP CNWClient::Upgrade(DWORD dwSetupFlags, DWORD dwUpgradeFromBuildNo) { return S_FALSE; } STDMETHODIMP CNWClient::ReadAnswerFile(PCWSTR pszAnswerFile, PCWSTR pszAnswerSection) { Validate_INetCfgNotify_ReadAnswerFile(pszAnswerFile, pszAnswerSection); TraceTag(ttidNWClientCfg, "CNWClient::ReadAnswerFile"); m_eInstallAction = eActInstall; // If we're not already installed, do the work. // if (pszAnswerFile && pszAnswerSection) { HRESULT hr = HrProcessAnswerFile(pszAnswerFile, pszAnswerSection); if (FAILED(hr)) { TraceError("CNWClient::NetworkInstall - Answer file has errors. Defaulting " "all information as if answer file did not exist.", hr); } } return S_OK; } STDMETHODIMP CNWClient::Install(DWORD dw) { Validate_INetCfgNotify_Install(dw); TraceTag(ttidNWClientCfg, "CNWClient::Install"); m_eInstallAction = eActInstall; // Install the NWLink sub-component HRESULT hr = HrInstallComponentOboComponent(m_pnc, NULL, GUID_DEVCLASS_NETTRANS, c_szInfId_MS_NWIPX, m_pncc, NULL); if (SUCCEEDED(hr)) { // If we're NT Server, we DO need to install it, as what we're // installing is GSNW, not CSNW (and therefore, since we're sharing // resources, we need to use the server service) // if (PF_SERVER == m_pf) { NETWORK_INSTALL_PARAMS nip; nip.dwSetupFlags = dw; nip.dwUpgradeFromBuildNo = -1; nip.pszAnswerFile = NULL; nip.pszAnswerSection = NULL; // Install Server hr = HrInstallComponentOboComponent(m_pnc, &nip, GUID_DEVCLASS_NETSERVICE, c_szInfId_MS_Server, m_pncc, NULL); } } TraceError("CNWClient::Install", hr); return hr; } STDMETHODIMP CNWClient::Removing() { TraceTag(ttidNWClientCfg, "CNWClient::Removing"); m_eInstallAction = eActRemove; // Remove the NWLink service // HRESULT hr = HrRemoveComponentOboComponent(m_pnc, GUID_DEVCLASS_NETTRANS, c_szInfId_MS_NWIPX, m_pncc); if (SUCCEEDED(hr)) { if (PF_SERVER == m_pf) { // Remove our reference of the Server service // hr = HrRemoveComponentOboComponent(m_pnc, GUID_DEVCLASS_NETSERVICE, c_szInfId_MS_Server, m_pncc); } } if (hr == NETCFG_S_STILL_REFERENCED) { // If services are still in use, that's OK, I just needed to make // sure that I released my reference. // hr = S_OK; } Validate_INetCfgNotify_Removing_Return(hr); TraceError("CNWClient::Removing()", hr); return hr; } STDMETHODIMP CNWClient::Validate() { return S_OK; } STDMETHODIMP CNWClient::CancelChanges() { return S_OK; } STDMETHODIMP CNWClient::ApplyRegistryChanges() { HRESULT hr = S_OK; TraceTag(ttidNWClientCfg, "CNWClient::ApplyRegistryChanges"); if (m_eInstallAction == eActRemove) { hr = HrRemoveCodeFromOldINF(); } else if (m_eInstallAction == eActInstall) { hr = HrRestoreRegistry(); if (FAILED(hr)) { TraceError("CNWClient::ApplyRegistryChanges - HrRestoreRegistry non-fatal error", hr); hr = S_OK; } hr = HrWriteAnswerFileParams(); if (FAILED(hr)) { TraceError("CNWClient::ApplyRegistryChanges - HrWriteAnswerFileParams " "non-fatal error", hr); hr = S_OK; } // If gateway is enabled, modify lanmanserver appropriately // Ignore the return code other than to trace it. // hr = HrEnableGatewayIfNeeded(); if (FAILED(hr)) { TraceError("CNWClient::ApplyRegistryChanges - HrEnableGatewayIfNeeded non-fatal error", hr); } hr = HrInstallCodeFromOldINF(); } Validate_INetCfgNotify_Apply_Return(hr); TraceError("CNWClient::ApplyRegistryChanges", (hr == S_FALSE) ? S_OK : hr); return hr; } STDMETHODIMP CNWClient::ApplyPnpChanges ( INetCfgPnpReconfigCallback* pICallback) { HRESULT hr; hr = HrRefreshEntireNetwork(); if (FAILED(hr)) { TraceError("CNWClient::ApplyPnpChanges - HrRefreshEntireNetwork" "non-fatal error", hr); hr = S_OK; } // GlennC can't do the work to make NW Client PnP so we're forced to // prompt for a reboot for any change. // return NETCFG_S_REBOOT; } // Note -- Don't convert this to a constant. We need copies of it within the // functions because ParseDisplayName actually mangles the string. // #define ENTIRE_NETWORK_PATH L"::{208D2C60-3AEA-1069-A2D7-08002B30309D}\\EntireNetwork" //+--------------------------------------------------------------------------- // // Function: HrGetEntireNetworkPidl // // Purpose: Get the pidl for "Entire Network". Used in places where we're // not folder specific, but we still need to update folder // entries. // // Arguments: // ppidlFolder [out] Return parameter for the folder pidl // // Returns: // // Author: anbrad 08 Jun 1999 // jeffspr 13 Jun 1998 // // Notes: // HRESULT HrGetEntireNetworkPidl(LPITEMIDLIST *ppidlFolder) { HRESULT hr = S_OK; LPSHELLFOLDER pshf = NULL; LPITEMIDLIST pidlFolder = NULL; Assert(ppidlFolder); WCHAR szEntireNetworkPath[] = ENTIRE_NETWORK_PATH; // Get the desktop folder, so we can parse the display name and get // the UI object of the connections folder // hr = SHGetDesktopFolder(&pshf); if (SUCCEEDED(hr)) { ULONG chEaten; hr = pshf->ParseDisplayName(NULL, 0, (WCHAR *) szEntireNetworkPath, &chEaten, &pidlFolder, NULL); ReleaseObj(pshf); } // If succeeded, fill in the return param. // if (SUCCEEDED(hr)) { *ppidlFolder = pidlFolder; } else { // If we failed, then delete the pidl if we already got it. // if (pidlFolder) SHFree(pidlFolder); } TraceHr(ttidNWClientCfg, FAL, hr, FALSE, "HrGetEntireNetworkPidl"); return hr; } //+--------------------------------------------------------------------------- // // Function: HrRefreshEntireNetwork // // Purpose: Update the "Entire Network" portion of the shell due to // the addition of a new networking client (NWClient) // // Arguments: // (none) // // Returns: // // Author: anbrad 08 Jun 1999 // // Notes: // HRESULT HrRefreshEntireNetwork() { HRESULT hr = S_OK; HCURSOR hcWait = SetCursor(LoadCursor(NULL, IDC_WAIT)); LPITEMIDLIST pidlFolder = NULL;; hr = HrGetEntireNetworkPidl(&pidlFolder); // If we now have a pidl, send the GenerateEvent to update the item // if (SUCCEEDED(hr)) { Assert(pidlFolder); // SHCNE_UPDATEDIR?ITEM GenerateEvent(SHCNE_UPDATEDIR, pidlFolder, NULL, NULL); } if (hcWait) { SetCursor(hcWait); } if (pidlFolder) { SHFree(pidlFolder); } TraceHr(ttidError, FAL, hr, FALSE, "HrRefreshEntireNetwork"); return hr; } //+--------------------------------------------------------------------------- // // Function: HrEnableGatewayIfNeeded // // Purpose: Update the Lanman dependencies, if appropriate (meaning if // gateway is enabled). // // Arguments: // (none) // // Returns: // // Author: jeffspr 19 Aug 1999 // // Notes: // HRESULT CNWClient::HrEnableGatewayIfNeeded() { HRESULT hr = S_OK; HKEY hKey = NULL; DWORD dwValue = 0; CServiceManager sm; CService svc; hr = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szNWClientParamPath, KEY_READ, &hKey); if (FAILED(hr)) { TraceError("Couldn't open NWClient param key", hr); goto Exit; } hr = HrRegQueryDword(hKey, c_szGWEnabledValue, &dwValue); if (FAILED(hr)) { if (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) { TraceError("Couldn't query the GWEnabled Value", hr); goto Exit; } else { dwValue = 0; } } else { // Normalize to bool // dwValue = !!dwValue; } RegSafeCloseKey(hKey); hKey = NULL; // If there are gateway services present, then add the dependencies // to LanmanServer // if (dwValue > 0) { // Set the value in the registry for the server paramaters. // hr = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szLMServerParamPath, KEY_WRITE, &hKey); if (SUCCEEDED(hr)) { hr = HrRegSetDword(hKey, c_szEnableSharedNetDrives, dwValue); RegSafeCloseKey(hKey); hKey = NULL; } hr = sm.HrOpen(); if (SUCCEEDED(hr)) { hr = sm.HrOpenService(&svc, c_szSvcLmServer, NO_LOCK); if (SUCCEEDED(hr)) { // Add dependency of NWC Workstation to Server // hr = sm.HrAddServiceDependency(c_szSvcLmServer, c_szSvcNWCWorkstation); if (SUCCEEDED(hr)) { hr = HrRegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szLMServerLinkagePath, KEY_READ | KEY_WRITE, &hKey); if (SUCCEEDED(hr)) { // Add the "OtherDependencies" to LanmanServer for legacy reasons // hr = HrRegAddStringToMultiSz(c_szSvcNWCWorkstation, hKey, NULL, c_szOtherDependencies, STRING_FLAG_ENSURE_AT_END | STRING_FLAG_DONT_MODIFY_IF_PRESENT, 0); RegSafeCloseKey(hKey); hKey = NULL; } } } else { TraceError("Failed to open LanmanServer service for dependency mods", hr); } } else { TraceError("Failed to open service control manager", hr); } } Exit: TraceHr(ttidNWClientCfg, FAL, hr, FALSE, "HrEnableGatewayIfNeeded"); return hr; }