Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

.NET - Diving into System Programming - Part 1

0.00/5 (No votes)
8 Mar 2004 4  
Device configuration using C# (Part 1)

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
{
 /// <summary>

 /// Summary description for Class.

 /// </summary>

 class DeviceClasses
 {
  /// <summary>

  /// The main entry point for the application.

  /// </summary>

  public const int MAX_NAME_PORTS=7;
  public const int RegDisposition_OpenExisting=(0x00000001); 
    // open key only if exists

  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);
// MaximumAllowed access type to Reg.

  public const int MAXIMUM_ALLOWED = (0x02000000);
[StructLayout(LayoutKind.Sequential)]

 public class SP_DEVINFO_DATA
 {
 public int cbSize;
 public Guid ClassGuid;
 public int DevInst; // DEVINST handle

 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, //char * ?

   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;
   //incorrect device class:

   if(result == CR_INVALID_DATA)
   {
    return -2;
   }
  //device class is absent

   if(result == CR_NO_SUCH_VALUE)
   {
    return -1;
   }
  //bad param. - fatal error

   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).

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here