Click here to Skip to main content
15,860,972 members
Articles / Programming Languages / C#
Article

Hardware Helper Library for C#

Rate me:
Please Sign up or sign in to vote.
4.86/5 (80 votes)
30 Nov 2007CPOL10 min read 436.5K   30.3K   204   137
How to monitor, enable, and disable hardware devices from C#
Screenshot - HardwareHelper.gif

Introduction

A few days back I ran into an issue with a network device that required a "restart" from my C# service. Seemed simple enough, and while I was aware that this type of operation could be performed by spawning a new process and running the infamous "DEVCON", I really couldn't see why I wouldn't be able to wrap those same calls from my C# application. Maybe I'm funny that way. But, I already had a C# library I created a while back to monitor devices being added or dropped from the Windows Device Manager, so the enumeration portion of the equation was already in place. Really how hard could it be?

Honestly I expected that within an hour or two of actually starting, I'd be kicked back in the chair patting myself on the back while my application happily enabled and disabled devices until it's little heart was content. That was not the case. After digging around in the DDK and the SDK for a couple hours, I was reminded once again how humbling a career field we software engineers have chosen. Not one to give up, I banged my head on the table, left a five and a quarter inch floppy under my pillow for the code fairy, and scoured the news groups most of the weekend.

Not surprisingly eventually I had a headache and I never did catch sight of the code fairy. But finally scouring the news groups turned up a couple clues that got my foot in the door. From there it was just a matter of brute force. The result is a helper library that can both monitor devices being added to or removed from the system, as well as a method that can successfully enable or disable any node on the device tree. After so much trouble for a seemingly simple task I figured why not share that library with you, my fellow code warriors. So without further ado, say hello to my little friend, the HardwareHelperLib.

Background

There isn't really a lot of background required for you to use the library I've provided. The heart of HardwareHelperLib is the unmanaged SetupAPI.DLL that is part of the platform SDK. Properly declaring and marshalling the API was the tough part and the whole reason I'm posting this code is so you don't have to spend your whole weekend banging your head against the table too. It's ALWAYS helpful to understand how an operating system interacts with the hardware and if you've never looked at the source code for DEVCON or some of the other drivers in the DDK it certainly wouldn't hurt you, (well it might hurt a little if you've strayed from C++ in favor of C# for a few years), but regardless whether you understand the inner-workings of the class or not you still should be able to use it. The example is very simplistic. The only prerequisite assumed is that you are at least somewhat familiar with Windows messages.

Using the Code

To use the HardwareHelperLib, just add the HardwareHelperLib.csproj file to your solution, and then reference that project in the studio. Add a using clause to the beginning of your source file and you will be ready to go.

C#
using HardwareHelperLib;

The next step is to declare a single global instance of the class for your application. Yes, I could have made the class a singleton pattern but let's face it, I was writing it for something fairly specific to what I was doing and as such wasn't all that worried about how someone else might choose to use or abuse it as the case may be. If you absolutely can't stop yourself from declaring two instances of the HardwareHelperLib, feel free to implement a Singleton or any other design pattern of your choosing. In the mean time the following should suffice.

C#
HH_Lib hwh = new HH_Lib();

Once we have our helper lib, there are basically only four exposed methods. The first is GetAll which returns an array of strings. The result set contains an inventory of all the hardware devices present on your system. These are devices that are physically PRESENT (or at least Windows believes are physically present in the case of software only or virtual devices). It does not matter if the devices are enabled or disabled in the local or global device profile. The syntax is straight-forward.

C#
string[] HardwareList = hwh.GetAll();

It's now easy to loop through and display the same text description for each device that you would see if you opened the Windows Device Manager. In the example below, I'm doing just that, adding the text to a standard list box.

C#
foreach (string s in HardwareList)
{
   listBox1.Items.Add(s);
}

Having an initial list of all the hardware installed on the machine, we will now begin monitoring the system for any hardware changes. This is just a matter of overriding the default window message handler, and then calling the appropriate method in the class. I should note here that while according to the SDK you should be able to hook a window handle or a service handle, I was unsuccessful in getting the latter to operate properly. I have no doubt it can be done but as luck would have it in my service I only needed to enable or disable the devices, not monitor the addition or removal of them, so at the time I chose not to stress over it.

C#
protected override void WndProc(ref Message m)
{
  switch (m.Msg)
  {
    case HardwareHelperLib.Native.WM_DEVICECHANGE:
    {
      if (m.WParam.ToInt32() ==  HardwareHelperLib.Native.DBT_DEVNODES_CHANGED)
      {
        listBox1.Items.Clear();
        string[] HardwareList = hwh.GetAll();
        foreach (string s in HardwareList)
        {
           listBox1.Items.Add(s);
        }
        label1.Text = listBox1.Items.Count.ToString() + " Devices Attached";
      }
      break;
    }
  }
  base.WndProc(ref m);
}

