#include "pch.h" #pragma hdrstop #include "connutil.h" #include "resource.h" #include "ncreg.h" #include "nsres.h" #include "wizard.h" #include "ncsetup.h" #include "..\lanui\util.h" #include "ncmisc.h" // Note: This module needs to touch pWizard as little as possible because it // it is deleted before this page is entered. // inline BOOL FNetDevPagesAdded(DWORD dw) { return ((dw & 0x10000000) != 0); } inline DWORD DwNetDevMarkPagesAdded(DWORD dw) { return (dw | 0x10000000); } inline BOOL FNetDevChecked(DWORD dw) { return ((dw & 0x01000000) != 0); } inline DWORD NetDevToggleChecked(DWORD dw) { if (dw & 0x01000000) return (dw & ~0x01000000); else return (dw | 0x01000000); } typedef struct { SP_CLASSIMAGELIST_DATA cild; HIMAGELIST hImageStateIcons; HDEVINFO hdi; HPROPSHEETPAGE hpsp; // The wnetdev HPROPSHEETPAGE PINTERNAL_SETUP_DATA pSetupData; } NetDevInfo; typedef struct { DWORD dwFlags; DWORD cPages; HPROPSHEETPAGE * phpsp; SP_DEVINFO_DATA deid; } NetDevItemInfo; // CHECKED_BY_DEFAULT controls whether the items needing configuration are // checked by default or not. // #define CHECKED_BY_DEFAULT 1 // TRUE if all the devices are selected for configuration. // static BOOL bAllSelected=FALSE; // DevInst of the device whose property page will be displayed after // the first page on which device selection is shown. // static DWORD dwFirstDevInst=0; HRESULT HrGetDevicesThatHaveWizardPagesToAdd(HDEVINFO* phdi); HRESULT HrFillNetDevList(HWND hwndLV, NetDevInfo * pNdi); // The property page of every isdn device queries if all the // devices have been selected or not. We return TRUE only if // all the devices have been selected and the query is from // the device whose property page is displayed first. This // is done to prevent the user from coming back to the device // selection page since there is nothing to do once all the // devices have been selected. // VOID SetSelectedAll (HWND hwndDlg, DWORD dwDevInst) { if (dwDevInst == dwFirstDevInst) { ::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)bAllSelected ); } else { ::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)FALSE ); } return; } HRESULT HrNetDevInitListView(HWND hwndLV, NetDevInfo * pNdi) { TraceFileFunc(ttidGuiModeSetup); DWORD dwStyle; RECT rc; LV_COLUMN lvc = {0}; SP_CLASSIMAGELIST_DATA * pcild; Assert(hwndLV); Assert(NULL != pNdi); // Set the shared image lists bit so the caller can destroy the class // image lists itself // dwStyle = GetWindowLong(hwndLV, GWL_STYLE); SetWindowLong(hwndLV, GWL_STYLE, (dwStyle | LVS_SHAREIMAGELISTS)); // Create small image lists // HRESULT hr = HrSetupDiGetClassImageList(&pNdi->cild); if (SUCCEEDED(hr)) { AssertSz(pNdi->cild.ImageList, "No class image list data!"); ListView_SetImageList(hwndLV, pNdi->cild.ImageList, LVSIL_SMALL); } else { TraceError("HrSetupDiGetClassImageList returns failure", hr); hr = S_OK; } // Create state image lists pNdi->hImageStateIcons = ImageList_LoadBitmapAndMirror( _Module.GetResourceInstance(), MAKEINTRESOURCE(IDB_CHECKSTATE), 16, 0, PALETTEINDEX(6)); ListView_SetImageList(hwndLV, pNdi->hImageStateIcons, LVSIL_STATE); GetClientRect(hwndLV, &rc); lvc.mask = LVCF_FMT | LVCF_WIDTH; lvc.fmt = LVCFMT_LEFT; lvc.cx = rc.right; // $REVIEW(tongl 12\22\97): Fix for bug#127472 // lvc.cx = rc.right - GetSystemMetrics(SM_CXVSCROLL); ListView_InsertColumn(hwndLV, 0, &lvc); if (SUCCEEDED(hr)) { // Selete the first item ListView_SetItemState(hwndLV, 0, LVIS_SELECTED, LVIS_SELECTED); } TraceError("HrNetDevInitListView", hr); return hr; } BOOL OnNetDevInitDialog(HWND hwndDlg, LPARAM lParam) { TraceFileFunc(ttidGuiModeSetup); NetDevInfo * pNdi; PROPSHEETPAGE* psp = (PROPSHEETPAGE*)lParam; Assert(psp->lParam); pNdi = reinterpret_cast(psp->lParam); ::SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pNdi); if (NULL != pNdi) { HWND hwndLV = GetDlgItem(hwndDlg, LVC_NETDEVLIST); Assert(hwndLV); if (SUCCEEDED(HrNetDevInitListView(hwndLV, pNdi))) { // Populate the list // if (NULL != pNdi->hdi) { (VOID)HrFillNetDevList(hwndLV, pNdi); } } } return FALSE; // We didn't change the default item of focus } VOID OnNetDevDestroy(HWND hwndDlg) { TraceFileFunc(ttidGuiModeSetup); NetDevInfo *pNdi = (NetDevInfo *)::GetWindowLongPtr(hwndDlg, DWLP_USER); if (NULL != pNdi) { if (pNdi->cild.ImageList) { // Destroy the class image list data (void) HrSetupDiDestroyClassImageList(&pNdi->cild); } if (pNdi->hImageStateIcons) { ImageList_Destroy(pNdi->hImageStateIcons); } // The cleanup any pages we loaded for the providers // but did not add to setup wizard will be done by processing the // LVN_DELETEITEM message // } ::SetWindowLongPtr(hwndDlg, DWLP_USER, 0); if (NULL != pNdi) { MemFree(pNdi); } } // // Function: OnNetDevPageNext // // Purpose: Handle the PSN_WIZNEXT notification // // Parameters: hwndDlg - Handle to NetDev dialog // // Returns: BOOL, TRUE if we processed the message // BOOL OnNetDevPageNext(HWND hwndDlg) { TraceFileFunc(ttidGuiModeSetup); BOOL fRet = FALSE; // Accept the default behaviour HWND hwndLV = GetDlgItem(hwndDlg, LVC_NETDEVLIST); int nCount = ListView_GetItemCount(hwndLV); int nCountSelected = 0; NetDevInfo *pNdi = (NetDevInfo *)::GetWindowLongPtr(hwndDlg, DWLP_USER); if ((0 < nCount) && (NULL != pNdi) && (NULL != pNdi->hpsp)) { fRet = TRUE; // Loop through the items in the listview if any are checked // But have not yet been marked as having their pages added, // add the pages and mark the item. // for (int nIdx=0; nIdxdwFlags)) { // Keep track of how many device have been selected. // nCountSelected++; if (!FNetDevPagesAdded(pndii->dwFlags)) { // // Since property pages are added when the device is // selected, any device could end up being the first to // show the property page as the devices could be // selected in a random order. So, we always save the // device instance in case this is the last device // selected. dwFirstDevInst = pndii->deid.DevInst; // Mark the pages as added // pndii->dwFlags = DwNetDevMarkPagesAdded(pndii->dwFlags); for (DWORD nIdx = pndii->cPages; nIdx > 0; nIdx--) { PropSheet_InsertPage(GetParent(hwndDlg), (WPARAM)pNdi->hpsp, (LPARAM)pndii->phpsp[nIdx - 1]); } // After loading the pages mark the option, uncheckable // (Note: with testing we might be able to support removal) // lvi.state = INDEXTOSTATEIMAGEMASK(SELS_INTERMEDIATE); lvi.mask = LVIF_STATE; lvi.stateMask = LVIS_STATEIMAGEMASK; BOOL ret = ListView_SetItem(hwndLV, &lvi); // Clear the reinstall flag on the device needing configuration // SetupDiSetConfigFlags(pNdi->hdi, &(pndii->deid), CONFIGFLAG_REINSTALL, SDFBO_XOR); } } } } bAllSelected = nCountSelected == nCount; } return fRet; } // // Function: OnNetDevPageActivate // // Purpose: Handle the PSN_SETACTIVE notification // // Parameters: hwndDlg - Handle to NetDev dialog // // Returns: BOOL, TRUE // BOOL OnNetDevPageActivate(HWND hwndDlg) { TraceFileFunc(ttidGuiModeSetup); TraceTag(ttidWizard, "Entering NetDev page..."); NetDevInfo *pNdi; if (0 == ListView_GetItemCount(GetDlgItem(hwndDlg, LVC_NETDEVLIST))) { ::SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, -1); TraceTag(ttidWizard, "NetDev page refuses activation, no items to display."); } else { pNdi = (NetDevInfo *)::GetWindowLongPtr(hwndDlg, DWLP_USER); Assert(pNdi); if ( pNdi ) { Assert(pNdi->pSetupData); if ( pNdi->pSetupData ) { pNdi->pSetupData->ShowHideWizardPage(TRUE); } } } // disable the back button (#342922) ::SendMessage(GetParent(hwndDlg), PSM_SETWIZBUTTONS, 0, (LPARAM)(PSWIZB_NEXT)); return TRUE; } // // Function: OnListViewDeleteItem // // Purpose: Handle the LVN_DELETEITEM notification // // Parameters: hwndList - Handle to listview control // pnmh - Ptr to the NMHDR for this notification // // Returns: none // VOID OnListViewDeleteItem(HWND hwndList, LPNMHDR pnmh) { NetDevItemInfo * pndii = NULL; NM_LISTVIEW * pnmlv = reinterpret_cast(pnmh); LV_ITEM lvi = {0}; lvi.mask = LVIF_PARAM; lvi.iItem = pnmlv->iItem; if (TRUE == ListView_GetItem(hwndList, &lvi)) { NetDevItemInfo * pndii = (NetDevItemInfo*)lvi.lParam; // Delete pages held, but that were not added to the wizard // if (pndii && !FNetDevPagesAdded(pndii->dwFlags)) { for (DWORD nIdx = 0; nIdx < pndii->cPages; nIdx++) { DestroyPropertySheetPage(pndii->phpsp[nIdx]); } delete pndii; } } } // // Function: OnListViewDeleteItem // // Purpose: Handle the NM_CLICK notification for the listview control // // Parameters: hwndList - Handle to listview control // pnmh - Ptr to the NMHDR for this notification // // Returns: none // VOID OnListViewClick(HWND hwndList, LPNMHDR pnmh) { INT iItem; DWORD dwpts; RECT rc; LV_HITTESTINFO lvhti; // we have the location dwpts = GetMessagePos(); // translate it relative to the listview GetWindowRect(hwndList, &rc); lvhti.pt.x = LOWORD(dwpts) - rc.left; lvhti.pt.y = HIWORD(dwpts) - rc.top; // get currently selected item iItem = ListView_HitTest(hwndList, &lvhti); // if no selection, or click not on state return false if (-1 != iItem) { // set the current selection // ListView_SetItemState(hwndList, iItem, LVIS_SELECTED, LVIS_SELECTED); if (LVHT_ONITEMSTATEICON != (LVHT_ONITEMSTATEICON & lvhti.flags)) { iItem = -1; } if (-1 != iItem) { LV_ITEM lvItem; // Get the item // lvItem.iItem = iItem; lvItem.mask = LVIF_PARAM; lvItem.iSubItem = 0; if (ListView_GetItem(hwndList, &lvItem)) { Assert(lvItem.lParam); NetDevItemInfo *pndii = (NetDevItemInfo*)lvItem.lParam; // Toggle the state (only if we haven't already added pages) // if (pndii && !FNetDevPagesAdded(pndii->dwFlags)) { pndii->dwFlags = NetDevToggleChecked(pndii->dwFlags); if (FNetDevChecked(pndii->dwFlags)) lvItem.state = INDEXTOSTATEIMAGEMASK(SELS_CHECKED); else lvItem.state = INDEXTOSTATEIMAGEMASK(SELS_UNCHECKED); lvItem.mask = LVIF_STATE; lvItem.stateMask = LVIS_STATEIMAGEMASK; BOOL ret = ListView_SetItem(hwndList, &lvItem); } } } } } // // Function: dlgprocNetDev // // Purpose: Dialog Procedure for the NetDev wizard page // // Parameters: standard dlgproc parameters // // Returns: INT_PTR // INT_PTR CALLBACK dlgprocNetDev( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) { TraceFileFunc(ttidGuiModeSetup); BOOL frt = FALSE; switch (uMsg) { case WM_INITDIALOG: OnNetDevInitDialog(hwndDlg, lParam); break; case WM_DESTROY: OnNetDevDestroy(hwndDlg); break; case WM_NOTIFY: { LPNMHDR pnmh = (LPNMHDR)lParam; if (LVC_NETDEVLIST == (int)pnmh->idFrom) { Assert(GetDlgItem(hwndDlg, LVC_NETDEVLIST) == pnmh->hwndFrom); if (NM_CLICK == pnmh->code) { OnListViewClick(pnmh->hwndFrom, pnmh); } else if (LVN_DELETEITEM == pnmh->code) { OnListViewDeleteItem(pnmh->hwndFrom, pnmh); } } else { // Must be a property sheet notification // switch (pnmh->code) { // propsheet notification case PSN_HELP: break; case PSN_SETACTIVE: frt = OnNetDevPageActivate(hwndDlg); break; case PSN_APPLY: break; case PSN_KILLACTIVE: break; case PSN_RESET: break; case PSN_WIZBACK: break; case PSN_WIZFINISH: break; case PSN_WIZNEXT: frt = OnNetDevPageNext(hwndDlg); break; default: break; } } } break; case WM_SELECTED_ALL: SetSelectedAll (hwndDlg, (DWORD)lParam); frt = TRUE; default: break; } return( frt ); } // // Function: NetDevPageCleanup // // Purpose: As a callback function to allow any page allocated memory // to be cleaned up, after the page will no longer be accessed. // // Parameters: pWizard [IN] - The wizard against which the page called // register page // lParam [IN] - The lParam supplied in the RegisterPage call // // Returns: nothing // VOID NetDevPageCleanup(CWizard *pWizard, LPARAM lParam) { TraceFileFunc(ttidGuiModeSetup); // Nothing to do. The pNdi is destroyed by the WM_DESTROY message // processed above. } // // Function: CreateNetDevPage // // Purpose: To determine if the NetDev page needs to be shown, and to // to create the page if requested. // // Parameters: pWizard [IN] - Ptr to a Wizard instance // pData [IN] - Context data to describe the world in // which the Wizard will be run // fCountOnly [IN] - If True, only the maximum number of // pages this routine will create need // be determined. // pnPages [IN] - Increment by the number of pages // to create/created // // Returns: HRESULT, S_OK on success // HRESULT HrCreateNetDevPage(CWizard *pWizard, PINTERNAL_SETUP_DATA pData, BOOL fCountOnly, UINT *pnPages) { TraceFileFunc(ttidGuiModeSetup); HRESULT hr = S_OK; if (!IsPostInstall(pWizard) && !IsUnattended(pWizard) && !IsUpgrade(pWizard)) { (*pnPages)++; // If not only counting, create and register the page if (!fCountOnly) { HPROPSHEETPAGE hpsp; PROPSHEETPAGE psp; NetDevInfo * pNdi; hr = E_OUTOFMEMORY; TraceTag(ttidWizard, "Creating NetDev Page"); pNdi = reinterpret_cast(MemAlloc(sizeof(NetDevInfo))); if (NULL != pNdi) { ZeroMemory(pNdi, sizeof(NetDevInfo)); psp.dwSize = sizeof( PROPSHEETPAGE ); psp.dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE; psp.pszHeaderTitle = SzLoadIds(IDS_T_NetDev); psp.pszHeaderSubTitle = SzLoadIds(IDS_ST_NetDev); psp.pszTemplate = MAKEINTRESOURCE(IDD_NetDevSelect); psp.hInstance = _Module.GetResourceInstance(); psp.hIcon = NULL; psp.pfnDlgProc = dlgprocNetDev; psp.lParam = reinterpret_cast(pNdi); hpsp = CreatePropertySheetPage(&psp); if (hpsp) { pNdi->pSetupData = pData; pNdi->hpsp = hpsp; pWizard->RegisterPage(IDD_NetDevSelect, hpsp, NetDevPageCleanup, (LPARAM)pNdi); hr = S_OK; } else { MemFree(pNdi); } } } } TraceHr(ttidWizard, FAL, hr, FALSE, "HrCreateNetDevPage"); return hr; } // // Function: AppendNetDevPage // // Purpose: Add the NetDev page, if it was created, to the set of pages // that will be displayed. // // Parameters: pWizard [IN] - Ptr to Wizard Instance // pahpsp [IN,OUT] - Array of pages to add our page to // pcPages [IN,OUT] - Count of pages in pahpsp // // Returns: Nothing // VOID AppendNetDevPage(CWizard *pWizard, HPROPSHEETPAGE* pahpsp, UINT *pcPages) { TraceFileFunc(ttidGuiModeSetup); if (!IsPostInstall(pWizard) && !IsUnattended(pWizard) && !IsUpgrade(pWizard)) { HPROPSHEETPAGE hPage = pWizard->GetPageHandle(IDD_NetDevSelect); Assert(hPage); pahpsp[*pcPages] = hPage; (*pcPages)++; } } // // Function: NetDevRetrieveInfo // // Purpose: To retrieve any network device info // // Parameters: pWizard [in] - Contains NetDevInfo blob to populate // // Returns: Nothing // VOID NetDevRetrieveInfo(CWizard * pWizard) { TraceFileFunc(ttidGuiModeSetup); Assert(pWizard); if (!IsPostInstall(pWizard) && !IsUnattended(pWizard) && !IsUpgrade(pWizard)) { // The pNdi pointer below was cached in two locations. // 1) In the HPROPSHEETPAGE lParam for access by the page // 2) In the wizard so we can make this NetDevRetrieveInfo call. // This second item was optional and could have been done in the // InitDialog above instead. NetDevInfo * pNdi = reinterpret_cast (pWizard->GetPageData(IDD_NetDevSelect)); TraceTag(ttidWizard, "NetDev retrieving info..."); if (NULL == pNdi) return; // Query all pages that might be added // (VOID)HrGetDevicesThatHaveWizardPagesToAdd(&pNdi->hdi); } } // // Function: HrSendFinishInstallWizardFunction // // Purpose: Sends a DIF_NEWDEVICEWIZARD_FINISHINSTALL fcn to the class // installer (and co-installers). The installers respond // if there are wizard pages to add // // Parameters: hdi [in] - See Device Installer Api for description // of the structure. // pdeid [in] - See Device Installer Api // pndwd [out] - See Device Installer Api // // // Returns: HRESULT. S_OK if successful, or a win32 converted error // HRESULT HrSendFinishInstallWizardFunction(HDEVINFO hdi, PSP_DEVINFO_DATA pdeid, PSP_NEWDEVICEWIZARD_DATA pndwd) { TraceFileFunc(ttidGuiModeSetup); Assert(IsValidHandle(hdi)); Assert(pdeid); Assert(pndwd); ZeroMemory(pndwd, sizeof(*pndwd)); // Set up the structure to retrieve wizard pages // pndwd->ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER); pndwd->ClassInstallHeader.InstallFunction = DIF_NEWDEVICEWIZARD_FINISHINSTALL; // Set up our wizard structure in the device structure HRESULT hr = HrSetupDiSetClassInstallParams(hdi, pdeid, reinterpret_cast(pndwd), sizeof(*pndwd)); if (SUCCEEDED(hr)) { // Call the class installers (and co-installers) hr = HrSetupDiCallClassInstaller(DIF_NEWDEVICEWIZARD_FINISHINSTALL, hdi, pdeid); if (SUCCEEDED(hr) || SPAPI_E_DI_DO_DEFAULT == hr) { // Get the wizard data hr = HrSetupDiGetFixedSizeClassInstallParams(hdi, pdeid, reinterpret_cast(pndwd), sizeof(*pndwd)); } } TraceError("HrSendFinishInstallWizardFunction", hr); return hr; } // // Function: HrGetDeviceWizardPages // // Purpose: To retrieve wizard pages for a device // // Parameters: hdi [in] - See Device Installer Api for description // of the structure. // pdeid [in] - See Device Installer Api // pphpsp [out] - An array of wizard pages for the device // pcPages [out] - The number of pages in pphpsp // // // Returns: HRESULT. S_OK if successful, or a win32 converted error // HRESULT HrGetDeviceWizardPages(HDEVINFO hdi, PSP_DEVINFO_DATA pdeid, HPROPSHEETPAGE** pphpsp, DWORD* pcPages) { TraceFileFunc(ttidGuiModeSetup); Assert(IsValidHandle(hdi)); Assert(pdeid); Assert(pphpsp); Assert(pcPages); HRESULT hr; SP_NEWDEVICEWIZARD_DATA ndwd; if (( NULL == pphpsp ) || ( NULL == pcPages ) ) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); } else { *pphpsp = NULL; *pcPages = 0; // Get the wizard data hr = HrSetupDiGetFixedSizeClassInstallParams(hdi, pdeid, reinterpret_cast(&ndwd), sizeof(ndwd)); // If successful and the correct header is present... if (SUCCEEDED(hr) && ndwd.NumDynamicPages && (DIF_NEWDEVICEWIZARD_FINISHINSTALL == ndwd.ClassInstallHeader.InstallFunction)) { // Copy the handles to the out parameter // *pphpsp = new HPROPSHEETPAGE[ndwd.NumDynamicPages]; if(pphpsp && *pphpsp) { CopyMemory(*pphpsp, ndwd.DynamicPages, sizeof(HPROPSHEETPAGE) * ndwd.NumDynamicPages); *pcPages = ndwd.NumDynamicPages; hr = S_OK; } else { hr = E_OUTOFMEMORY; } } } TraceError("HrGetDeviceWizardPages", hr); return hr; } // // Function: HrFillNetDevList // // Purpose: To populate the listview with text and data of the ISDN // cards which need configuration // // Parameters: hwndLV - Handle to the the listview control // pNdi - Net Device Info // // Returns: HRESULT. S_OK if successful, or a win32 converted error // HRESULT HrFillNetDevList(HWND hwndLV, NetDevInfo * pNdi) { TraceFileFunc(ttidGuiModeSetup); SP_DEVINFO_DATA deid; DWORD dwIndex = 0; HRESULT hr = S_OK; Assert(hwndLV); Assert(pNdi); Assert(pNdi->hdi); while (SUCCEEDED(hr = HrSetupDiEnumDeviceInfo(pNdi->hdi, dwIndex, &deid))) { HPROPSHEETPAGE * phpsp = NULL; DWORD cPages = 0; hr = HrGetDeviceWizardPages(pNdi->hdi, &deid, &phpsp, &cPages); if (SUCCEEDED(hr) && (cPages > 0)) { NetDevItemInfo * pndii = new NetDevItemInfo; if (NULL != pndii) { ZeroMemory(pndii, sizeof(NetDevItemInfo)); pndii->phpsp = phpsp; pndii->cPages = cPages; pndii->deid = deid; #if CHECKED_BY_DEFAULT pndii->dwFlags = NetDevToggleChecked(pndii->dwFlags); #else pndii->dwFlags = 0; #endif PWSTR szName = NULL; hr = HrSetupDiGetDeviceName(pNdi->hdi, &deid, &szName); if (SUCCEEDED(hr)) { int nIdx; LV_ITEM lvi; int nCount = ListView_GetItemCount(hwndLV); Assert(NULL != szName); Assert(lstrlen(szName)); // Add the item info to the list view lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE; lvi.iItem = nCount; lvi.iSubItem = 0; #if CHECKED_BY_DEFAULT lvi.state = INDEXTOSTATEIMAGEMASK(SELS_CHECKED); #else lvi.state = INDEXTOSTATEIMAGEMASK(SELS_UNCHECKED); #endif lvi.stateMask = LVIS_STATEIMAGEMASK; lvi.pszText = szName; lvi.cchTextMax = lstrlen(lvi.pszText); if ( HrSetupDiGetClassImageIndex(&pNdi->cild, &GUID_DEVCLASS_NET, &lvi.iImage) != S_OK ) { // In case of failure, hard-code the value of the image index of // Net class image. lvi.iImage = 18; } lvi.lParam = (LPARAM)pndii; if (-1 == ListView_InsertItem(hwndLV, &lvi)) { TraceError("HrFillNetDevList - ListView_InsertItem", E_OUTOFMEMORY); } delete [](BYTE *)szName; } } } dwIndex++; } // Convert running out of items to S_OK // if (hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)) { hr = S_OK; } TraceError("HrFillNetDevList", hr); return hr; } // // Function: HrGetDevicesThatHaveWizardPagesToAdd // // Purpose: To retrieve a list of devices that have wizard pages to add // to the networking wizard // // Parameters: phdi [out] - See Device Install Api for description of the // structure. This will hold a list of device. // // Returns: HRESULT // HRESULT HrGetDevicesThatHaveWizardPagesToAdd(HDEVINFO* phdi) { TraceFileFunc(ttidGuiModeSetup); Assert(phdi); // initialize out param *phdi = NULL; // Create a device info list to hold the list of devices HRESULT hr = HrSetupDiGetClassDevs(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, phdi); if (SUCCEEDED(hr)) { SP_DEVINFO_DATA deid; DWORD dwIndex = 0; SP_NEWDEVICEWIZARD_DATA ndwd = {0}; DWORD dwConfigFlags = 0; BOOL fDeleteDeviceInfo; // Enumerate each device in phdi and check if it was a failed install // Gui-mode setup marks any device with wizard pages as needing // reinstall // while (SUCCEEDED(hr = HrSetupDiEnumDeviceInfo(*phdi, dwIndex, &deid))) { fDeleteDeviceInfo = FALSE; // Get the current config flags for the device // We don't need the return value because we will be checking // if pdwConfigFlags is non-null (void) HrSetupDiGetDeviceRegistryProperty(*phdi, &deid, SPDRP_CONFIGFLAGS, NULL, (BYTE*)(&dwConfigFlags), sizeof(dwConfigFlags), NULL); // Are there any config flags and if so, is the reinstall bit // present? if (dwConfigFlags & CONFIGFLAG_REINSTALL) { // Note that we leak this (pdeid) and we don't care because it's // only twelve bytes per adapter and only during setup. Note also // that the reason we allocate as opposed to use the stack is so that // the data passed lives at least as long as the wizard pages themselves. // PSP_DEVINFO_DATA pdeid = new SP_DEVINFO_DATA; if(pdeid) { CopyMemory(pdeid, &deid, sizeof(SP_DEVINFO_DATA)); // Get any wizard pages // hr = HrSendFinishInstallWizardFunction(*phdi, pdeid, &ndwd); if (FAILED(hr) || !ndwd.NumDynamicPages) { // no pages so we will delete this device info from // our list fDeleteDeviceInfo = TRUE; // Clean up because we didn't keep this. // delete pdeid; } } // clear the config flags for the next pass dwConfigFlags = 0; } else { // no config flags so there weren't any pages. We will // delete this device info from our list fDeleteDeviceInfo = TRUE; } if (fDeleteDeviceInfo) { // There were no pages added so remove the device // from our list (void) SetupDiDeleteDeviceInfo(*phdi, &deid); } else { dwIndex++; } } // Failures during this portion should be ignored since we may have // successfully added devices to phdi if (FAILED(hr)) { hr = S_OK; } } TraceError("HrGetDevicesThatHaveWizardPagesToAdd", hr); return hr; }