Click here to Skip to main content
Click here to Skip to main content

Hardware Helper Library for C#

By , 30 Nov 2007
 
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.

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.

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.

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.

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.

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.

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.

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.

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:

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:

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.

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.

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)

About the Author

wjfrancis
Software Developer (Senior)
United States United States
Member
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!

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralExcellent - working on a couple of changesmemberscott.leckie21 Aug '09 - 4:24 
Thanks for this - very interesting - 5 from me.
 
I'm adding a couple of enhancements, if you don't mind, including retrieval of other properties and a "diff" like view to show what changed as the list is refreshed (i.e. devices added in green or removed in red). If you're interested in the output, please let me know.
 
Cheers
Scott
QuestionHow could I support matching DeviceID?memberyeahtuna19 Jul '09 - 15:54 
First off I'd like to say thank-you for such a wonderful library. I was looking for a way to ditch using devcon.exe and this is the answer.
 
I'd like to match for DeviceID. Can you give me any hints as to where I should be looking to make the necessary changes?
AnswerRe: How could I support matching DeviceID?memberyeahtuna10 Oct '09 - 5:35 
I managed to get support for matching Hardware ID. I just added this const and passed it in the call to SetupDiGetDeviceRegistryProperty.
 
Const SPDRP_HARDWAREID As Long = (&H1)
 
You can also use other constants to get the devices friendly name, class and others. I was trying off and on for a couple days to read 'SPDRP_DEVTYPE which sure return a DWORD but had absolutely no luck. In the end I just used a combination 'SPDRP_CLASS' and segments of the 'SPDRP_HARDWAREID' to infer the type of device.
 
Thanks again for the wonderful code.
GeneralI won pnp drive arrived and removed time from usb to save in DBmemberPower-x13 May '09 - 7:52 
Problem I use your lib it's work. but I won modified this code for get pnp drive arrived time and removed of usb to save in DB .How to solve this problem . thank for this lib.
Generalcan't run in Vista!!!!!memberfuckyou-usa12 May '09 - 2:22 
thanks, but can't run in Vista.....
GeneralRe: can't run in Vista!!!!!memberneohacker107 Apr '10 - 23:37 
Runs on Win7 ... Could be due to your security settings
-NeO Hacker10

GeneralInteresting, but...memberwasouthpnt30 Apr '09 - 8:42 
What if you wanted to only monitor a specific category of devices such as COMPORTS or NETWORK ADAPTERS? To my knowledge there are no category names.
Thanks.
GeneralGreat got my five if u can help me i will be greatfulmemberHasbi Allah20 Apr '09 - 22:56 
it doesn't work on my laptop but works fine on the pc !! Cry | :((
GeneralRe: Great got my five if u can help me i will be greatfulmembernohear_t24 Apr '09 - 10:11 
Help you? You have not even posted anything helpful, like an error message.
QuestionCould not enable a devicememberMike Han17 Apr '09 - 5:29 
Hi, I tested this gr8 app on my Vista machine. This app works just fine, but it could not enable a device that I MANUALLY disabled from computer management. Do you have any idea?
AnswerRe: Could not enable a devicemember[MD]Valkyrie-MT18 Sep '10 - 7:46 
I noticed the same thing. A device disabled in Device Manager cannot be re-enabled by this lib...
GeneraldevconmemberSwapnil Shah10 Apr '09 - 2:18 
hi
can u send me devcon source code if u have??
i really need it.
mail id.:: shahswapnil963@yahoo.co.in
Thanks
GeneralRe: devconmembernohear_t15 Apr '09 - 10:03 
Create an account and download it like everyone else.
GeneralRe: devconmemberSwapnil Shah15 Apr '09 - 19:11 
I did.
Thanks for such a good article.
GeneralThis is Gr8, have one querymemberkisna_madhusudan10 Feb '09 - 1:39 
Hi,
 
This is really a Gr8 help.
 