As you can see, when the WM_DEVICECHANGE message arrives, we simply reload the entire list. There is no fancy method of determining which device was added or removed at this time, though I guess if reloading the entire list is a problem you could modify the library to keep up with the hardware list and simply pass the changed items as an event argument.

Once you override the default message handler, you still need to register for the WM_DEVICECHANGE message. I do this in my form load with a simple call to HookHardwareNotifications. The first parameter is a Windows handle (or service handle if I ever get that option working). The second parameter is a Boolean that specifies which of the two the former parameter represents. In other words, true if you are passing a window handle to the method and false if you are passing in a service handle.

C#
hwh.HookHardwareNotifications(this.Handle, true);

That's it for hardware monitoring. If you want to see for yourself, run the sample application and try connecting and disconnecting a USB device. I work a lot in shops that design hardware devices as well as software and so keeping a log of what was added and removed from a particular system is invaluable when troubleshooting.

The other functionality the library provides is that of enabling and disabling devices. Getting access to that feature is just a matter of calling SetDeviceState. In my example, I do this with the selected item from a list box, which I previously populated using GetAll.

C#
string[] devices = new string[1];
devices[0]= listBox1.SelectedItem.ToString();
hwh.SetDeviceState(devices, true);

The first parameter is an array of strings that contains the "match pattern" to let SetDeviceState know which devices are affected. The Boolean that follows is set to true for enabling a device, or false for disabling it.

There are a couple words of explanation required here to accurately describe how "match pattern" works. I realize perhaps it is not the most straight-forward way to select which devices are to be affected, but as I said when I started this I was doing the library for a fairly specific need I had. You are welcome to modify it to match the device criteria differently, or perhaps you'll even want to use the vendor and hardware ID numbers like DEVCON does. However for the purpose of this discussion, I will at least explain how my method works, and why I chose to implement it the way I did.

The array of strings that gets passed to SetDeviceState is a list of non-case sensitive exact string match conditions. And EVERY condition must return true for the device to be selected. The strings you pass in will be compared against an array of strings exactly like that returned by the GetAll() method. Let me give you some examples. Imagine what follows is a partial listing of the hardware tree currently in your device manager.

C#
Network Adapters
Intel(r) 82566DM Gigabit Network Connection
Intel(r) 82566DP Mobile Network Connection
Broadcom Pro 1000 Network Card

Now let us consider that you have arranged an array of "match patterns" to contain the following:

C#
match[0]="Intel"match[1]="Network"match[2]="Connection"

Calling SetDeviceState(match,false) would effectively disable all devices that contained all three match criteria in their description, or the first two adapters. Similarly an array containing:

C#
match[0]="Intel(r) 82566DM Gigabit Network Connection"

will ONLY disable the first adapter in the list when calling SetDeviceState. So far so good. This is probably what you expected and you are wondering why the heck I am spending so much time pointing out what you already deduced. In that case, ponder the following "match pattern" array for a moment.

C#
match[0]="Intel(r) 82566DM Gigabit Network Connection"
match[1]="Intel(r) 82566DP Mobile Network Connection"

At first glance, you might expect a call to SetDeviceState(match,false) to disable both the first two Ethernet adapters in the device manager's list. You would be wrong though. In fact NO devices will be affected because NONE of the devices contain exactly both of the above match criteria. Hopefully, this makes it clear why it is important to consider your match criteria carefully. Accidentally disabling a system-level device in your code or a network adapter you are currently using could have some pretty unpleasant side effects.

This might possibly leave you scratching your head asking why I just didn't use something concrete like the VEN and DEV (or VID and PID in the case of USB devices). The answer is again I was using the library for something I was doing, and that specific scenario required some flexibility. My service is running on a large number of servers, all of which have different networking hardware. I needed to disable many different models of a device within one particular family. What I didn't want to have to do was customize the list for each and every server the software was deployed to. So in that respect, albeit it is slightly confusing to explain to others, the "match pattern" array did just that.

With that said, I believe I have pretty much explained all there is to explain about how to use the library. Of course you'll need to turn off the hardware event monitoring before your application exits. I included the following method call in my form closing event.

C#
hwh.CutLooseHardwareNotifications(this.Handle);

The call will not fail if you are not currently "hooking" hardware notifications, so be sure you call this method before exiting. I say better safe than sorry. Such methods make use of unmanaged resources and it is important to give them the opportunity to perform a proper memory clean up before exiting.

Points of Interest

One final point of interest here is that some devices will require a reboot after enabling or disabling them before the change will go into effect. The unmanaged SetupAPI.Dll has two special flags DI_NEEDRESTART | DI_NEEDREBOOT which get combined with a logical OR operation and passed to SetupDiGetDeviceInstallParams to determine whether or not a reboot will be required. I already knew my devices did not require a restart, in fact if they did, in my particular situation this solution would not have been viable. However, if you want to know whether or not your device state change requires a reboot, I would think you could easily insert this functionality just by taking a look at how it is done inside of the DDK (again see the DEVCON source files).

History

  • Hardware Helper Lib (v)1.0.0 WJF 11/26/07: Original implementation
  • Hardware Lib Test (v)1.0.0 WJF 11/26/07: Original implementation

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
I began coding at the ripe old age of eleven (yep, I've always been a nerd). Back then every 8-bit computer on the market had its own flavor of BASIC burned into the EPROM and I was adept at most of them. Somewhere in my bedroom there was an actual bed, but you'd be hard pressed to find it surrounded as it was. My collection included a C64, VIC20, TRS80, APPLE II+, TI-99/4A, and even one of those silver Timex Sinclair "computers" with the chicklet keys.

Eventually I taught myself 6502 assembler, and later Pascal and C. While I spent the majority of my professional career doing a mixture of C++, C#, and dabbling in ARM Assembler, for the last year I've been focusing on JAVA and the Android Platform. While I am a Windows guy at heart lately I'm finding some love for UBUNTU as well.

When I am not at the computer I am hanging out with my 12 year old son. He just finished coding a javascript implementation of Conway's Game of Life. Oh yeah, I guess that means we were in front of the computer. Go figure!

Comments and Discussions

 
BugSome bugs in win 7(fixed) [modified] Pin
loyldg28-Aug-11 0:40
loyldg28-Aug-11 0:40 
QuestionCan I know the Status, Connectivity !!! Pin
All Time Programming17-Feb-11 2:03
All Time Programming17-Feb-11 2:03 
GeneralAnother Win7 Problem Pin
Max Jostkleigrewe13-Feb-11 23:22
Max Jostkleigrewe13-Feb-11 23:22 
GeneralRe: Another Win7 Problem Pin
alirezasob5-Apr-18 2:00
alirezasob5-Apr-18 2:00 
GeneralMy vote of 5 Pin
Markus Rauhecker6-Jan-11 7:24
Markus Rauhecker6-Jan-11 7:24 
Generaldoes not display correctly on Windows 7 Pin
tarantillo4-Dec-10 21:27
tarantillo4-Dec-10 21:27 
GeneralMy vote of 5 Pin
Shahan Ayyub4-Nov-10 9:18
Shahan Ayyub4-Nov-10 9:18 
GeneralWindows 7 Issue - possible fix Pin
Lee.W.Spencer21-Oct-10 6:26
Lee.W.Spencer21-Oct-10 6:26 
GeneralRe: Windows 7 Issue - possible fix Pin
Cheetah200531-Oct-10 4:27
Cheetah200531-Oct-10 4:27 
GeneralDoes not work with Win 7 32 OS Pin
user20515-Oct-10 11:08
user20515-Oct-10 11:08 
Generalnever set the size of the struct manual... Pin
Paw Jershauge30-Sep-10 3:57
Paw Jershauge30-Sep-10 3:57 
GeneralWindows 7 [modified] Pin
Manons_tm-kult1-Jul-10 3:33
Manons_tm-kult1-Jul-10 3:33 
GeneralRe: Windows 7 Pin
temp555630-Aug-10 19:38
temp555630-Aug-10 19:38 
GeneralWindows 7 64 Bits Pin
Van Roie Dominique22-May-10 20:54
Van Roie Dominique22-May-10 20:54 
GeneralRe: Windows 7 64 Bits Pin
164chris14-Aug-10 6:55
164chris14-Aug-10 6:55 
Generalthanks Pin
Member 35817572-Mar-10 20:17
Member 35817572-Mar-10 20:17 
QuestionHow can i get the Hardware Name Pin
youyuming30-Dec-09 16:08
youyuming30-Dec-09 16:08 
Thanks for this article it helps me a lot..
How can i get the Hardware Name(Production Model).
eg:CPU is Intel or AMD Detial and so on .

Thanks a lots !

You yuming
QuestionHow to control usb power on/off ? Pin
rainyeh20-Dec-09 19:22
rainyeh20-Dec-09 19:22 
AnswerRe: How to control usb power on/off ? Pin
rainyeh6-Jan-10 7:36
rainyeh6-Jan-10 7:36 
GeneralFix for vista Pin
BBBrrriiiaaannn11-Dec-09 16:20
BBBrrriiiaaannn11-Dec-09 16:20 
GeneralWindows Services Pin
tunder1421-Nov-09 18:17
tunder1421-Nov-09 18:17 
GeneralDevice details of the inserted or removed hardware Pin
santhosh rajashekar10-Sep-09 23:49
santhosh rajashekar10-Sep-09 23:49 
GeneralExcellent - working on a couple of changes Pin
scott.leckie21-Aug-09 4:24
scott.leckie21-Aug-09 4:24 
QuestionHow could I support matching DeviceID? Pin
yeahtuna19-Jul-09 15:54
yeahtuna19-Jul-09 15:54 
AnswerRe: How could I support matching DeviceID? Pin
yeahtuna10-Oct-09 5:35
yeahtuna10-Oct-09 5:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.