//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1997. // // File: E N U M . C P P // // Contents: Implementation of LAN connection enumerator object // // Notes: // // Author: danielwe 2 Oct 1997 // //---------------------------------------------------------------------------- #include "pch.h" #pragma hdrstop #include "enuml.h" #include "lan.h" #include "lancmn.h" #include "ncnetcfg.h" #include "ncreg.h" #include "ncsetup.h" LONG g_CountLanConnectionEnumerators; HRESULT CLanConnectionManagerEnumConnection::CreateInstance( NETCONMGR_ENUM_FLAGS Flags, REFIID riid, LPVOID *ppv) { HRESULT hr = E_OUTOFMEMORY; CLanConnectionManagerEnumConnection* pObj; Assert(ppv); *ppv = NULL; pObj = new CComObject; if (pObj) { // Do the standard CComCreator::CreateInstance stuff. // pObj->SetVoid(NULL); pObj->InternalFinalConstructAddRef(); hr = pObj->FinalConstruct(); pObj->InternalFinalConstructRelease(); if (SUCCEEDED(hr)) { hr = pObj->QueryInterface(riid, ppv); } if (FAILED(hr)) { delete pObj; } } TraceError("CLanConnectionManagerEnumConnection::CreateInstance", hr); return hr; } //+--------------------------------------------------------------------------- // // Member: CLanConnectionManagerEnumConnection::~CLanConnectionManagerEnumConnection // // Purpose: Called when the enumeration object is released for the last // time. // // Arguments: // (none) // // Returns: Nothing // // Author: danielwe 2 Oct 1997 // // Notes: // CLanConnectionManagerEnumConnection::~CLanConnectionManagerEnumConnection() { SetupDiDestroyDeviceInfoListSafe(m_hdi); InterlockedDecrement(&g_CountLanConnectionEnumerators); } //+--------------------------------------------------------------------------- // IEnumNetConnection // //+--------------------------------------------------------------------------- // // Member: CLanConnectionManagerEnumConnection::Next // // Purpose: Retrieves the next celt LAN connection objects // // Arguments: // celt [in] Number to retrieve // rgelt [out] Array of INetConnection objects retrieved // pceltFetched [out] Returns Number in array // // Returns: S_OK if succeeded, OLE or Win32 error otherwise // // Author: danielwe 2 Oct 1997 // // Notes: // STDMETHODIMP CLanConnectionManagerEnumConnection::Next(ULONG celt, INetConnection **rgelt, ULONG *pceltFetched) { HRESULT hr = S_OK; // Validate parameters. // if (!rgelt || (!pceltFetched && (1 != celt))) { hr = E_POINTER; goto done; } // Initialize output parameters. // if (pceltFetched) { *pceltFetched = 0; } // Handle the request for zero elements. Also do nothing if the enumerator // was created without valid parameters. // if (0 == celt || FIsDebugFlagSet(dfidSkipLanEnum)) { hr = S_FALSE; goto done; } hr = HrNextOrSkip(celt, rgelt, pceltFetched); done: TraceError("CLanConnectionManagerEnumConnection::Next", (hr == S_FALSE) ? S_OK : hr); return hr; } //+--------------------------------------------------------------------------- // // Member: CLanConnectionManagerEnumConnection::Skip // // Purpose: Skips over celt number of connections // // Arguments: // celt [in] Number of connections to skip // // Returns: S_OK if successful, otherwise Win32 error // // Author: danielwe 2 Oct 1997 // // Notes: // STDMETHODIMP CLanConnectionManagerEnumConnection::Skip(ULONG celt) { HRESULT hr = S_OK; hr = HrNextOrSkip(celt, NULL, NULL); TraceError("CLanConnectionManagerEnumConnection::Skip", (hr == S_FALSE) ? S_OK : hr); return hr; } //+--------------------------------------------------------------------------- // // Member: CLanConnectionManagerEnumConnection::Reset // // Purpose: Resets the enumerator to the beginning // // Arguments: // (none) // // Returns: S_OK // // Author: danielwe 2 Oct 1997 // // Notes: // STDMETHODIMP CLanConnectionManagerEnumConnection::Reset() { HRESULT hr; m_dwIndex = 0; // refresh so that we have a new view of what adapters are installed // each time reset is called // SetupDiDestroyDeviceInfoListSafe(m_hdi); hr = HrSetupDiGetClassDevs(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, &m_hdi); TraceError("CLanConnectionManagerEnumConnection::Reset", hr); return hr; } //+--------------------------------------------------------------------------- // // Member: CLanConnectionManagerEnumConnection::Clone // // Purpose: Creates a new enumeration object pointing at the same location // as this object // // Arguments: // ppenum [out] New enumeration object // // Returns: S_OK if successful, otherwise OLE or Win32 error // // Author: danielwe 19 Mar 1998 // // Notes: // STDMETHODIMP CLanConnectionManagerEnumConnection::Clone(IEnumNetConnection **ppenum) { HRESULT hr = E_OUTOFMEMORY; // Validate parameters. // if (!ppenum) { hr = E_POINTER; } else { CLanConnectionManagerEnumConnection * pObj; // Initialize output parameter. // *ppenum = NULL; pObj = new CComObject ; if (pObj) { hr = S_OK; CExceptionSafeComObjectLock EsLock (this); // Copy our internal state. // pObj->m_dwIndex = m_dwIndex; // Return the object with a ref count of 1 on this // interface. pObj->m_dwRef = 1; *ppenum = pObj; } } TraceError ("CLanConnectionManagerEnumConnection::Clone", hr); return hr; } // // Helper functions // //+--------------------------------------------------------------------------- // // Member: CLanConnectionManagerEnumConnection::HrCreateLanConnectionInstance // // Purpose: Helper function to create a LAN connection object instance // // Arguments: // deid [in] Device info data // rgelt [in] Array of connection objects // ulEntry [in] Index of connection object // // Returns: S_OK if success, Win32 or OLE error otherwise // // Author: danielwe 8 Jan 1998 // // Notes: // HRESULT CLanConnectionManagerEnumConnection::HrCreateLanConnectionInstance( SP_DEVINFO_DATA &deid, INetConnection **rgelt, ULONG ulEntry) { HRESULT hr; WCHAR szPnpId[MAX_DEVICE_ID_LEN]; hr = HrSetupDiGetDeviceInstanceId(m_hdi, &deid, szPnpId, MAX_DEVICE_ID_LEN, NULL); if (S_OK == hr) { HDEVINFO hdiCopy; SP_DEVINFO_DATA deidCopy; hr = HrSetupDiCreateDeviceInfoList(&GUID_DEVCLASS_NET, NULL, &hdiCopy); if (S_OK == hr) { BOOL fDestroyCopy = TRUE; hr = HrSetupDiOpenDeviceInfo(hdiCopy, szPnpId, NULL, DIOD_INHERIT_CLASSDRVS, &deidCopy); if (S_OK == hr) { fDestroyCopy = FALSE; hr = CLanConnection::CreateInstance(hdiCopy, deidCopy, szPnpId, IID_INetConnection, reinterpret_cast (rgelt + ulEntry)); } // CLanConnection::CreateInstance() will hand off the hdiCopy. So // even if that fails, we don't want to destroy hdiCopy anymore. // if (fDestroyCopy) { // If we fail to continue, free the copy we just made // (VOID) SetupDiDestroyDeviceInfoList(hdiCopy); } } } TraceError("CLanConnectionManagerEnumConnection::" "HrCreateLanConnectionInstance", hr); return hr; } //+--------------------------------------------------------------------------- // // Function: FIsHidden // // Purpose: Returns TRUE if the given hkey references the device instance // of a hidden adapter (virtual or otherwise) // // Arguments: // hkey [in] HKEY of device instance for adapter (i.e. {GUID}\0000) // // Returns: TRUE if it is hidden, FALSE if not // // Author: danielwe 17 Apr 1998 // // Notes: // BOOL FIsHidden(HKEY hkey) { DWORD dwCharacter; if (S_OK == HrRegQueryDword(hkey, L"Characteristics", &dwCharacter)) { return !!(dwCharacter & NCF_HIDDEN); } return FALSE; } //+--------------------------------------------------------------------------- // // Function: FIsHiddenElan // // Purpose: Returns TRUE if the given hkey references the device instance // of a hidden ELAN adapter (when the physical ATM adapter is not // available) // // Arguments: // hdi [in] HDEVINFO structure for this adapter // hkey [in] HKEY of device instance for adapter (i.e. {GUID}\0000) // // // Returns: TRUE if it is hidden, FALSE if not // // Author: tongl 9/10/98 // // Notes: // BOOL FIsHiddenElan(HDEVINFO hdi, HKEY hkey) { BOOL fRet = FALSE; HRESULT hr; PWSTR pszAtmAdapterPnpId; hr = HrRegQuerySzWithAlloc(hkey, L"AtmAdapterPnpId", &pszAtmAdapterPnpId); if (S_OK == hr) { SP_DEVINFO_DATA deid; hr = HrSetupDiOpenDeviceInfo(hdi, pszAtmAdapterPnpId, NULL, 0, &deid); if (S_OK == hr) { // Elan should be hidden if the physical adapter is not functioning // and is hidden in the folder fRet = !FIsFunctioning(&deid); } MemFree(pszAtmAdapterPnpId); } return fRet; } //+--------------------------------------------------------------------------- // // Member: CLanConnectionManagerEnumConnection::HrNextOrSkip // // Purpose: Helper function to handle the ::Next or ::Skip method // implementation // // Arguments: // celt [in] Number of items to advance // rgelt [in] Array in which to place connection objects // pceltFetched [out] Returns number of items fetched // // Returns: S_OK if success, OLE or Win32 error otherwise // // Author: danielwe 8 Jan 1998 // // Notes: // HRESULT CLanConnectionManagerEnumConnection::HrNextOrSkip( ULONG celt, INetConnection **rgelt, ULONG *pceltFetched) { HRESULT hr = S_OK; SP_DEVINFO_DATA deid = {0}; ULONG ulEntry = 0; if (rgelt) { // Important to initialize rgelt so that in case we fail, we can // release only what we put in rgelt. // ZeroMemory(rgelt, sizeof (*rgelt) * celt); } Assert(celt > 0); if (!m_hdi) { hr = HrSetupDiGetClassDevs(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, &m_hdi); } while (celt && SUCCEEDED(hr) && SUCCEEDED(hr = HrSetupDiEnumDeviceInfo(m_hdi,m_dwIndex, &deid))) { HKEY hkey; m_dwIndex++; hr = HrSetupDiOpenDevRegKey(m_hdi, &deid, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ, &hkey); if (SUCCEEDED(hr)) { hr = HrIsLanCapableAdapterFromHkey(hkey); if (S_OK == hr) { if (FIsFunctioning(&deid) && FIsValidNetCfgDevice(hkey) && !FIsHidden(hkey) && !FIsHiddenElan(m_hdi, hkey)) { // On Skip, don't create an instance // if (rgelt) { hr = HrCreateLanConnectionInstance(deid, rgelt, ulEntry); } ulEntry++; celt--; } } RegCloseKey(hkey); } else { // skip device entirely if error trying to open it hr = S_OK; } } if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr) { hr = S_OK; } if (SUCCEEDED(hr)) { TraceTag (ttidLanCon, "Enumerated %lu LAN connections", ulEntry); if (pceltFetched) { *pceltFetched = ulEntry; } // If celt is positive then we couldn't satisfy the request completely hr = (celt > 0) ? S_FALSE : S_OK; } else { // For any failures, we need to release what we were about to return. // Set any output parameters to NULL. // if (rgelt) { for (ULONG ulIndex = 0; ulIndex < ulEntry; ulIndex++) { ReleaseObj(rgelt[ulIndex]); rgelt[ulIndex] = NULL; } } if (pceltFetched) { *pceltFetched = 0; } } TraceError("CLanConnectionManagerEnumConnection::HrNextOrSkip", (hr == S_FALSE) ? S_OK : hr); return hr; } // // Private helper functions // extern const WCHAR c_szRegKeyInterfacesFromInstance[]; extern const WCHAR c_szRegValueUpperRange[]; extern const WCHAR c_szRegValueLowerRange[]; static const WCHAR c_chComma = L','; extern const WCHAR c_szBiNdis4[]; extern const WCHAR c_szBiNdis5[]; extern const WCHAR c_szBiNdis5Ip[]; extern const WCHAR c_szBiNdisAtm[]; extern const WCHAR c_szBiNdis1394[]; extern const WCHAR c_szBiNdisBda[]; extern const WCHAR c_szBiLocalTalk[]; //+--------------------------------------------------------------------------- // // Function: HrIsLanCapableAdapterFromHkey // // Purpose: Determines if the given HKEY describes a LAN capable adapter // // Arguments: // hkey [in] HKEY under Control\Class\{GUID}\ (aka driver key) // // Returns: S_OK if device is LAN capable, S_FALSE if not, Win32 error // otherwise // // Author: danielwe 7 Jan 1998 // // Notes: // HRESULT HrIsLanCapableAdapterFromHkey(HKEY hkey) { HRESULT hr = S_OK; WCHAR szBuf[256]; DWORD cbBuf = sizeof(szBuf); list lstr; list::iterator lstrIter; BOOL fMatch = FALSE; HKEY hkeyInterfaces; hr = HrRegOpenKeyEx(hkey, c_szRegKeyInterfacesFromInstance, KEY_READ, &hkeyInterfaces); if (SUCCEEDED(hr)) { hr = HrRegQuerySzBuffer(hkeyInterfaces, c_szRegValueUpperRange, szBuf, &cbBuf); if (SUCCEEDED(hr)) { ConvertStringToColString(szBuf, c_chComma, lstr); for (lstrIter = lstr.begin(); lstrIter != lstr.end(); lstrIter++) { // See if it matches one of these if (!lstrcmpiW((*lstrIter)->c_str(), c_szBiNdis4) || !lstrcmpiW((*lstrIter)->c_str(), c_szBiNdis5) || !lstrcmpiW((*lstrIter)->c_str(), c_szBiNdis5Ip) || !lstrcmpiW((*lstrIter)->c_str(), c_szBiNdisAtm) || !lstrcmpiW((*lstrIter)->c_str(), c_szBiNdis1394) || !lstrcmpiW((*lstrIter)->c_str(), c_szBiNdisBda)) { fMatch = TRUE; break; } } DeleteColString(&lstr); } if (!fMatch) { cbBuf = sizeof(szBuf); hr = HrRegQuerySzBuffer(hkeyInterfaces, c_szRegValueLowerRange, szBuf, &cbBuf); if (SUCCEEDED(hr)) { ConvertStringToColString(szBuf, c_chComma, lstr); for (lstrIter = lstr.begin(); lstrIter != lstr.end(); lstrIter++) { // See if it matches one of these if (!lstrcmpiW((*lstrIter)->c_str(), c_szBiLocalTalk)) { fMatch = TRUE; break; } } DeleteColString(&lstr); } } RegCloseKey(hkeyInterfaces); } else if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) { hr = S_OK; } if (SUCCEEDED(hr)) { if (fMatch) { hr = S_OK; } else { hr = S_FALSE; } } TraceHr (ttidError, FAL, hr, S_FALSE == hr, "HrIsLanCapableAdapterFromHkey"); return hr; } static const WCHAR c_szKeyFmt[] = L"%s\\%s\\%s"; extern const WCHAR c_szRegValueNetCfgInstanceId[]; extern const WCHAR c_szRegKeyComponentClasses[]; extern const WCHAR c_szRegValueInstallerAction[]; //+--------------------------------------------------------------------------- // // Function: FIsValidNetCfgDevice // // Purpose: Determines if the given HKEY is that of a valid NetCfg adapter // // Arguments: // hkey [in] HKEY under Control\Class\{GUID}\ (aka driver key) // // Returns: TRUE if valid, FALSE otherwise // // Author: danielwe 7 Jan 1998 // // Notes: // BOOL FIsValidNetCfgDevice(HKEY hkey) { HRESULT hr; WCHAR szGuid[c_cchGuidWithTerm + 1]; DWORD cbBuf = sizeof(szGuid); hr = HrRegQuerySzBuffer(hkey, c_szRegValueNetCfgInstanceId, szGuid, &cbBuf); return (S_OK == hr); } //+--------------------------------------------------------------------------- // // Function: FIsFunctioning // // Purpose: Determines if the given dev node is a functioning device // // Arguments: // pdeid [in] Dev info data for device // // Returns: TRUE if device is functioning, FALSE if not // // Author: danielwe 2 Sep 1998 // // Notes: "Functioning" means the device is enabled and started with // no problem codes, or it is disabled and stopped with no // problem codes. // BOOL FIsFunctioning(SP_DEVINFO_DATA * pdeid) { ULONG ulStatus; ULONG ulProblem; CONFIGRET cfgRet; cfgRet = CM_Get_DevNode_Status_Ex(&ulStatus, &ulProblem, pdeid->DevInst, 0, NULL); if (CR_SUCCESS == cfgRet) { TraceTag(ttidLanCon, "CM_Get_DevNode_Status_Ex (enum): ulProblem " "= 0x%08X, ulStatus = 0x%08X.", ulProblem, ulStatus); return FIsDeviceFunctioning(ulProblem); } // By default return FALSE return FALSE; }