示例#1
0
BOOL
CDeviceView::ListDevicesByConnection()
{
    WCHAR DeviceName[DEVICE_NAME_LEN];
    INT ClassImage;
    BOOL bSuccess;

    /* Get the details of the root of the device tree */
    bSuccess = m_Devices->GetDeviceTreeRoot(DeviceName, DEVICE_NAME_LEN, &ClassImage);
    if (bSuccess)
    {
        /* Add the root of the device tree to the treeview */
        m_hTreeRoot = InsertIntoTreeView(NULL,
                                         DeviceName,
                                         NULL,
                                         ClassImage,
                                         0);
    }

    /* Walk the device tree and add all the devices */
    RecurseChildDevices(m_Devices->GetRootDevice(), m_hTreeRoot);

    /* Expand the root item */
    (VOID)TreeView_Expand(m_hTreeView,
                          m_hTreeRoot,
                          TVE_EXPAND);

    return TRUE;
}
示例#2
0
HTREEITEM
InitTreeView(HWND hTreeView)
{
    HTREEITEM hRoot;
    TCHAR ComputerName[MAX_PATH];
    DWORD dwSize = MAX_PATH;
    INT RootImage;

    (void)TreeView_DeleteAllItems(hTreeView);

    /* Get the device image list */
    ImageListData.cbSize = sizeof(ImageListData);
    SetupDiGetClassImageList(&ImageListData);

    (void)TreeView_SetImageList(hTreeView,
                                ImageListData.ImageList,
                                TVSIL_NORMAL);

    if (!GetComputerName(ComputerName,
                         &dwSize))
    {
        ComputerName[0] = _T('\0');
    }

    /* Get the image index of the computer class */
    SetupDiGetClassImageIndex(&ImageListData,
                              &GUID_DEVCLASS_COMPUTER,
                              &RootImage);

    /* Insert the root item into the tree */
    hRoot = InsertIntoTreeView(hTreeView,
                               NULL,
                               ComputerName,
                               NULL,
                               RootImage,
                               0);

    return hRoot;
}
示例#3
0
bool
CDeviceView::RecurseChildDevices(
    _In_ DEVINST ParentDevice,
    _In_ HTREEITEM hParentTreeItem
    )
{
    HTREEITEM hDevItem = NULL;
    DEVINST Device;
    bool HasProblem = false;
    bool bSuccess;

    // Check if the parent has any child devices 
    if (GetChildDevice(ParentDevice, &Device) == FALSE)
        return true;

    // Get the cached device node
    CDeviceNode *DeviceNode;
    DeviceNode = dynamic_cast<CDeviceNode *>(GetDeviceNode(Device));
    if (DeviceNode == nullptr)
    {
        ATLASSERT(FALSE);
        return false;
    }

    // Don't show hidden devices if not requested
    if ((m_ShowHidden == TRUE) || (!(DeviceNode->IsHidden())))
    {
        // Add this device to the tree under its parent 
        hDevItem = InsertIntoTreeView(hParentTreeItem,
                                      DeviceNode);
        if (hDevItem)
        {
            // Check if this child has any children itself 
            if (!RecurseChildDevices(Device, hDevItem))
                HasProblem = true;
        }

        if (DeviceNode->HasProblem())
        {
            HasProblem = true;
        }
    }


    // Check for siblings
    for (;;)
    {
        // Check if the parent device has anything at the same level 
        bSuccess = GetSiblingDevice(Device, &Device);
        if (bSuccess == FALSE) break;

        DeviceNode = dynamic_cast<CDeviceNode *>(GetDeviceNode(Device));
        if (DeviceNode == nullptr)
        {
            ATLASSERT(FALSE);
        }

        // Don't show hidden devices if not requested
        if ((m_ShowHidden == TRUE) || (!(DeviceNode->IsHidden())))
        {
            if (DeviceNode->HasProblem())
            {
                HasProblem = true;
            }

            // Add this device to the tree under its parent 
            hDevItem = InsertIntoTreeView(hParentTreeItem,
                                          DeviceNode);
            if (hDevItem)
            {
                // Check if this child has any children itself 
                if (!RecurseChildDevices(Device, hDevItem))
                    HasProblem = true;
            }
        }
    }

    (void)TreeView_SortChildren(m_hTreeView,
                                hParentTreeItem,
                                0);

    // Expand the class if it has a problem device
    if (HasProblem == true)
    {
        (void)TreeView_Expand(m_hTreeView,
                              hParentTreeItem,
                              TVE_EXPAND);
    }

    // If there was a problem, expand the ancestors
    if (HasProblem) return false;

    return true;
}
示例#4
0
bool
CDeviceView::ListDevicesByType()
{
    CClassNode *ClassNode;
    CDeviceNode *DeviceNode;
    HDEVINFO hDevInfo;
    HTREEITEM hTreeItem = NULL;
    GUID ClassGuid;
    INT ClassIndex;
    BOOL bClassSuccess, bSuccess;

    ClassIndex = 0;
    do
    {
        // Loop through all the device classes
        bClassSuccess = GetNextClass(ClassIndex, &ClassGuid, &hDevInfo);
        if (bClassSuccess)
        {
            bool bClassUnknown = false;
            bool AddedParent = false;
            INT DeviceIndex = 0;
            bool MoreItems = false;

            // Get the cached class node
            ClassNode = GetClassNode(&ClassGuid);
            if (ClassNode == nullptr)
            {
                ATLASSERT(FALSE);
                ClassIndex++;
                continue;
            }

            // Set a flag is this is the (special case) unknown class
            if (IsEqualGUID(ClassGuid, GUID_DEVCLASS_UNKNOWN))
                bClassUnknown = true;

            // Check if this is a hidden class
            if (IsEqualGUID(ClassGuid, GUID_DEVCLASS_LEGACYDRIVER) ||
                IsEqualGUID(ClassGuid, GUID_DEVCLASS_VOLUME))
            {
                // Ignore this device if we aren't displaying hidden devices
                if (m_ShowHidden == FALSE)
                {
                    ClassIndex++;
                    continue;
                }
            }

            do
            {
                // Get a handle to all the devices in this class
                SP_DEVINFO_DATA DeviceInfoData;
                ZeroMemory(&DeviceInfoData, sizeof(SP_DEVINFO_DATA));
                DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
                bSuccess = SetupDiEnumDeviceInfo(hDevInfo,
                                                 DeviceIndex,
                                                 &DeviceInfoData);
                if (bSuccess == FALSE && GetLastError() == ERROR_NO_MORE_ITEMS)
                    MoreItems = false;

                if (bSuccess)
                {
                    MoreItems = true;

                    // The unknown class handle contains all devices on the system,
                    // and we're just looking for the ones with a null GUID
                    if (bClassUnknown)
                    {
                        if (IsEqualGUID(DeviceInfoData.ClassGuid, GUID_NULL) == FALSE)
                        {
                            // This is a known device, we aren't interested in it
                            DeviceIndex++;
                            continue;
                        }
                    }

                    // Get the cached device node
                    DeviceNode = GetDeviceNode(DeviceInfoData.DevInst);
                    if (DeviceNode == nullptr)
                    {
                        ATLASSERT(bClassUnknown == true);
                        DeviceIndex++;
                        continue;
                    }

                    // Check if this is a hidden device
                    if (DeviceNode->IsHidden())
                    {
                        // Ignore this device if we aren't displaying hidden devices
                        if (m_ShowHidden == FALSE)
                        {
                            DeviceIndex++;
                            continue;
                        }
                    }

                    // We have a device, we need to add the parent if it hasn't yet been added
                    if (AddedParent == false)
                    {
                        // Insert the new class under the root item
                        hTreeItem = InsertIntoTreeView(m_hTreeRoot,
                                                       ClassNode);
                        AddedParent = true;
                    }

                    // Add the device under the class item node
                    (void)InsertIntoTreeView(hTreeItem, DeviceNode);

                    // Expand the class if it has a problem device
                    if (DeviceNode->HasProblem())
                    {
                        (void)TreeView_Expand(m_hTreeView,
                                              hTreeItem,
                                              TVE_EXPAND);
                    }
                }

                DeviceIndex++;

            } while (MoreItems);

            // If this class has devices, sort them alphabetically
            if (AddedParent == true)
            {
                (void)TreeView_SortChildren(m_hTreeView,
                                            hTreeItem,
                                            0);
            }
        }

        ClassIndex++;

    } while (bClassSuccess);

    // Sort the classes alphabetically
    (void)TreeView_SortChildren(m_hTreeView,
                                m_hTreeRoot,
                                0);

    // Expand the root item
    (void)TreeView_Expand(m_hTreeView,
                          m_hTreeRoot,
                          TVE_EXPAND);

    // Pre-select the root item
    (VOID)TreeView_SelectItem(m_hTreeView,
                              m_hTreeRoot);

    return 0;
}
示例#5
0
bool
CDeviceView::AddRootDevice()
{
    m_hTreeRoot = InsertIntoTreeView(NULL, m_RootNode);
    return (m_hTreeRoot != NULL);
}
示例#6
0
static HTREEITEM
AddDeviceToTree(HWND hTreeView,
                HTREEITEM hRoot,
                DEVINST dnDevInst,
                BOOL bShowHidden)
{
    TCHAR DevName[MAX_DEV_LEN];
    TCHAR FriendlyName[MAX_DEV_LEN];
    TCHAR ClassGuidString[MAX_GUID_STRING_LEN];
    GUID ClassGuid;
    ULONG ulLength;
    LPTSTR DeviceID = NULL;
    INT ClassImage = 24;
    CONFIGRET cr;

    ulLength = MAX_GUID_STRING_LEN * sizeof(TCHAR);
    cr = CM_Get_DevNode_Registry_Property(dnDevInst,
                                          CM_DRP_CLASSGUID,
                                          NULL,
                                          ClassGuidString,
                                          &ulLength,
                                          0);
    if (cr == CR_SUCCESS)
    {
        pSetupGuidFromString(ClassGuidString, &ClassGuid);

        if (bShowHidden == FALSE &&
            (IsEqualGUID(&ClassGuid, &GUID_DEVCLASS_LEGACYDRIVER) ||
             IsEqualGUID(&ClassGuid, &GUID_DEVCLASS_VOLUME)))
            return NULL;
    }
    else
    {
        /* It's a device with no driver */
        ClassGuid = GUID_DEVCLASS_UNKNOWN;
    }

    cr = CM_Get_Device_ID(dnDevInst,
                          DevName,
                          MAX_DEV_LEN,
                          0);
    if (cr != CR_SUCCESS)
        return NULL;

    ulLength = MAX_DEV_LEN * sizeof(TCHAR);
    cr = CM_Get_DevNode_Registry_Property(dnDevInst,
                                          CM_DRP_FRIENDLYNAME,
                                          NULL,
                                          FriendlyName,
                                          &ulLength,
                                          0);
    if (cr != CR_SUCCESS)
    {
        ulLength = MAX_DEV_LEN * sizeof(TCHAR);
        cr = CM_Get_DevNode_Registry_Property(dnDevInst,
                                              CM_DRP_DEVICEDESC,
                                              NULL,
                                              FriendlyName,
                                              &ulLength,
                                              0);
        if (cr != CR_SUCCESS)
            return NULL;
    }

    if (!SetupDiGetClassImageIndex(&ImageListData,
                                   &ClassGuid,
                                   &ClassImage))
    {
        /* FIXME: can we do this?
         * Set the blank icon: IDI_SETUPAPI_BLANK = 41
         * it'll be image 24 in the imagelist */
        ClassImage = 24;
    }

    if (DevName != NULL)
    {
        DeviceID = HeapAlloc(GetProcessHeap(),
                             0,
                             (lstrlen(DevName) + 1) * sizeof(TCHAR));
        if (DeviceID == NULL)
        {
            return NULL;
        }

        lstrcpy(DeviceID, DevName);
    }

    return InsertIntoTreeView(hTreeView,
                              hRoot,
                              FriendlyName,
                              DeviceID,
                              ClassImage,
                              0);
}
示例#7
0
static
VOID
EnumDevices(
    HWND hTreeView,
    PDEVCLASS_ENTRY pClassArray,
    ULONG ulClassCount,
    BOOL bShowHidden)
{
    HDEVINFO hDevInfo =  INVALID_HANDLE_VALUE;
    SP_DEVINFO_DATA DeviceInfoData;
    ULONG Status, Problem;
    DWORD DevIdSize;
    TCHAR DeviceName[MAX_DEV_LEN];
    DWORD DevIndex;
    LPTSTR InstanceId;
    PDEVCLASS_ENTRY pClass;
    UINT OverlayImage;
    CONFIGRET cr;


    /* Get device info for all devices of a particular class */
    hDevInfo = SetupDiGetClassDevs(NULL,
                                   NULL,
                                   NULL,
                                   DIGCF_PRESENT | DIGCF_ALLCLASSES);
    if (hDevInfo == INVALID_HANDLE_VALUE)
        return;

    for (DevIndex = 0; ; DevIndex++)
    {
        ZeroMemory(&DeviceInfoData, sizeof(SP_DEVINFO_DATA));
        DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

        InstanceId = NULL;
        DeviceName[0] = _T('\0');
        OverlayImage = 0;

        if (!SetupDiEnumDeviceInfo(hDevInfo,
                                   DevIndex,
                                   &DeviceInfoData))
            break;

        if (bShowHidden == FALSE &&
            (IsEqualGUID(&DeviceInfoData.ClassGuid, &GUID_DEVCLASS_LEGACYDRIVER) ||
             IsEqualGUID(&DeviceInfoData.ClassGuid, &GUID_DEVCLASS_VOLUME)))
            continue;

        pClass = GetClassFromClassGuid(pClassArray,
                                       ulClassCount,
                                       &DeviceInfoData.ClassGuid);

        /* get the device ID */
        if (!SetupDiGetDeviceInstanceId(hDevInfo,
                                        &DeviceInfoData,
                                        NULL,
                                        0,
                                        &DevIdSize))
        {
            if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
            {
                InstanceId = (LPTSTR)HeapAlloc(GetProcessHeap(),
                                             0,
                                             DevIdSize * sizeof(TCHAR));
                if (InstanceId != NULL)
                {
                    if (!SetupDiGetDeviceInstanceId(hDevInfo,
                                                    &DeviceInfoData,
                                                    InstanceId,
                                                    DevIdSize,
                                                    NULL))
                    {
                        HeapFree(GetProcessHeap(),
                                 0,
                                 InstanceId);
                        InstanceId = NULL;
                    }
                }
            }
        }

        /* Skip the root device */
        if (InstanceId != NULL &&
            _tcscmp(InstanceId, _T("HTREE\\ROOT\\0")) == 0)
        {
            HeapFree(GetProcessHeap(),
                     0,
                     InstanceId);
            InstanceId = NULL;
            continue;
        }

        /* Get the device's friendly name */
        if (!SetupDiGetDeviceRegistryProperty(hDevInfo,
                                              &DeviceInfoData,
                                              SPDRP_FRIENDLYNAME,
                                              0,
                                              (BYTE*)DeviceName,
                                              MAX_DEV_LEN,
                                              NULL))
        {
            /* If the friendly name fails, try the description instead */
            SetupDiGetDeviceRegistryProperty(hDevInfo,
                                             &DeviceInfoData,
                                             SPDRP_DEVICEDESC,
                                             0,
                                             (BYTE*)DeviceName,
                                             MAX_DEV_LEN,
                                             NULL);
        }

        cr = CM_Get_DevNode_Status_Ex(&Status,
                                      &Problem,
                                      DeviceInfoData.DevInst,
                                      0,
                                      NULL);
        if ((cr == CR_SUCCESS) &&
            (Status & DN_HAS_PROBLEM))
        {
            if (Problem == CM_PROB_DISABLED ||
                Problem == CM_PROB_HARDWARE_DISABLED)
                OverlayImage = 2;
            else
                OverlayImage = 1;
        }

        InsertIntoTreeView(hTreeView,
                           pClass->hItem,
                           DeviceName,
                           InstanceId,
                           pClass->ClassImage,
                           OverlayImage);

        if (OverlayImage != 0)
        {
             /* Expand the class if the device has a problem */
             (void)TreeView_Expand(hTreeView,
                                   pClass->hItem,
                                   TVE_EXPAND);
        }

        pClass->bUsed = TRUE;
    }

    if (hDevInfo !=  INVALID_HANDLE_VALUE)
        SetupDiDestroyDeviceInfoList(hDevInfo);
}
示例#8
0
static
VOID
EnumDeviceClasses(
    HWND hTreeView,
    HTREEITEM hRoot,
    PDEVCLASS_ENTRY pClassArray,
    ULONG ClassCount)
{
    WCHAR ClassName[MAX_DEV_LEN];
    WCHAR ClassDesc[MAX_DEV_LEN];
    PDEVCLASS_ENTRY pClass;
    ULONG ClassIndex;
    DWORD dwSize;
    LONG lSize;
    HKEY hKey;
    CONFIGRET cr;

    for (ClassIndex = 0; ClassIndex < ClassCount; ClassIndex++)
    {
        pClass = &pClassArray[ClassIndex];

        cr = CM_Enumerate_Classes(ClassIndex,
                                  &pClass->ClassGuid,
                                  0);
        if (cr == CR_NO_SUCH_VALUE)
            return;

        dwSize = MAX_CLASS_NAME_LEN;
        if (!SetupDiClassNameFromGuid(&pClass->ClassGuid,
                                      ClassName,
                                      dwSize,
                                      &dwSize))
        {
            ClassName[0] = _T('\0');
        }

        if (!SetupDiGetClassImageIndex(&ImageListData,
                                       &pClass->ClassGuid,
                                       &pClass->ClassImage))
        {
            /* FIXME: can we do this?
             * Set the blank icon: IDI_SETUPAPI_BLANK = 41
             * it'll be image 24 in the imagelist */
            pClass->ClassImage = 24;
        }

        hKey = SetupDiOpenClassRegKeyEx(&pClass->ClassGuid,
                                        MAXIMUM_ALLOWED,
                                        DIOCR_INSTALLER,
                                        NULL,
                                        0);
        if (hKey != INVALID_HANDLE_VALUE)
        {
            lSize = MAX_DEV_LEN;
            if (RegQueryValue(hKey,
                              NULL,
                              ClassDesc,
                              &lSize) != ERROR_SUCCESS)
            {
                ClassDesc[0] = _T('\0');
            }

            RegCloseKey(hKey);
        }

        pClass->hItem = InsertIntoTreeView(hTreeView,
                                           hRoot,
                                           (ClassDesc[0] != _T('\0')) ? ClassDesc : ClassName,
                                           NULL,
                                           pClass->ClassImage,
                                           0);
    }
}
示例#9
0
VOID
CDeviceView::RecurseChildDevices(
    _In_ DEVINST ParentDevice,
    _In_ HTREEITEM hParentTreeItem
    )
{
    HTREEITEM hDevItem = NULL;
    DEVINST Device;
    WCHAR DeviceName[DEVICE_NAME_LEN];
    LPTSTR DeviceId = NULL;
    INT ClassImage;
    BOOL IsUnknown = FALSE;
    BOOL IsHidden = FALSE;
    ULONG DeviceStatus = 0;
    ULONG ProblemNumber = 0;
    UINT OverlayImage = 0;
    BOOL bSuccess;

    /* Check if the parent has any child devices */
    if (m_Devices->GetChildDevice(ParentDevice, &Device) == FALSE)
        return;

    /* Lookup the info about this device */
    bSuccess = m_Devices->GetDevice(Device,
                                    DeviceName,
                                    DEVICE_NAME_LEN,
                                    &DeviceId,
                                    &ClassImage,
                                    &DeviceStatus,
                                    &ProblemNumber);
    if (bSuccess)
    {
        /* Check if this is a hidden device */
        if ((m_ShowHidden == TRUE) || (!(DeviceStatus & DN_NO_SHOW_IN_DM)))
        {
            /* Check if the device has a problem */
            if (DeviceStatus & DN_HAS_PROBLEM)
            {
                OverlayImage = 1;
            }

            /* The disabled overlay takes precidence over the problem overlay */
            if (ProblemNumber == CM_PROB_HARDWARE_DISABLED)
            {
                OverlayImage = 2;
            }

            /* Add this device to the tree under its parent */
            hDevItem = InsertIntoTreeView(hParentTreeItem,
                                          DeviceName,
                                          (LPARAM)DeviceId,
                                          ClassImage,
                                          0);


            if (hDevItem)
            {
                /* Check if this child has any children itself */
                RecurseChildDevices(Device, hDevItem);
            }
        }
    }


    for (;;)
    {
        /* Check if the parent device has anything at the same level */
        bSuccess = m_Devices->GetSiblingDevice(Device, &Device);
        if (bSuccess == FALSE) break;

        /* Lookup the info about this device */
        bSuccess = m_Devices->GetDevice(Device,
                                        DeviceName,
                                        DEVICE_NAME_LEN,
                                        &DeviceId,
                                        &ClassImage,
                                        &DeviceStatus,
                                        &ProblemNumber);
        if (bSuccess)
        {
            /* Check if this is a hidden device */
            if (DeviceStatus & DN_NO_SHOW_IN_DM)
            {
                if (m_ShowHidden == FALSE)
                    continue;
            }

            /* Check if the device has a problem */
            if (DeviceStatus & DN_HAS_PROBLEM)
            {
                OverlayImage = 1;
            }

            /* The disabled overlay takes precidence over the problem overlay */
            if (ProblemNumber == CM_PROB_HARDWARE_DISABLED)
            {
                OverlayImage = 2;
            }


            /* Add this device to the tree under its parent */
            hDevItem = InsertIntoTreeView(hParentTreeItem,
                                            DeviceName,
                                            (LPARAM)DeviceId,
                                            ClassImage,
                                            0);
            if (hDevItem)
            {
                /* Check if this child has any children itself */
                RecurseChildDevices(Device, hDevItem);
            }
        }
    }

    (void)TreeView_SortChildren(m_hTreeView,
                                hParentTreeItem,
                                0);
}
示例#10
0
BOOL
CDeviceView::ListDevicesByType()
{
    HTREEITEM hDevItem = NULL;
    GUID ClassGuid;
    WCHAR ClassName[CLASS_NAME_LEN];
    WCHAR ClassDescription[CLASS_DESC_LEN];
    INT ClassIndex;
    INT ClassImage;
    LPTSTR DeviceId = NULL;
    BOOL bSuccess;


    /* Get the details of the root of the device tree */
    bSuccess = m_Devices->GetDeviceTreeRoot(ClassName, CLASS_NAME_LEN, &ClassImage);
    if (bSuccess)
    {
        /* Add the root of the device tree to the treeview */
        m_hTreeRoot = InsertIntoTreeView(NULL,
                                         ClassName,
                                         NULL,
                                         ClassImage,
                                         0);
    }

    /* If something went wrong, bail */
    if (m_hTreeRoot == NULL) return FALSE;

    ClassIndex = 0;
    do
    {
        /* Get the next device class */
        bSuccess = m_Devices->EnumClasses(ClassIndex,
                                          &ClassGuid,
                                          ClassName,
                                          CLASS_NAME_LEN,
                                          ClassDescription,
                                          CLASS_DESC_LEN,
                                          &ClassImage);
        if (bSuccess)
        {
            BOOL bDevSuccess, AddedParent;
            HANDLE Handle = NULL;
            WCHAR DeviceName[DEVICE_NAME_LEN];
            INT DeviceIndex = 0;
            BOOL MoreItems = FALSE;
            BOOL DeviceHasProblem = FALSE;
            ULONG DeviceStatus = 0;
            ULONG ProblemNumber = 0;
            ULONG OverlayImage = 0;

            AddedParent = FALSE;

            do
            {
                /* Enum all the devices that belong to this class */
                bDevSuccess = m_Devices->EnumDevicesForClass(&Handle,
                                                             &ClassGuid,
                                                             DeviceIndex,
                                                             &MoreItems,
                                                             DeviceName,
                                                             DEVICE_NAME_LEN,
                                                             &DeviceId,
                                                             &DeviceStatus,
                                                             &ProblemNumber);
                if (bDevSuccess)
                {
                    /* Check if this is a hidden device */
                    if (DeviceStatus & DN_NO_SHOW_IN_DM)
                    {
                        if (m_ShowHidden == FALSE)
                        {
                            DeviceIndex++;
                            continue;
                        }
                    }

                    /* Check if the device has a problem */
                    if (DeviceStatus & DN_HAS_PROBLEM)
                    {
                        DeviceHasProblem = TRUE;
                        OverlayImage = 1;
                    }

                    /* The disabled overlay takes precidence over the problem overlay */
                    if (ProblemNumber == CM_PROB_HARDWARE_DISABLED)
                    {
                        OverlayImage = 2;
                    }


                    /* We have a device, we're gonna need to add the parent first */
                    if (AddedParent == FALSE)
                    {
                        /* Insert the new class under the root item */
                        hDevItem = InsertIntoTreeView(m_hTreeRoot,
                                                      ClassDescription,
                                                      NULL,
                                                      ClassImage,
                                                      0);

                        /* Don't add it again */
                        AddedParent = TRUE;
                    }

                    /* Add the device under the class item */
                    (VOID)InsertIntoTreeView(hDevItem,
                                             DeviceName,
                                             (LPARAM)DeviceId,
                                             ClassImage,
                                             OverlayImage);

                    /* Check if there's a problem with the device */
                    if (DeviceHasProblem)
                    {
                        /* Expand the class */
                        (VOID)TreeView_Expand(m_hTreeView,
                                              hDevItem,
                                              TVE_EXPAND);
                    }
                }

                DeviceIndex++;

            } while (MoreItems);

            /* Check if this class has any devices */
            if (AddedParent == TRUE)
            {
                /* Sort the devices alphabetically */
                (VOID)TreeView_SortChildren(m_hTreeView,
                                            hDevItem,
                                            0);
            }
        }

        ClassIndex++;

    } while (bSuccess);

    /* Sort the classes alphabetically */
    (VOID)TreeView_SortChildren(m_hTreeView,
                                m_hTreeRoot,
                                0);

    /* Expand the root item */
    (VOID)TreeView_Expand(m_hTreeView,
                          m_hTreeRoot,
                          TVE_EXPAND);

    /* Pre-select the root item */
    (VOID)TreeView_SelectItem(m_hTreeView,
                              m_hTreeRoot);

    return 0;
}