I have one query...this lists the hardwares for the sytem on which we are running it. I have a requirement to get the list of all hardwares in network computer, i.e I have to input IP Address in textbox and on hit of a button need to display all hardwares of that Computer (IP Address in network).
 
Please suggest...thanks in advance....
 
Regards,
Madhusudan
AnswerRe: This is Gr8, have one querymemberjeromydean11 Feb '09 - 8:11 
Have a look into WMI, that will do what you want.
GeneralGot my 5memberlpgray5 Feb '09 - 9:56 
Just what I've been looking for! Thanks.
GeneralGreate job , I rated 5 (Excellent)membersaeed mousavi14 Dec '08 - 23:54 
Previousley I thought this is only possible with C++, you showed me the better way , thank you Wink | ;)
AnswerModification: Adding 64-bit compatibility. [modified]memberjkhines5 Dec '08 - 8:12 
I made the following changes in order for the code to run on 64-bit:
  1. Added two break statements following //Skip comments per previous comment[^].
  2. Redefined SP_DEVINFO_DATA's "reserved" member from ulong to IntPtr.
  3. Changed SP_DEVINFO_DATA's "reserved" assignment in two places:
    • FROM: DeviceInfoData.reserved = 0;
    • TO: DeviceInfoData.reserved = IntPtr.Zero;
  4. Changed SP_DEVINFO_DATA's "cbSize" assignment in two places:
    • FROM: DeviceInfoData.cbSize = 28;
    • TO: DeviceInfoData.cbSize = Marshal.SizeOf(typeof(Native.SP_DEVINFO_DATA));
 
This has allowed the test harness to run and enable/disable devices on XP x64.
 
modified on Friday, December 5, 2008 2:29 PM

GeneralRe: Modification: Adding 64-bit compatibility.memberJink Welby1 Mar '09 - 16:06 
Your 64bit mods work on Server 2008 as well. Thanks to both of you Big Grin | :-D !
GeneralRe: Modification: Adding 64-bit compatibility.memberKornelis2k13 May '10 - 22:23 
If make this modifications it works on my Win7 64-bit system, but only if i compile the
Program to the target Platform x64 or Any CPU. If i switch manually to x86 it does
not change anything. As i have have to use libs which need x86 as target platform
i can not use any cpu.
 
Is there any solution getting this work on an 64-bit windows as x86 assambly?
 
Thanks ins Advantage
GeneralRe: Modification: Adding 64-bit compatibility. [modified]memberKrishanCode18 Nov '11 - 13:12 
I tried all changed but it does not work for window 7 as well as i tried on machine with window 8 developer version i have.
 
I need this app badly to make it work. I have spend 2 days.
 
All i need if to either use same app and make it work on window 7 or add a console project instead of
win form and see if it can unable or disable.
 
I am able to list the devices but cannot unable or disable it. Because we are using windowHandle which can be the problem..
 

If someone has this application working please send me code to
 
yogeshsdsu@gmail.com
 
Thanks a lot.
QuestionDoes it work with vista ?memberThe Cpp16 Nov '08 - 13:25 
thanx for this article it helps me a lot..
 
Does it work with vista ? if no please help me to do one for vista ..
 
again thanks a lot
AnswerRe: Does it work with vista ?memberjkhines5 Dec '08 - 13:12 
My testing has shown that this code works on Vista.
GeneralRe: Does it work with vista ?memberThe Cpp11 Dec '08 - 12:21 
thanks for the reply
AnswerRe: Does it work with vista ?memberdroo9917 Sep '09 - 6:43 
if I disable the NIC manually, the program does not enable.
GeneralQuite nicememberJakobmhc19 Oct '08 - 23:47 
It saved me for alot of work, thanks!
GeneralA little modification [modified]memberszutyok13 Aug '08 - 0:42 
The class is excellent and spared me a week of doc-digging, thank you for wrtining it!
 
However, I had to make a small change:
 
while (!Native.SetupDiGetDeviceRegistryProperty(hDevInfo,
                                                DeviceInfoData,
                                                Native.SPDRP_DEVICEDESC,
                                                0,
                                                DeviceName,
                                                Native.MAX_DEV_LEN,
                                                IntPtr.Zero))
{
    //Skip
    break; // Inserting a BREAK here
}
 
