Working with Devices: Retrieving Device Setup Class Names

We now have our GUID for the Setup Class. Maybe we want to get the name of the class. We can do that by using the following function:

Private Declare Ansi Function SetupDiClassNameFromGuidA Lib "setupapi.dll" (ByRef ClassGuid As Guid, ByVal ClassName As System.Text.StringBuilder, ByVal ClassNameSize As Integer, ByRef RequiredSize As IntPtr) As Boolean

I am explicitly calling the Ansi function. I believe that calling just the SetupDiClassNameFromGuid without the 'A' should default to the correct function ('A' or 'W'), but, I couldn't get that working correctly for some reason.

Here is the function that will return the name of the device setup class.



Imports System.Runtime.InteropServices
Imports System.ComponentModel

'Returns a Class name from its Guid

Private Function GetClassName(ByVal ClassGuid As Guid) As String
'We first get the Name's Length

Dim intRequiredSize As IntPtr
Dim Result As Boolean
Result = SetupDiClassNameFromGuidA(ClassGuid, Nothing, 0, intRequiredSize)
If Not Result Then
Dim intErrorCode As Integer = Marshal.GetLastWin32Error()
If intErrorCode <> 122 Then Throw (New Win32Exception(intErrorCode))
End If

'Now we instantiate that StringBuilder with the size and get the name

Dim ClassName As New System.Text.StringBuilder(intRequiredSize.ToInt32)
Result = SetupDiClassNameFromGuidA(ClassGuid, ClassName, intRequiredSize.ToInt32, intRequiredSize)
If Not Result Then Throw (New Win32Exception(Marshal.GetLastWin32Error))
Return ClassName.ToString
End Function

When retrieving strings and such it is fairly common to see that we first call the function without providing a buffer to store the data and then provide a buffer. The reason for this is because we will get back the required size by first calling the function as we did above.

Using the code from the previous post and this code I can show you the output from my computer.

25dbce51-6c8f-4a72-8a6d-b54c2b4fc835 WCEUSBS
36fc9e60-c465-11cf-8056-444553540000 USB
4658ee7e-f050-11d1-b6bd-00c04fa372a7 PnpPrinters
48721b56-6795-11d2-b1a8-0080c72e74a2 Dot4
49ce6ac8-6f86-11d2-b1e5-0080c72e74a2 Dot4Print
4d36e965-e325-11ce-bfc1-08002be10318 CDROM
4d36e966-e325-11ce-bfc1-08002be10318 Computer
4d36e967-e325-11ce-bfc1-08002be10318 DiskDrive
4d36e968-e325-11ce-bfc1-08002be10318 Display
4d36e969-e325-11ce-bfc1-08002be10318 fdc
4d36e96a-e325-11ce-bfc1-08002be10318 hdc
4d36e96b-e325-11ce-bfc1-08002be10318 Keyboard
4d36e96c-e325-11ce-bfc1-08002be10318 MEDIA
4d36e96d-e325-11ce-bfc1-08002be10318 Modem
4d36e96e-e325-11ce-bfc1-08002be10318 Monitor
4d36e96f-e325-11ce-bfc1-08002be10318 Mouse
4d36e970-e325-11ce-bfc1-08002be10318 MTD
4d36e971-e325-11ce-bfc1-08002be10318 MultiFunction
4d36e972-e325-11ce-bfc1-08002be10318 Net
4d36e973-e325-11ce-bfc1-08002be10318 NetClient
4d36e974-e325-11ce-bfc1-08002be10318 NetService
4d36e975-e325-11ce-bfc1-08002be10318 NetTrans
4d36e977-e325-11ce-bfc1-08002be10318 PCMCIA
4d36e978-e325-11ce-bfc1-08002be10318 Ports
4d36e979-e325-11ce-bfc1-08002be10318 Printer
4d36e97b-e325-11ce-bfc1-08002be10318 SCSIAdapter
4d36e97d-e325-11ce-bfc1-08002be10318 System
4d36e97e-e325-11ce-bfc1-08002be10318 Unknown
4d36e980-e325-11ce-bfc1-08002be10318 FloppyDisk
50127dc3-0f36-415e-a6cc-4cb3be910b65 Processor
50906cb8-ba12-11d1-bf5d-0000f805f530 MultiPortSerial
50dd5230-ba8a-11d1-bf5d-0000f805f530 SmartCardReader
533c5b84-ec70-11d2-9505-00c04f79deaf VolumeSnapshot
66f250d6-7801-4a64-b139-eea80a450b24 1394Debug
6bdd1fc1-810f-11d0-bec7-08002be2092f 1394
6bdd1fc5-810f-11d0-bec7-08002be2092f Infrared
6bdd1fc6-810f-11d0-bec7-08002be2092f Image
6d807884-7d21-11cf-801c-08002be10318 TapeDrive
71a27cdd-812a-11d0-bec7-08002be2092f Volume
72631e54-78a4-11d0-bcf7-00aa00b7b32a Battery
745a17a0-74d3-11d0-b6fe-00a0c90f57da HIDClass
7ebefbc0-3200-11d2-b4c2-00a0c9697d07 61883
8ecc055d-047f-11d1-a537-0000f8753ed1 LegacyDriver
a0a588a4-c46f-4b37-b7ea-c82fe89870c6 SDHost
a1aa288f-bd7d-4a38-bee3-7d7cbc3674d8 UsbPcCardReader
c06ff265-ae09-48f0-812c-16753d7cba83 Avc
c459df55-db08-11d1-b009-00a0c9081ff6 Enum1394
ce5939ae-ebde-11d0-b181-0000f8753ec4 MediumChanger
d45b1c18-c8fa-11d1-9f77-0000f805f530 NtApm
d48179be-ec20-11d1-b6b8-00c04fa372a7 SBP2
e0cbf06c-cd8b-4647-bb8a-263b43f0f974 Bluetooth
eec5ad98-8080-425f-922a-dabf3de3f69a WPD
feb8d079-0681-11d4-9531-0060089abc08 USB


Notice that there are two GUIDs that map to the name 'USB'. Device Setup Classes can use the same name for more than one GUID. To tell you the truth though, I'm not sure why. Also notice that some of these aren't standard. For example, 'UsbPcCardReader' is a class installed by the manufacturer of a USB PCMCIA Card reader.

Working with Devices: Enumerating Device Setup Classes

So as promised lets enumerate through the Device Setup Classes. Sometimes I may say Device Class. This may be ambiguous with Device Setup Class and Device Interface Class. Most times I mean Device Setup Class, but I will try to correct myself as I go.

So the easiest way to see the device setup classes on your computer is simply to view it from the Registry. Under HKLM\SYSTEM\CurrentControlSet\Control\Class you'll find several Subkeys. Each subkey represents a setup class by GUID. You can look at each one and see the name for the setup class.

For example look at the GUID {4D36E972-E325-11CE-BFC1-08002bE10318}. You should see that this correlates to the net class. 'net' translates into the friendly name 'Network adapters'. By expanding this key you'll see more subkeys. Each subkey represents a device that falls under the net class.

You may be asking yourself "why do I have so many listed when only a handful show up in device manager?" This is because a key is created at any time a device is added to the computer. Even if it is removed the key will remain. Also there is typically more than one device installed when a physical device is installed. Another reason is that programs can install non-physical devices like VPN Adapters. Finally many devices are simply hidden in the device manager.

So we can get at this through the function CM_Enumerate_Classes

Here is the declare statement that I used.

Private Declare Auto Function CM_Enumerate_Classes Lib "cfgmgr32.dll" (ByVal ClassIndex As Integer, ByRef ClassGuid As Guid, ByVal Reserved As Integer) As Integer

The first argument is an index. We'll start at 0 and then keep incrementing. The next argument is the Guid that is outputted. This is the Guid that is at that specific index. The last argument is reserved and a 0 should be passed.

Here is some code that will set an index at 0 and keep incrementing it until the result is not 0. Technically, I believe an error CR_NO_SUCH_VALUE will be returned. The Guid will be added to an array list and then that list is sorted and returned.

'Returns an array of all the Class Guids on the System
Private Function GetClassGuids() As ArrayList
Dim i As Integer = 0
Dim Result As Integer
Dim ClassGuid As Guid
Dim GuidList As New ArrayList
Do
Result = CM_Enumerate_Classes(i, ClassGuid, 0)
i += 1
If Result = 0 Then GuidList.Add(ClassGuid)
Loop Until Result <> 0
GuidList.Sort()
Return GuidList
End Function


So thats it.

Oh, this code was written in VS.NET 2003 for .NET v1.1

Working with Devices: General Overview

So lets take a look at what is the most common view of devices on windows

On the left you see the Device Manager. To get to this just right click on My Computer and click Manage then click on Device Manager. You can also add it to the mmc console like I did.

It shows a tree view with your computer listed at the root and devices under neath that.

Each Item under your computer is called a Device Setup Class. Your devices are members of a Device Setup Class.

So you can see the Printer Port device is a member of the Ports Setup Class.

Another Class that is used is called the Device Interface Class. This class basically defines how the device is connected to the computer.

Imagine that you have two CD-ROM Drives. One drive is connected through an IDE Cable to your motherboard and the other is connected by USB. This means that one CD-ROM drive would be in the USB Device interface Class and the other would be in a different Device Interface Class (IDE I guess). But, BOTH would be in the CD-ROM Setup Class.

In my limited experience it seems that for interacting with devices for actions such as getting information on them and disabling/enabling then you'll be using the Device Setup Classes. In the case of registering your application for notifications on devices being inserted and removed then you would be using the Device Interface Classes.

I'll also say that the Device Interface Classes aren't defined in one single location. I think that the issue is that the developer should know the Class that he'll be working with and therefore know things like the GUID and whatnot.

By comparison, the Device Setup Classes' GUIDs are quite readily accessible in one single location. That is what we'll be going over on the next post.

For more information on the Device Classes take a look at Microsoft's MSDN Article.

Working with Devices

I've written a couple programs that have used the SetupAPI and CfgMgr API. What made it difficult for me was understanding how to create the correct declare statement and then the proper usage. Normally the usage was given in C++. While I could read most of it, some parts were foreign to me.

So what I thought was that I would start to write on this blog a bit more and post some of the functions along with their use in some sort of sample. I'll try to start off with the functions with the least dependencies (preferably none) and then start adding more functions that utilize previously discussed functions.

Free MS Visual Studio 2005 Standard Edition

By viewing two labcasts at Microsoft and filling out the review sheet you can receive a free copy of VS 2005 Standard. I think you have to pay s&h. Not bad though.

http://www.microsoft.com/business/vb2005upgrade/labcasts.mspx