Introduction
In my previous articles, I have demonstrated applications for System programming. I remember how, many years ago I liked to dig into the system tables or verify system facility. Of course, it was not really a PC, but computers like IBM360 or PDP11 or microVAX but it was wonderful. Now after these years I still like to do things like COM interfaces, DLL libraries, hardware configuration etc. Now it is as interesting for me as well as before...Uh... System Programming... I will always love you.
Objectives
It is a sufficiently common opinion that C# is a "child language". I don't think so. In my opinion it is absolutely incorrect. So to disprove it I decided to write three applications that show information about hardware device configuration. I will demonstrate how C# can use Win32 API functions (even functions from DDK) to give access to information about configuration. Those who use C# know that it is possible via P/Invoke. Another problem that C# successfully solves is data marshalling from unstructured unmanaged data (commonly obtained as a result of execution of Win32 API functions) to managed structures. And for last it is possible to use the techniques discussed here with Windows Forms so the application can look pretty.
Device classes
All devices in the system join in the device classes. As you can see in the below picture, the class has name and Guid (so it can be found in Registry). The class can also have a description. For example, for class "Ports" the description is "Ports (COM & LPT)". Class also has devices that are present in the configuration.
System Device Manager gives information about classes (including hidden ones) that are present on PC:
Below you see code in C# that will enumerate all present device classes for PC. As I said this code uses P/Invoke for accessing DDK (cfgmgr32.dll) and SDK (setupapi.dll) DLLs.
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace DevClasses
{
class DeviceClasses
{
public const int MAX_NAME_PORTS=7;
public const int RegDisposition_OpenExisting=(0x00000001);
public const int CM_REGISTRY_HARDWARE=(0x00000000);
public const int CR_SUCCESS = (0x00000000);
public const int CR_NO_SUCH_VALUE = (0x00000025);
public const int CR_INVALID_DATA = (0x0000001F);
public const int DIGCF_PRESENT = (0x00000002);
public const int DIOCR_INSTALLER = (0x00000001);
public const int MAXIMUM_ALLOWED = (0x02000000);
[StructLayout(LayoutKind.Sequential)]
public class SP_DEVINFO_DATA
{
public int cbSize;
public Guid ClassGuid;
public int DevInst;
public ulong Reserved;
};
[DllImport("cfgmgr32.dll")]
public static extern UInt32
CM_Open_DevNode_Key(IntPtr dnDevNode, UInt32 samDesired,
UInt32 ulHardwareProfile,
UInt32 Disposition,IntPtr phkDevice, UInt32 ulFlags);
[DllImport("cfgmgr32.dll")]
public static extern UInt32
CM_Enumerate_Classes(UInt32 ClassIndex,ref Guid ClassGuid, UInt32 Params);
[DllImport("setupapi.dll")]
public static extern Boolean
SetupDiClassNameFromGuidA(ref Guid ClassGuid,
StringBuilder ClassName,
UInt32 ClassNameSize, ref UInt32 RequiredSize);
[DllImport("setupapi.dll")]
public static extern IntPtr
SetupDiGetClassDevsA(ref Guid ClassGuid, UInt32 Enumerator,
IntPtr hwndParent, UInt32 Flags);
[DllImport("setupapi.dll")]
public static extern Boolean
SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet, UInt32 MemberIndex,
ref SP_DEVINFO_DATA DeviceInfoData);
[DllImport("setupapi.dll")]
public static extern Boolean
SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);
[DllImport("setupapi.dll")]
public static extern IntPtr
SetupDiGetClassDevsA(ref Guid ClassGuid, UInt32 samDesired,
UInt32 Flags, ref string hwndParent, IntPtr Reserved);
[DllImport("setupapi.dll")]
public static extern IntPtr
SetupDiOpenClassRegKeyExA(
ref Guid ClassGuid, UInt32 samDesired, int Flags, IntPtr MachineName,
UInt32 Reserved);
[DllImport("advapi32.dll")]
public static extern UInt32
RegQueryValueA(IntPtr KeyClass,UInt32 SubKey,
StringBuilder ClassDescription,ref UInt32 sizeB);
[DllImport("user32.dll")]
public static extern Boolean
CharToOem(String lpszSrc, StringBuilder lpszDst);
public static int EnumerateClasses(UInt32 ClassIndex,
ref StringBuilder ClassName, StringBuilder ClassDescription,
ref bool DevicePresent)
{
Guid ClassGuid=Guid.Empty;
IntPtr NewDeviceInfoSet;
SP_DEVINFO_DATA DeviceInfoData;
UInt32 result;
StringBuilder name=new StringBuilder("");
bool resNam=false;
UInt32 RequiredSize=0;
IntPtr ptr;
result = CM_Enumerate_Classes(ClassIndex, ref ClassGuid,0);
ClassName=new StringBuilder("");
DevicePresent=false;
if(result == CR_INVALID_DATA)
{
return -2;
}
if(result == CR_NO_SUCH_VALUE)
{
return -1;
}
if(result != CR_SUCCESS)
{
return -3;
}
name.Capacity=0;
resNam=SetupDiClassNameFromGuidA(ref ClassGuid,name,RequiredSize,
ref RequiredSize);
if(RequiredSize > 0)
{
name.Capacity=(int)RequiredSize;
resNam=SetupDiClassNameFromGuidA(ref ClassGuid,name,
RequiredSize,ref RequiredSize);
}
NewDeviceInfoSet=SetupDiGetClassDevsA(
ref ClassGuid,
0,
IntPtr.Zero,
DIGCF_PRESENT);
if(NewDeviceInfoSet.ToInt32() == -1)
{ DevicePresent=false;
ClassName=name;
return 0;}
IntPtr KeyClass=SetupDiOpenClassRegKeyExA(
ref ClassGuid, MAXIMUM_ALLOWED, DIOCR_INSTALLER,IntPtr.Zero,0);
if(KeyClass.ToInt32() == -1)
{ DevicePresent=false;
ClassName=name;
return 0;}
UInt32 sizeB=1000;
String abcd="";
StringBuilder CD=new StringBuilder("");
ClassDescription.Capacity=1000;
UInt32 res=RegQueryValueA(KeyClass,0,ClassDescription,ref sizeB);
if(res != 0)ClassDescription=new StringBuilder("");
SetupDiDestroyDeviceInfoList(NewDeviceInfoSet);
ClassName=name;
DevicePresent=true;
return 0;
}
[STAThread]
static void Main(string[] args)
{
StringBuilder classes=new StringBuilder("");
StringBuilder classesDescr=new StringBuilder("");
StringBuilder classesDescrOEM=new StringBuilder("");
classesDescrOEM.Capacity=1000;
Boolean DevExist=false;
UInt32 i=0;
while(true)
{
int res=EnumerateClasses(i,ref classes,classesDescr,ref DevExist);
if(res == -1)break;
++i;
if(res < -1 || !DevExist)continue;
Console.WriteLine("ClassName={0}, Description={1}",classes,classesDescr);
}
return;
}
}
}
After running the application you will see all device classes on your PC (managed version; I tested for Win2000).