#include "pch.h" PPCI_TREE SoftPCI_BuildTree( VOID ); VOID SoftPCI_DisplayTreeMenu( IN PPCI_DN Pdn, IN POINT Pt ); VOID SoftPCI_DisplayStandardTreeMenu( IN PPCI_DN Pdn, IN POINT Pt ); VOID SoftPCI_DisplayHotplugTreeMenu( IN PPCI_DN Pdn, IN POINT Pt ); VOID SoftPCI_FreeBranch( IN PPCI_DN Dn ); VOID SoftPCI_InsertTreeItem( IN PPCI_DN Pdn, IN HTREEITEM HtiParent ); VOID SoftPCI_ExpandItem( IN HTREEITEM Hti, IN PVOID Arg1, IN PVOID Arg2 ); VOID SoftPCI_RestoreSelection( IN HTREEITEM Hti, IN PVOID Data1, IN PVOID Data2 ); BOOL g_TreeCreated = FALSE; BOOL g_TreeLocked = FALSE; BOOL g_PendingRefresh = FALSE; PPCI_TREE g_PciTree; LONG_PTR g_DefTreeWndProc; PWCHAR g_LastSelection = NULL; VOID SoftPCI_CreateTreeView( VOID ) { HTREEITEM htiParent; TVITEM tvitem; PPCI_DN rootDevNode = NULL; HMENU menu = GetMenu(g_SoftPCIMainWnd); PPCI_DN pdn = NULL; PCI_DN selectedDevNode; BOOL selectionFound; PWCHAR p; //HCURSOR oldCursor; // // Empty the tree. // TreeView_DeleteAllItems(g_TreeViewWnd); if (g_TreeCreated) { SoftPCI_DestroyTree(g_PciTree); } g_PciTree = SoftPCI_BuildTree(); if (!g_PciTree) { MessageBox(g_SoftPCIMainWnd, L"Failed to create g_PciTree!", L"ERROR", MB_OK); return; } g_TreeCreated = TRUE; SOFTPCI_ASSERT(g_PciTree->ClassImageListData.ImageList != INVALID_HANDLE_VALUE); TreeView_SetImageList(g_TreeViewWnd, g_PciTree->ClassImageListData.ImageList, TVSIL_NORMAL); // // Insert the rest of the items. // SoftPCI_InsertTreeItem(g_PciTree->RootDevNode, TVI_ROOT); // // Currently we always expand the entire tree when it is built. Should see // if there is a way to avoid this.... // SoftPCI_WalkTree( g_PciTree->RootTreeItem, SoftPCI_ExpandItem, NULL, NULL ); // // Restore last selection if any // if (g_LastSelection){ selectionFound = FALSE; SoftPCI_WalkTree( g_PciTree->RootTreeItem, SoftPCI_RestoreSelection, &selectionFound, NULL ); if (!selectionFound) { // // If the last selection no longer exists then we back up to the // parent and check one more time. // p = g_LastSelection; p += wcslen(g_LastSelection); while(*p != '\\'){ p--; } *p = 0; // // Now run the tree one more time looking for the parent // SoftPCI_WalkTree( g_PciTree->RootTreeItem, SoftPCI_RestoreSelection, &selectionFound, NULL ); } }else{ TreeView_Select(g_TreeViewWnd, g_PciTree->RootTreeItem, TVGN_CARET); TreeView_EnsureVisible(g_TreeViewWnd, g_PciTree->RootTreeItem); } SoftPCI_UpdateTabCtrlWindow(g_CurrentTabSelection); } PPCI_TREE SoftPCI_BuildTree(VOID) /*++ Routine Description: This function is the entry point for building our PCI_TREE Arguments: none Return Value: PPCI_TREE we have created --*/ { DEVNODE dn = 0; PPCI_DN pdn; PPCI_TREE pcitree; pcitree = (PPCI_TREE) calloc(1, sizeof(PCI_TREE)); if (!pcitree) return NULL; CM_Locate_DevNode(&dn, NULL, CM_LOCATE_DEVNODE_NORMAL); SOFTPCI_ASSERT(dn != 0); pcitree->ClassImageListData.ImageList = INVALID_HANDLE_VALUE ; pcitree->ClassImageListData.cbSize = sizeof(SP_CLASSIMAGELIST_DATA) ; if (!SetupDiGetClassImageList(&pcitree->ClassImageListData)){ pcitree->ClassImageListData.ImageList = INVALID_HANDLE_VALUE; } pcitree->DevInfoSet = SetupDiCreateDeviceInfoList(NULL, NULL) ; SOFTPCI_ASSERT(pcitree->DevInfoSet != INVALID_HANDLE_VALUE); // // Now find all things PCI and build a PCI_DN tree // pdn = NULL; SoftPCI_EnumerateDevices(pcitree, &pdn, dn, NULL); pcitree->RootDevNode = pdn; return pcitree; } VOID SoftPCI_DestroyTree( IN PPCI_TREE PciTree ) /*++ Routine Description: This routine frees all our allocations for the Tree Arguments: PciTree - Tree to distroy Return Value: none --*/ { PPCI_DN dn = PciTree->RootDevNode; // // First set the tree view image list to NULL // TreeView_SetImageList(g_TreeViewWnd, NULL, TVSIL_NORMAL); // // Now free all our allocated PCI_DN structs // SoftPCI_FreeBranch(dn); // // Destroy our image and info lists // if (PciTree->ClassImageListData.ImageList != INVALID_HANDLE_VALUE){ SetupDiDestroyClassImageList(&PciTree->ClassImageListData); PciTree->ClassImageListData.ImageList = INVALID_HANDLE_VALUE; } if (PciTree->DevInfoSet != INVALID_HANDLE_VALUE){ SetupDiDestroyDeviceInfoList(PciTree->DevInfoSet); PciTree->DevInfoSet = INVALID_HANDLE_VALUE; } // // And finally.... // free(PciTree); } VOID SoftPCI_DisplayTreeMenu( IN PPCI_DN Pdn, IN POINT Pt ) /*++ Routine Description: This routine dispatches the menu request to the appropriate menu function Arguments: Pdn - PCI_DN of the item we are displaying the menu for Pt - coordinates for the item Return Value: none --*/ { if (Pdn->Flags & SOFTPCI_HOTPLUG_SLOT) { SoftPCI_DisplayHotplugTreeMenu(Pdn,Pt); } else if (Pdn->Flags & SOFTPCI_UNENUMERATED_DEVICE) { // // If it's an unenumerated device (in an unpowered hotplug slot), // you can't do anything with it. // return; } else { SoftPCI_DisplayStandardTreeMenu(Pdn, Pt); } return; } VOID SoftPCI_DisplayStandardTreeMenu( IN PPCI_DN Pdn, IN POINT Pt ) /*++ Routine Description: This routine displays the standard context menu when the user right-clicks a device Arguments: Pdn - PCI_DN of the slot Pt - coordinates for the device Return Value: none --*/ { HMENU menu, popup; ULONG dnProblem; BOOL enableDevice = FALSE; INT selection = 0; menu = LoadMenu(g_Instance, MAKEINTRESOURCE(IDM_TREEMENU)); if (!menu) { MessageBox(g_SoftPCIMainWnd, L"failed to display menu!", NULL, MB_OK); return; } popup = GetSubMenu(menu, 0); // // If SoftPCI support is not installed or this isnt a bridge device, // disable the option to add devices. // if ((g_DriverHandle == NULL) || !SoftPCI_IsBridgeDevice(Pdn)) { SoftPCI_DisableMenuItem(menu, ID_INSTALLDEVICE); } if (!SoftPCI_IsSoftPCIDevice(Pdn)) { SoftPCI_DisableMenuItem(menu, ID_DELETEDEVICE); SoftPCI_DisableMenuItem(menu, ID_STATICDEVICE); } if (SoftPCI_GetDeviceNodeProblem(Pdn->DevNode, &dnProblem)) { if (dnProblem == CM_PROB_DISABLED) { enableDevice = TRUE; SoftPCI_SetMenuItemText(menu, ID_ENABLEDISABLEDEVICE, L"E&nable Device"); }else{ // // For now we will not allow the option to disable a non-working device // SoftPCI_DisableMenuItem(menu, ID_ENABLEDISABLEDEVICE); } } // // If this device is in a hotplug slot, can't just rip out // the hardware. You have to go through the appropriate // mechanism. // if (Pdn->Parent && (Pdn->Parent->Flags & SOFTPCI_HOTPLUG_SLOT)) { SoftPCI_DisableMenuItem(menu, ID_DELETEDEVICE); } // // Make sure it pops up in the right place.... // ClientToScreen(g_SoftPCIMainWnd, &Pt); // // lets see the menu // selection = TrackPopupMenuEx( popup, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD, Pt.x, Pt.y, g_SoftPCIMainWnd, NULL ); // // Now we will handle our Floating Tree View menu items // switch (selection) { case ID_INSTALLDEVICE: DISPLAY_NEWDEV_DLG(Pdn); break; case ID_ENABLEDISABLEDEVICE: SoftPCI_EnableDisableDeviceNode( Pdn->DevNode, enableDevice ); break; case ID_DELETEDEVICE: if ((MessageBox(g_SoftPCIMainWnd, L"This option will delete or surprise remove the device from the system.", L"WARNING", MB_OKCANCEL)) == IDOK){ // // Here we tell our driver to delete the specified device. This will // cause a re-enum of everything that will result in the cleanup // of this device in user mode. // if (!SoftPCI_DeleteDevice(Pdn->SoftDev)) { MessageBox(g_SoftPCIMainWnd, L"Failed to delete device!", NULL, MB_OK); } } break; case ID_STATICDEVICE: if (SoftPCI_SaveDeviceToRegisty(Pdn)){ MessageBox(g_SoftPCIMainWnd, L"Successfully saved devices to registry!", NULL, MB_OK); }else{ MessageBox(g_SoftPCIMainWnd, L"Failed to save devices to registry!", NULL, MB_OK); } break; case ID_REFRESHTREE: CM_Reenumerate_DevNode(Pdn->DevNode, 0); SoftPCI_CreateTreeView(); break; default: break; } } VOID SoftPCI_DisplayHotplugTreeMenu( IN PPCI_DN Pdn, IN POINT Pt ) /*++ Routine Description: This routine displays the hotplug specific context menu when the user right-clicks a hotplug slot. Arguments: Pdn - PCI_DN of the slot Pt - coordinates for the slot Return Value: none --*/ { HMENU menu, popup; INT selection; PPCI_DN parentDn; BOOL status; SHPC_SLOT_STATUS_REGISTER slotStatus; menu = LoadMenu(g_Instance, MAKEINTRESOURCE(IDM_HOTPLUGSLOTMENU)); if (!menu) { MessageBox(g_SoftPCIMainWnd, L"failed to display menu!", NULL, MB_OK); return; } popup = GetSubMenu(menu, 0); // // If SoftPCI support is not installed disable the option to add devices. // if (!g_DriverHandle) { SoftPCI_DisableMenuItem(menu, ID_INSTALLDEVICE); } // // If our Device Property dialog is open dont allow properties to be // selected again. // if (g_NewDevDlg) { // // ISSUE: BrandonA - Figure out why we hang if the first Dialog // launched is killed before the second... // SoftPCI_DisableMenuItem(menu, ID_INSTALLDEVICE); } // // DWALKER // Get slot status from driver // appropriately grey out open/close MRL menu item. // if MRL is closed, disable removing the device. // parentDn = Pdn->Parent; status = SoftPCI_GetSlotStatus(parentDn, Pdn->Slot.Function, &slotStatus ); if (status == FALSE) { MessageBox(g_SoftPCIMainWnd, L"failed to display menu!", NULL, MB_OK); return; } // // If the MRL is closed, you can't insert or remove the device. // Otherwise, disable the appropriate menu item based on the presence // of a device in the slot. // if (slotStatus.MRLSensorState == SHPC_MRL_CLOSED) { SoftPCI_DisableMenuItem(menu, ID_REMOVEHPDEVICE); SoftPCI_DisableMenuItem(menu, ID_INSTALLDEVICE); } else if (Pdn->Child == NULL) { SoftPCI_DisableMenuItem(menu, ID_REMOVEHPDEVICE); } else { SoftPCI_DisableMenuItem(menu, ID_INSTALLDEVICE); } if (slotStatus.MRLSensorState == SHPC_MRL_CLOSED) { RemoveMenu(menu, ID_CLOSEMRL, MF_BYCOMMAND); } else { RemoveMenu(menu, ID_OPENMRL, MF_BYCOMMAND); } AppendMenu(popup, MF_SEPARATOR, 0, NULL); switch (slotStatus.PowerIndicatorState) { case SHPC_INDICATOR_OFF: AppendMenu(popup, MF_STRING | MF_GRAYED, ID_POWERINDICATOR, L"Power Indicator: Off"); break; case SHPC_INDICATOR_ON: AppendMenu(popup, MF_STRING | MF_GRAYED, ID_POWERINDICATOR, L"Power Indicator: On"); break; case SHPC_INDICATOR_BLINK: AppendMenu(popup, MF_STRING | MF_GRAYED, ID_POWERINDICATOR, L"Power Indicator: Blinking"); break; case SHPC_INDICATOR_NOP: AppendMenu(popup, MF_STRING | MF_GRAYED, ID_POWERINDICATOR, L"Power Indicator: Unspecified"); break; } switch (slotStatus.AttentionIndicatorState) { case SHPC_INDICATOR_OFF: AppendMenu(popup, MF_STRING | MF_GRAYED, ID_ATTENINDICATOR, L"Attention Indicator: Off"); break; case SHPC_INDICATOR_ON: AppendMenu(popup, MF_STRING | MF_GRAYED, ID_ATTENINDICATOR, L"Attention Indicator: On"); break; case SHPC_INDICATOR_BLINK: AppendMenu(popup, MF_STRING | MF_GRAYED, ID_ATTENINDICATOR, L"Attention Indicator: Blinking"); break; case SHPC_INDICATOR_NOP: AppendMenu(popup, MF_STRING | MF_GRAYED, ID_POWERINDICATOR, L"Attention Indicator: Unspecified"); break; } // // Get the menu updated after our additions. // //DrawMenuBar(g_SoftPCIMainWnd); // // Make sure it pops up in the right place.... // ClientToScreen(g_SoftPCIMainWnd, &Pt); // // lets see the menu // selection = TrackPopupMenuEx(popup, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD, Pt.x, Pt.y, g_SoftPCIMainWnd, NULL ); // // Now we will handle our Floating Tree View menu items // switch (selection) { case ID_INSTALLDEVICE: // // For now kill any dialogs we may already have open before starting this one // //if (g_DevPropDlg) { // SendMessage(g_DevPropDlg, WM_CLOSE, 0L, 0L); //} DISPLAY_NEWDEV_DLG(Pdn); break; case ID_REMOVEHPDEVICE: SoftPCI_RemoveHotplugDevice(parentDn, Pdn->Slot.Function ); SoftPCI_CreateTreeView(); break; case ID_CLOSEMRL: SoftPCI_ExecuteHotplugSlotMethod(parentDn, Pdn->Slot.Function, MRLClose ); break; case ID_OPENMRL: SoftPCI_ExecuteHotplugSlotMethod(parentDn, Pdn->Slot.Function, MRLOpen ); break; case ID_ATTENBUTTON: SoftPCI_ExecuteHotplugSlotMethod(parentDn, Pdn->Slot.Function, AttentionButton ); break; default: break; } // // Make sure we dont lose our focus // //SetFocus(g_TreeViewWnd); } VOID SoftPCI_FreeBranch( IN PPCI_DN Dn ) /*++ Routine Description: This routine will free the specified PCI_DN struct along with all siblings and children. Arguments: Dn - PCI_DN to free Return Value: none --*/ { PPCI_DN child, sibling; if (Dn) { child = Dn->Child; sibling = Dn->Sibling; if (Dn->SoftDev) { free(Dn->SoftDev); } SetupDiDeleteDeviceInfo(Dn->PciTree->DevInfoSet, &Dn->DevInfoData) ; free(Dn); SoftPCI_FreeBranch(child); SoftPCI_FreeBranch(sibling); } } VOID SoftPCI_OnTreeSelectionChange( IN HWND Wnd ) /*++ Routine Description: This routine informs our properties sheet that the selection has changes so that it can update Arguments: Wnd - Return Value: none --*/ { TV_ITEM tviItem; PPCI_DN pdn = NULL; RECT itemRect; ULONG slotCount; // // Get the Current Item // tviItem.mask = TVIF_PARAM; tviItem.hItem = TreeView_GetSelection(g_TreeViewWnd); tviItem.lParam = 0; TreeView_GetItem(g_TreeViewWnd, &tviItem); if (tviItem.lParam) { g_PdnToDisplay = (PPCI_DN)tviItem.lParam; if (g_LastSelection) { free(g_LastSelection); g_LastSelection = NULL; } // // Save the last selection so we can restore it if the tree // is rebuilt. // g_LastSelection = SoftPCI_GetPciPathFromDn(g_PdnToDisplay); SoftPCI_UpdateTabCtrlWindow(g_CurrentTabSelection); } } LRESULT WINAPI SoftPCI_TreeWndProc( IN HWND Wnd, IN UINT Message, IN WPARAM wParam, IN LPARAM lParam ) /*++ Routine Description: This routine hooks the Tree Window message proc and is responsible for resizing our pane window when it is resized. Arguments: hWnd - Window handle Message - Message to process wParam - Message param lParam - Message param Return Value: return value depends on message handled. --*/ { RECT rectMain, rectTree; TV_ITEM tviItem; TVHITTESTINFO hitinfo; PPCI_DN pdn; PCI_DN dn; RECT itemRect; POINT pt; // // Get the Current Item // // switch (Message) { case WM_KEYDOWN: switch (wParam){ case VK_APPS: // // Grab the PCI_DN from the current tree item // pdn = SoftPCI_GetDnFromTreeItem(NULL); // // We copy this to a new DN because the TREE is constantly being // rebuilt and we cannot rely on the TV_ITEM.lParam value to always // be accurate later (we may have changed it). // RtlCopyMemory(&dn, pdn, sizeof(PCI_DN)); if (TreeView_GetItemRect(g_TreeViewWnd, TreeView_GetSelection(g_TreeViewWnd), &itemRect, TRUE)) { // // Adjust the location for our menu // pt.x = itemRect.right; pt.y = itemRect.top; SoftPCI_DisplayTreeMenu(&dn, pt); } break; default: return CallWindowProc((WNDPROC)g_DefTreeWndProc, Wnd, Message, wParam, lParam); } break; case WM_RBUTTONDOWN: ZeroMemory(&hitinfo, sizeof(TVHITTESTINFO)); hitinfo.pt.x = GET_X_LPARAM(lParam); hitinfo.pt.y = GET_Y_LPARAM(lParam); if (TreeView_HitTest(g_TreeViewWnd, &hitinfo)) { g_TreeLocked = TRUE; pdn = SoftPCI_GetDnFromTreeItem(hitinfo.hItem); // // See comment above for reason why we copy this here.... // RtlCopyMemory(&dn, pdn, sizeof(PCI_DN)); // // If an item in the tree is already selected this will cause the selection to change // as each item is right clicked. // TreeView_Select(g_TreeViewWnd, hitinfo.hItem, TVGN_CARET); SoftPCI_DisplayTreeMenu(&dn, hitinfo.pt); g_TreeLocked = FALSE; if (g_PendingRefresh) { g_PendingRefresh = FALSE; SoftPCI_CreateTreeView(); } } break; default: return CallWindowProc((WNDPROC)g_DefTreeWndProc, Wnd, Message, wParam, lParam); } return 0; } PPCI_DN SoftPCI_GetDnFromTreeItem( IN HTREEITEM TreeItem ) /*++ Routine Description: This routine returns a PCI_DN for either the currently selected TreeItem or the one specified by the caller. Arguments: TreeItem - Handle to TreeItem we want to query. If NULL then we default to current selection. Return Value: return value will be TV_ITEM.lParam value --*/ { TV_ITEM tviItem; tviItem.mask = TVIF_PARAM; tviItem.hItem = (TreeItem ? TreeItem : TreeView_GetSelection(g_TreeViewWnd)); tviItem.lParam = 0; TreeView_GetItem(g_TreeViewWnd, &tviItem); SOFTPCI_ASSERT(((PPCI_DN)tviItem.lParam) != NULL); return (PPCI_DN)tviItem.lParam; } VOID SoftPCI_InsertTreeItem( IN PPCI_DN Pdn, IN HTREEITEM HtiParent ) /*++ Routine Description: This routine takes our tree of PCI_DN structs and builds the UI representaion of it. Arguments: Pdn Current Pdn being intserted HtiParent The HTREEITEM that is to be the parent of this Pdn Return Value: none --*/ { PPCI_DN childDevNode; PPCI_DN siblingDevNode; TV_INSERTSTRUCT tvInsertStruct; HTREEITEM htiNewParent; TV_ITEM tvi; INT index; ULONG problem; SOFTPCI_ASSERT(Pdn != NULL); do { childDevNode = Pdn->Child; siblingDevNode = Pdn->Sibling; // // Get the parent item, and tell it it has children now // if (HtiParent != TVI_ROOT) { tvi.mask = TVIF_CHILDREN; tvi.hItem = HtiParent; TreeView_GetItem(g_TreeViewWnd, &tvi); // // Increment the ChildCount; // ++tvi.cChildren; TreeView_SetItem(g_TreeViewWnd, &tvi); } // // Add This Device at the current Level // tvInsertStruct.hParent = HtiParent; tvInsertStruct.hInsertAfter = TVI_LAST; tvInsertStruct.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_CHILDREN | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_STATE; tvInsertStruct.item.cChildren = 0; tvInsertStruct.item.lParam = (ULONG_PTR) Pdn; tvInsertStruct.item.state = INDEXTOOVERLAYMASK(0); // // If the device has a problem make let's reflect so.... // if (SoftPCI_GetDeviceNodeProblem(Pdn->DevNode, &problem)){ if (problem == CM_PROB_DISABLED) { tvInsertStruct.item.state = INDEXTOOVERLAYMASK(IDI_DISABLED_OVL - IDI_CLASSICON_OVERLAYFIRST + 1); }else{ tvInsertStruct.item.state = INDEXTOOVERLAYMASK(IDI_PROBLEM_OVL - IDI_CLASSICON_OVERLAYFIRST + 1); } } tvInsertStruct.item.stateMask = TVIS_OVERLAYMASK | TVIS_CUT; tvInsertStruct.item.pszText = (LPTSTR) Pdn->FriendlyName; // // Figure out which icon goes which each device. // if (SetupDiGetClassImageIndex(&Pdn->PciTree->ClassImageListData, &Pdn->DevInfoData.ClassGuid, &index)){ tvInsertStruct.item.iImage = tvInsertStruct.item.iSelectedImage = index ; }else{ tvInsertStruct.item.iImage = tvInsertStruct.item.iSelectedImage = -1 ; } htiNewParent = TreeView_InsertItem(g_TreeViewWnd, &tvInsertStruct); if (g_PciTree->RootTreeItem == NULL) { g_PciTree->RootTreeItem = htiNewParent; } // // if this device has a child lets walk them next // if (childDevNode){ SoftPCI_InsertTreeItem(childDevNode, htiNewParent); } }while ((Pdn = siblingDevNode) != NULL); } VOID SoftPCI_ExpandItem( IN HTREEITEM Hti, IN PULONG Data1, IN PULONG Data2 ) { // // Expand this item. // TreeView_Expand(g_TreeViewWnd, Hti, TVE_EXPAND); } VOID SoftPCI_RestoreSelection( IN HTREEITEM Hti, IN PVOID Data1, IN PVOID Data2 ) { PWCHAR slotPath, p; PPCI_DN pdn; PBOOL selectionFound; selectionFound = (PBOOL)Data1; pdn = SoftPCI_GetDnFromTreeItem(Hti); if (pdn == NULL) { return; } slotPath = SoftPCI_GetPciPathFromDn(pdn); if ((wcscmp(slotPath, g_LastSelection)) == 0) { // // Restore the selection to this point. // TreeView_Select(g_TreeViewWnd, Hti, TVGN_CARET); TreeView_EnsureVisible(g_TreeViewWnd, Hti); *selectionFound = TRUE; } free(slotPath); } VOID SoftPCI_WalkTree( IN HTREEITEM Hti, IN PSOFTPCI_TREECALLBACK TreeCallback, IN PVOID Arg1, IN PVOID Arg2 ) { if (Hti) { // // Call the CallBack. // (*TreeCallback)(Hti, Arg1, Arg2); // // Call this on my first child. // SoftPCI_WalkTree(TreeView_GetChild(g_TreeViewWnd, Hti), TreeCallback, Arg1, Arg2 ); // // Call this on my first sibling. // SoftPCI_WalkTree(TreeView_GetNextSibling(g_TreeViewWnd, Hti), TreeCallback, Arg1, Arg2 ); } } #if 0 VOID SoftPCI_GetDnFromTree( IN HTREEITEM Hti, IN OUT PVOID Pdn, //PPCI_DN * IN PVOID PdnToFind ) { TV_ITEM tvi; PPCI_DN pdn = NULL; PPCI_DN pdnToFind = (PPCI_DN)PdnToFind; tvi.lParam = 0; tvi.hItem = Hti; TreeView_GetItem(g_TreeViewWnd, &tvi); pdn = (PPCI_DN)tvi.lParam; if (pdn) { if ((pdnToFind->Bus == pdn->Bus) && (pdnToFind->Device == pdn->Device) && (pdnToFind->Function == pdn->Function)) { *(PPCI_DN *)Pdn = pdn; } } } #endif