//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1999. // // File: R E G B I N D . C P P // // Contents: This module is responsible for writing bindings to the // registry so that they may be consumed by NDIS and TDI. // // Notes: // // Author: shaunco 1 Feb 1999 // //---------------------------------------------------------------------------- #include "pch.h" #pragma hdrstop #include "filtdevs.h" #include "lanamap.h" #include "netcfg.h" #include "ncreg.h" #include "ndispnp.h" HRESULT HrRegSetMultiSzAndLogDifference ( IN HKEY hkey, IN PCWSTR pszValueName, IN PCWSTR pmszValue, IN const CComponent* pComponent ) { // Only log the difference if we're operating under the appropriate // diagnostic context. // if (g_pDiagCtx->Flags() & DF_REPAIR_REGISTRY_BINDINGS) { HRESULT hr; DWORD cbCurrent; PWSTR pmszCurrent = (PWSTR)g_pDiagCtx->GetScratchBuffer(&cbCurrent); // Read the current value into the diagnostic context's scratch // buffer. // hr = HrRegQueryTypeSzBuffer (hkey, pszValueName, REG_MULTI_SZ, pmszCurrent, &cbCurrent); // Grow the scratch buffer and retry if the value is bigger than // than will fit. // if ((HRESULT_FROM_WIN32(ERROR_MORE_DATA) == hr) || ((NULL == pmszCurrent) && (S_OK == hr))) { pmszCurrent = (PWSTR)g_pDiagCtx->GrowScratchBuffer(&cbCurrent); if (pmszCurrent) { hr = HrRegQueryTypeSzBuffer (hkey, pszValueName, REG_MULTI_SZ, pmszCurrent, &cbCurrent); } else { hr = E_OUTOFMEMORY; } } if (S_OK == hr) { DWORD cbValue = CbOfMultiSzAndTermSafe(pmszValue); // Compare the values and log if they are different. // if ((cbValue != cbCurrent) || (memcmp(pmszValue, pmszCurrent, cbCurrent))) { FILE *LogFile = g_pDiagCtx->LogFile(); fprintf(LogFile, "reset Linkage\\%S for %S. bad value was:\n", pszValueName, pComponent->PszGetPnpIdOrInfId()); fprintf(LogFile, " REG_MULTI_SZ =\n"); if (*pmszCurrent) { while (*pmszCurrent) { fprintf(LogFile, " %S\n", pmszCurrent); pmszCurrent += wcslen(pmszCurrent) + 1; } } else { fprintf(LogFile, " \n"); } fprintf(LogFile, "\n"); } else { // The value is correct. No need to write it. // return S_OK; } } else if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) { fprintf(g_pDiagCtx->LogFile(), "added Linkage\\%S for %S\n", pszValueName, pComponent->PszGetPnpIdOrInfId()); } } // N.B. success or failure of the diagnostic portion of this routine // (above) should NOT affect the return value of this routine. // return HrRegSetMultiSz (hkey, pszValueName, pmszValue); } HRESULT HrCreateLinkageKey ( IN const CComponent* pComponent, IN CFilterDevice* pDevice, IN HDEVINFO hdi, OUT HKEY* phKey) { HRESULT hr = E_UNEXPECTED; HKEY hkeyParent = NULL; CONST REGSAM samDesired = KEY_READ | KEY_WRITE; Assert (pComponent || pDevice); Assert (!(pComponent && pDevice)); Assert (FIff(pDevice, hdi)); Assert (phKey); if (pComponent) { // Open the parent of the linkage key. This is the instance key if // the component is enumerated or does not have a service. // if (FIsEnumerated (pComponent->Class()) || !pComponent->FHasService()) { hr = pComponent->HrOpenInstanceKey (samDesired, &hkeyParent, NULL, NULL); if ((S_OK == hr) && FIsEnumerated (pComponent->Class())) { // Write out the netcfg instance id. Connections will use // this to determine if the device is known by net config // and will create the key under network // to store its connection info. We only need to do this // for enumerated components. // hr = HrRegSetGuidAsSz (hkeyParent, L"NetCfgInstanceId", pComponent->m_InstanceGuid); } } else { hr = pComponent->HrOpenServiceKey (samDesired, &hkeyParent); } } else { Assert (pDevice); Assert (hdi); hr = HrSetupDiOpenDevRegKey ( hdi, &pDevice->m_deid, DICS_FLAG_GLOBAL, 0, DIREG_DRV, samDesired, &hkeyParent); } if (S_OK == hr) { Assert (hkeyParent); hr = HrRegCreateKeyEx ( hkeyParent, L"Linkage", REG_OPTION_NON_VOLATILE, samDesired, NULL, phKey, NULL); RegCloseKey (hkeyParent); } TraceHr (ttidError, FAL, hr, (SPAPI_E_NO_SUCH_DEVINST == hr), "HrCreateLinkageKey"); return hr; } HRESULT HrWriteLinkageValues ( IN const CComponent* pComponent, IN PCWSTR pmszBind, IN PCWSTR pmszExport, IN PCWSTR pmszRoute) { HRESULT hr; HKEY hkeyLinkage; PCWSTR pmsz; Assert (pmszBind); Assert (pmszExport); Assert (pmszRoute); g_pDiagCtx->Printf (ttidBeDiag, " %S (%S)\n", pComponent->Ext.PszBindName(), pComponent->PszGetPnpIdOrInfId()); if (FIsEnumerated (pComponent->Class())) { g_pDiagCtx->Printf (ttidBeDiag, " UpperBind:\n"); } else { g_pDiagCtx->Printf (ttidBeDiag, " Bind:\n"); } pmsz = pmszBind; while (*pmsz) { g_pDiagCtx->Printf (ttidBeDiag, " %S\n", pmsz); pmsz += wcslen (pmsz) + 1; } g_pDiagCtx->Printf (ttidBeDiag, " Export:\n"); pmsz = pmszExport; while (*pmsz) { g_pDiagCtx->Printf (ttidBeDiag, " %S\n", pmsz); pmsz += wcslen (pmsz) + 1; } g_pDiagCtx->Printf (ttidBeDiag, "\n"); hr = HrCreateLinkageKey (pComponent, NULL, NULL, &hkeyLinkage); if (S_OK == hr) { // For enumerated components, write RootDevice, UpperBind, and Export. // For non-enumerated components, write Bind and Export. // if (FIsEnumerated (pComponent->Class())) { // Create the root device multi-sz from the bindname. // WCHAR mszRootDevice [_MAX_PATH]; wcscpy (mszRootDevice, pComponent->Ext.PszBindName()); mszRootDevice [wcslen(mszRootDevice) + 1] = 0; hr = HrRegSetMultiSzAndLogDifference ( hkeyLinkage, L"RootDevice", mszRootDevice, pComponent); if (S_OK == hr) { hr = HrRegSetMultiSzAndLogDifference ( hkeyLinkage, L"UpperBind", pmszBind, pComponent); } } else { hr = HrRegSetMultiSzAndLogDifference ( hkeyLinkage, L"Bind", pmszBind, pComponent); if (S_OK == hr) { hr = HrRegSetMultiSzAndLogDifference ( hkeyLinkage, L"Route", pmszRoute, pComponent); } } if ((S_OK == hr) && *pmszExport) { hr = HrRegSetMultiSzAndLogDifference ( hkeyLinkage, L"Export", pmszExport, pComponent); } RegCloseKey (hkeyLinkage); } TraceHr (ttidError, FAL, hr, (SPAPI_E_NO_SUCH_DEVINST == hr), "HrWriteLinkageValues"); return hr; } HRESULT HrWriteFilterDeviceLinkage ( IN CFilterDevice* pDevice, IN HDEVINFO hdi, IN PCWSTR pmszExport, IN PCWSTR pmszRootDevice, IN PCWSTR pmszUpperBind) { HRESULT hr; HKEY hkeyLinkage; PCWSTR pmsz; g_pDiagCtx->Printf (ttidBeDiag, " %S filter over %S adapter\n", pDevice->m_pFilter->m_pszInfId, pDevice->m_pAdapter->m_pszPnpId); g_pDiagCtx->Printf (ttidBeDiag, " Export:\n"); pmsz = pmszExport; while (*pmsz) { g_pDiagCtx->Printf (ttidBeDiag, " %S\n", pmsz); pmsz += wcslen (pmsz) + 1; } g_pDiagCtx->Printf (ttidBeDiag, " RootDevice:\n"); pmsz = pmszRootDevice; while (*pmsz) { g_pDiagCtx->Printf (ttidBeDiag, " %S\n", pmsz); pmsz += wcslen (pmsz) + 1; } g_pDiagCtx->Printf (ttidBeDiag, " UpperBind:\n"); pmsz = pmszUpperBind; while (*pmsz) { g_pDiagCtx->Printf (ttidBeDiag, " %S\n", pmsz); pmsz += wcslen (pmsz) + 1; } g_pDiagCtx->Printf (ttidBeDiag, "\n"); hr = HrCreateLinkageKey (NULL, pDevice, hdi, &hkeyLinkage); if (S_OK == hr) { hr = HrRegSetMultiSz (hkeyLinkage, L"Export", pmszExport); if (S_OK == hr) { hr = HrRegSetMultiSz (hkeyLinkage, L"RootDevice", pmszRootDevice); } if (S_OK == hr) { hr = HrRegSetMultiSz (hkeyLinkage, L"UpperBind", pmszUpperBind); } // Delete values used by the previous binding engine that are // not needed any longer. // RegDeleteValue (hkeyLinkage, L"BindPath"); RegDeleteValue (hkeyLinkage, L"Bind"); RegDeleteValue (hkeyLinkage, L"Route"); RegDeleteKey (hkeyLinkage, L"Disabled"); RegCloseKey (hkeyLinkage); } // Now write to the standard filter parameter registry layout under // the filter's service key. // if (pDevice->m_pFilter->Ext.PszService()) { HKEY hkeyAdapterParams; WCHAR szRegPath [_MAX_PATH]; Assert (pDevice->m_pFilter->Ext.PszService()); Assert (pDevice->m_pAdapter->Ext.PszBindName()); wsprintfW ( szRegPath, L"System\\CurrentControlSet\\Services\\%s\\Parameters\\Adapters\\%s", pDevice->m_pFilter->Ext.PszService(), pDevice->m_pAdapter->Ext.PszBindName()); hr = HrRegCreateKeyEx ( HKEY_LOCAL_MACHINE, szRegPath, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkeyAdapterParams, NULL); if (S_OK == hr) { // UpperBindings is a REG_SZ, not a REG_MULTI_SZ. // hr = HrRegSetSz (hkeyAdapterParams, L"UpperBindings", pmszExport); RegCloseKey (hkeyAdapterParams); } } TraceHr (ttidError, FAL, hr, FALSE, "HrWriteFilterDeviceLinkage"); return hr; } HRESULT HrWriteFilteredAdapterUpperBind ( IN const CComponent* pAdapter, IN PCWSTR pmszUpperBind) { HRESULT hr; HKEY hkeyLinkage; hr = HrCreateLinkageKey (pAdapter, NULL, NULL, &hkeyLinkage); if (S_OK == hr) { hr = HrRegSetMultiSz (hkeyLinkage, L"UpperBind", pmszUpperBind); RegCloseKey (hkeyLinkage); } TraceHr (ttidError, FAL, hr, FALSE, "HrWriteFilteredAdapterUpperBind"); return hr; } HRESULT CRegistryBindingsContext::HrPrepare ( IN CNetConfig* pNetConfig) { HRESULT hr; Assert (pNetConfig); m_pNetConfig = pNetConfig; hr = m_BindValue.HrReserveBytes (4096); if (S_OK != hr) { return hr; } hr = m_ExportValue.HrReserveBytes (4096); if (S_OK != hr) { return hr; } hr = m_RouteValue.HrReserveBytes (4096); if (S_OK != hr) { return hr; } // Ensure all of the external data for all components is loaded. // hr = m_pNetConfig->HrEnsureExternalDataLoadedForAllComponents (); if (S_OK != hr) { return hr; } // Ensure all of the notify objects have been initialized. // hr = m_pNetConfig->Notify.HrEnsureNotifyObjectsInitialized (); if (S_OK != hr) { return hr; } return S_OK; } HRESULT CRegistryBindingsContext::HrDeleteBindingsForComponent ( IN const CComponent* pComponent) { return HrWriteLinkageValues (pComponent, L"", L"", L""); } HRESULT CRegistryBindingsContext::HrGetAdapterUpperBindValue ( IN const CComponent* pAdapter) { HRESULT hr; const CBindPath* pBindPath; m_BindValue.Clear(); // Get the upper bindings of the component. This returns a bindset // with binpaths only 2 levels deep. That is, the bindpaths begin // with the components one level above pComponent. // hr = m_pNetConfig->Core.HrGetComponentUpperBindings ( pAdapter, GBF_PRUNE_DISABLED_BINDINGS, &m_BindSet); if (S_OK == hr) { for (pBindPath = m_BindSet.begin(); pBindPath != m_BindSet.end(); pBindPath++) { // Don't put filters in the UpperBind of an adapter. // if (pBindPath->POwner()->FIsFilter()) { continue; } hr = m_BindValue.HrCopyString ( pBindPath->POwner()->Ext.PszBindName()); if (S_OK != hr) { break; } } hr = m_BindValue.HrCopyString (L""); } TraceHr (ttidError, FAL, hr, FALSE, "CRegistryBindingsContext::HrGetAdapterUpperBindValue"); return hr; } HRESULT CRegistryBindingsContext::HrWriteBindingsForComponent ( IN const CComponent* pComponent) { HRESULT hr; const CBindPath* pBindPath; CBindPath::const_iterator iter; const CComponent* pUpper; const CComponent* pLower; WCHAR szBind [_MAX_BIND_LENGTH]; WCHAR szExport [_MAX_BIND_LENGTH]; WCHAR szRoute [_MAX_BIND_LENGTH]; PWCHAR pchBind; PWCHAR pchExport; Assert (pComponent); pComponent->Ext.DbgVerifyExternalDataLoaded (); // If the component is not bindable, we have nothing to do. // if (!pComponent->FIsBindable()) { return S_OK; } m_BindValue.Clear (); m_ExportValue.Clear (); m_RouteValue.Clear (); wcscpy (szExport, L"\\Device\\"); wcscat (szExport, pComponent->Ext.PszBindName()); hr = m_ExportValue.HrCopyString (szExport); Assert (S_OK == hr); hr = m_ExportValue.HrCopyString (L""); Assert (S_OK == hr); if (FIsEnumerated (pComponent->Class())) { // UpperBind // hr = HrGetAdapterUpperBindValue (pComponent); } else { // Bind, Export // hr = m_pNetConfig->Core.HrGetComponentBindings ( pComponent, GBF_PRUNE_DISABLED_BINDINGS, &m_BindSet); if ((S_OK == hr) && (m_BindSet.CountBindPaths() > 0)) { // Since the component has bindings, it's export value will be // different from the default one we initialized with above. // m_ExportValue.Clear (); for (pBindPath = m_BindSet.begin(); pBindPath != m_BindSet.end(); pBindPath++) { Assert (pBindPath->CountComponents() > 1); wcscpy (szBind, L"\\Device\\"); wcscpy (szExport, L"\\Device\\"); *szRoute = 0; for (iter = pBindPath->begin(); iter != pBindPath->end(); iter++) { pUpper = *iter; Assert (pUpper); // For the bind value, skip the first component in each // path because it is the component we are writing the // bindings for. // if (iter != pBindPath->begin()) { Assert (wcslen(szBind) + 1 + wcslen(pUpper->Ext.PszBindName()) < celems(szBind)); // If this isn't the first component to come after // \Device\, add underscores to seperate the // components. // if (iter != (pBindPath->begin() + 1)) { wcscat (szBind, L"_"); wcscat (szRoute, L" "); } wcscat (szBind, pUpper->Ext.PszBindName()); wcscat (szRoute, L"\""); wcscat (szRoute, pUpper->Ext.PszBindName()); wcscat (szRoute, L"\""); } Assert (wcslen(szExport) + 1 + wcslen(pUpper->Ext.PszBindName()) < celems(szExport)); // If this isn't the first component to come after // \Device\, add underscores to seperate the // components. // if (iter != pBindPath->begin()) { wcscat (szExport, L"_"); } wcscat (szExport, pUpper->Ext.PszBindName()); // If the next component in the bindpath is the last // component, it is an adapter (by convention). Check // to see if there are multiple interfaces to be expanded // for the current component over this adapter. // if ((iter + 1) == (pBindPath->end() - 1)) { DWORD cInterfaces; GUID* pguidInterfaceIds; pLower = *(iter + 1); hr = pUpper->Notify.HrGetInterfaceIdsForAdapter ( m_pNetConfig->Notify.PINetCfg(), pLower, &cInterfaces, &pguidInterfaceIds); if (FAILED(hr)) { break; } if (cInterfaces) { Assert (pguidInterfaceIds); if (iter != pBindPath->begin()) { wcscat (szBind, L"_"); pchBind = szBind + wcslen(szBind); Assert (wcslen(szBind) + c_cchGuidWithTerm < celems(szBind)); } else { // The first component in the bindpath is // one that has multiple interfaces over the // adapter. The bind value should be as // normal, the export value will have the // expand interfaces. // Assert (wcslen(szBind) + wcslen(pLower->Ext.PszBindName()) < celems(szBind)); wcscat (szBind, pLower->Ext.PszBindName()); hr = m_BindValue.HrCopyString (szBind); if (S_OK != hr) { break; } } wcscat (szExport, L"_"); pchExport = szExport + wcslen(szExport); Assert (wcslen(szExport) + c_cchGuidWithTerm < celems(szExport)); for (UINT i = 0; i < cInterfaces; i++) { if (iter != pBindPath->begin()) { StringFromGUID2 ( pguidInterfaceIds[i], pchBind, c_cchGuidWithTerm); hr = m_BindValue.HrCopyString (szBind); if (S_OK != hr) { break; } } StringFromGUID2 ( pguidInterfaceIds[i], pchExport, c_cchGuidWithTerm); hr = m_ExportValue.HrCopyString (szExport); if (S_OK != hr) { break; } } CoTaskMemFree (pguidInterfaceIds); if (iter != pBindPath->begin()) { wcscat (szRoute, L" "); } wcscat (szRoute, L"\""); wcscat (szRoute, pLower->Ext.PszBindName()); wcscat (szRoute, L"\""); hr = m_RouteValue.HrCopyString (szRoute); if (S_OK != hr) { break; } // We only allow one component in a bindpath // to support mutliple interfaces and it always // comes at the end of the bindpath. Therefore, // after expanding them, we are done with the // bindpath and proceed to the next. (Hence, the // 'break'). // break; } } } // If we exited the loop because we traversed the entire // bindpath (as opposed to expanding multiple interfaces, // where we would have stopped short), then add the bind // and export strings for this bindpath to the buffer and // proceed to the next bindpath. // if (iter == pBindPath->end()) { hr = m_BindValue.HrCopyString (szBind); if (S_OK != hr) { break; } hr = m_ExportValue.HrCopyString (szExport); if (S_OK != hr) { break; } hr = m_RouteValue.HrCopyString (szRoute); if (S_OK != hr) { break; } } } // The bind and export values are multi-sz, so make sure they // are double null-terminiated. // hr = m_BindValue.HrCopyString (L""); if (S_OK == hr) { hr = m_ExportValue.HrCopyString (L""); } if (S_OK == hr) { hr = m_RouteValue.HrCopyString (L""); } } // Special case: NCF_DONTEXPOSELOWER // if ((S_OK == hr) && ((pComponent->m_dwCharacter & NCF_DONTEXPOSELOWER) || (0 == wcscmp(L"ms_nwspx", pComponent->m_pszInfId)))) { wcscpy (szExport, L"\\Device\\"); wcscat (szExport, pComponent->Ext.PszBindName()); m_ExportValue.Clear (); hr = m_ExportValue.HrCopyString (szExport); Assert (S_OK == hr); hr = m_ExportValue.HrCopyString (L""); Assert (S_OK == hr); } // End Special case } if (S_OK == hr) { // Need to write out lanamap before writing new bindings since // we need the old binding information to persist lana numbers. // if (0 == wcscmp (pComponent->m_pszInfId, L"ms_netbios")) { (VOID) HrUpdateLanaConfig ( m_pNetConfig->Core.Components, (PCWSTR)m_BindValue.PbBuffer(), m_BindSet.CountBindPaths()); } hr = HrWriteLinkageValues ( pComponent, (PCWSTR)m_BindValue.PbBuffer(), (PCWSTR)m_ExportValue.PbBuffer(), (PCWSTR)m_RouteValue.PbBuffer()); if(S_OK == hr) { // mbend June 20, 2000 // RAID 23275: Default gateway isn't respecting the adapter order specified under connections->advanced->properties // Notify NDIS when the binding list for a component changes. // UNICODE_STRING LowerComponent; UNICODE_STRING UpperComponent; UNICODE_STRING BindList; BOOL bOk = TRUE; if (FIsEnumerated(pComponent->Class())) { RtlInitUnicodeString(&BindList, NULL); RtlInitUnicodeString(&LowerComponent, NULL); RtlInitUnicodeString(&UpperComponent, pComponent->Ext.PszBindName()); bOk = NdisHandlePnPEvent( NDIS, BIND_LIST, &LowerComponent, &UpperComponent, &BindList, const_cast(m_BindValue.PbBuffer()), m_BindValue.CountOfBytesUsed()); } else { RtlInitUnicodeString(&BindList, NULL); RtlInitUnicodeString(&LowerComponent, NULL); RtlInitUnicodeString(&UpperComponent, pComponent->Ext.PszBindName()); TraceTag(ttidBeDiag, "BindName (TDI Client): %S", pComponent->Ext.PszBindName()); bOk = NdisHandlePnPEvent( TDI, RECONFIGURE, &LowerComponent, &UpperComponent, &BindList, const_cast(m_BindValue.PbBuffer()), m_BindValue.CountOfBytesUsed()); } if(!bOk) { // hr = HrFromLastWin32Error(); } } } TraceHr (ttidError, FAL, hr, FALSE, "CRegistryBindingsContext::HrWriteBindingsForComponent"); return hr; } HRESULT CRegistryBindingsContext::HrWriteBindingsForFilterDevices ( IN CFilterDevices* pFilterDevices) { HRESULT hr; CFilterDevices::iterator iter; CFilterDevices::iterator next; CFilterDevice* pDevice; CFilterDevice* pNextDevice; CFilterDevice* pPrevDevice; PCWSTR pmszRootDevice; PCWSTR pmszUpperBind; #define SZ_DEVICE_LEN 8 // characters in L"\\Device\\" WCHAR mszExport [SZ_DEVICE_LEN + c_cchGuidWithTerm + 1]; WCHAR* const pchExportGuid = mszExport + SZ_DEVICE_LEN; // Pre-fill the beginning of the Export string. // Set the terminating NULL for the mutli-sz too. // wcscpy (mszExport, L"\\Device\\"); Assert (SZ_DEVICE_LEN == wcslen(mszExport)); mszExport[celems(mszExport) - 1] = 0; hr = S_OK; // Sort the filter devices by pAdapter and then by // pFilter->m_dwFilterClassOrdinal. We will then iterate all filter // devices to write the bindings. Because of the sort, we'll iterate // all filter devices for a given adapter in class order from smallest // to largest. (Smaller class ordinals have affinity for the protocol.) // pFilterDevices->SortForWritingBindings (); pPrevDevice = NULL; for (iter = pFilterDevices->begin(); iter != pFilterDevices->end(); iter++) { pDevice = *iter; Assert (pDevice); // Generate the rest of the Export string. // \Device\{GUID} // Assert ((c_cchGuidWithTerm - 1) == wcslen(pDevice->m_szInstanceGuid)); wcscpy (pchExportGuid, pDevice->m_szInstanceGuid); // If this device's adapter is different than the previous device's // adapter, we are dealing with the top of a new chain. We need // to initialize RootDevice which will be the multi-sz of all // bindnames in the chain including the adapter. // if (!pPrevDevice || (pDevice->m_pAdapter != pPrevDevice->m_pAdapter)) { // Compute RootDevice. // We'll use m_ExportValue as the buffer. // m_ExportValue.Clear(); m_ExportValue.HrCopyString (pDevice->m_szInstanceGuid); for (next = iter + 1; next != pFilterDevices->end(); next++) { pNextDevice = *next; Assert (pNextDevice); // We're done when we reach the next filter chain. // if (pNextDevice->m_pAdapter != pDevice->m_pAdapter) { break; } m_ExportValue.HrCopyString (pNextDevice->m_szInstanceGuid); } m_ExportValue.HrCopyString (pDevice->m_pAdapter->Ext.PszBindName()); m_ExportValue.HrCopyString (L""); pmszRootDevice = (PCWSTR)m_ExportValue.PbBuffer(); Assert (*pmszRootDevice); // Compute UpperBind. // We'll use m_BindValue as the buffer. // hr = HrGetAdapterUpperBindValue (pDevice->m_pAdapter); } // We're continuing in the filter chain and this device is not // the topmost. (not closest to the protocol). // else { // Since RootDevice was built up for the top device in the chain, // each successive device just needs to skip past the next // string in the mutli-sz. // Assert (*pmszRootDevice); pmszRootDevice += wcslen(pmszRootDevice) + 1; // UpperBind is the previous device's filter's bind name. // m_BindValue.Clear(); m_BindValue.HrCopyString (pPrevDevice->m_pFilter->Ext.PszBindName()); m_BindValue.HrCopyString (L""); } pmszUpperBind = (PCWSTR)m_BindValue.PbBuffer(); // We now have: // Export in mszExport // RootDevice at pmszRootDevice (in m_ExportValue) // UpperBind at pmszUpperBind (in m_BindValue) // hr = HrWriteFilterDeviceLinkage ( pDevice, pFilterDevices->m_hdi, mszExport, pmszRootDevice, pmszUpperBind); // If this is the last device in the chain, we need to write // the UpperBind of the adapter to be this filter device. // next = iter + 1; if ((next == pFilterDevices->end()) || (*next)->m_pAdapter != pDevice->m_pAdapter) { // UpperBind is this last device's filter's bind name. // m_BindValue.Clear(); m_BindValue.HrCopyString (pDevice->m_pFilter->Ext.PszBindName()); m_BindValue.HrCopyString (L""); pmszUpperBind = (PCWSTR)m_BindValue.PbBuffer(); hr = HrWriteFilteredAdapterUpperBind ( pDevice->m_pAdapter, pmszUpperBind); } // Remember the previous device so that when we go to the next // device, we'll know we're dealing with a different chain if // the next device's adapter is different than this one. // pPrevDevice = pDevice; } TraceHr (ttidError, FAL, hr, FALSE, "CRegistryBindingsContext::HrWriteBindingsForFilterDevices"); return hr; }