for on my dev machine the loop got stuck. I'm in a rush to finish my code, I hope I can hop in later to give you more details (win error code, device that got skipped) if you are interested in it. (Crafting the perfect code Smile | :) )
 
thanks again for the article and the class!
 
EDIT: the same goes for all the while loops in the code
 
modified on Thursday, August 14, 2008 3:53 AM

GeneralRe: A little modificationmembernohear_t20 Mar '09 - 16:50 
The code in its original form will cause errors of the following form:
 
"The exception unknown software exception (0xYYYYYYYY) occurred in the application at location 0xXXXXXXXX" when you continually add/remove hardware over and over again. When you add the above code modification, the problem is no longer observed.
 
If I am not mistaken, when each thread is spawned, there is really no application name associated with it other than a thread ID and windows interprets that as an "unknown software".
 
The above should fix should be included into your article.
RantWhy can't correct running at Vista64 and XP64.memberhanyungwang6 Jul '08 - 23:05 
Hi,
Why can't correct running at Vista64 and XP64.
Thanks.
AnswerRe: Why can't correct running at Vista64 and XP64.memberjkhines8 Dec '08 - 6:57 
I have added a comment. It has changes to run on x64.
QuestionGreat and very useful - thanks! But is there a way to check if device is en-/disabled?memberit-bergmann2 Jul '08 - 8:28 
Hi,
 
thanks for this great code - as I'm looking actual for something like this Smile | :)
 
However there's one function more I would need:
Does anybody know a way for to check if a device is enabled or not?
 
Thanks,
 
Andre
GeneralGreat article, codememberfethiye31 Mar '08 - 12:53 
Hi there, I have just downloaded and ran it on my system. The code is stuck at the Native.SetupDiGetDeviceRegistryProperty function call in the GetAll function. Do you experience that as well? If so, any idea how I can get over it? Thank you.
GeneralYour History and ThanksmemberJonShops11 Dec '07 - 17:49 
Thanks for the article on Device Manager. It takes healthy restraint to keep a library to-the-point...I know how tempting it is to rewrite as if for posterity, when other people will likely rip the good ideas into their own classes, anyway. I tend to find myself pondering methods, naming conventions, and ref vs. val passing as if Bertrand Meyer was going to personally grade my work.
 
I really wanted to comment, though, on your "History" because it is SO like mine...only I never had a VIC. Stayed after school to use the Apple II, learned its 6502, and also had a Sinclair, but it was pre-Timex, with the 8k ROM upgrade. Actually stored programs on a 3M Wollensak reel-to-reel. Some early gurus wrote a book on the Sinclair 'Monitor' and I was hooked.
 
JonShops -- Fun really begins with the words, "So what in the World do I do now?"

GeneralHardware scannermemberBob Carter10 Dec '07 - 9:02 
This was an awesome contribution! Thanks.
QuestionNice work - but what about the service?memberAgentM0075 Dec '07 - 15:00 
Is there a way to detect new devices using a windows service?
GeneralRe: Nice work - but what about the service?memberwjfrancis6 Dec '07 - 9:42 
Supposedly you can register the service handle just like a window handle. While I only experimented with this briefly, it did not appear to work. If I get some time, I will look into what went wrong. The MSDN documentation certainly led me to believe detecting hardware notifications within a service was within the realm of possibilities. If you get around to it before I do, I certainly would appreciate your insights.
GeneralRe: Nice work - but what about the service?memberslp155011 Dec '08 - 15:34 
Create a form, make it hidden, then initiate in the Windows service.
GeneralWowmemberPaul Conrad1 Dec '07 - 16:34 

Very nice article.
 

"Real programmers just throw a bunch of 1s and 0s at the computer to see what sticks" - Pete O'Hanlon

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

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 30 Nov 2007
Article Copyright 2007 by wjfrancis
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid