Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Is that you? Writing Better Software for Cool USB Hardware

  Interfacing wireless PC lock using .Net. This application allows you to perform some actions when you leave ( like locking your computer, turning down the volume) and running certain tasks when u arrive back.
Scott Hanselman's Computer Zen

Difficulty: Intermediate
Time Required: 6-10 hours
Cost: Less Than $50
Software: Visual Studio Express Editions
Hardware: Wireless PC Lock
Download: Download

Note: The source code for this project is only the beginning. It continues to evolve at SourceForge. Go there to help guide its evolution or download the latest version for the .NET Framework 2.0 or 1.1.

Summary: In this fourth installment of the "Some Assembly Required" column, Scott Hanselman and Bryan Batchelder find a piece of hardware so compelling—and with included software so bad—that they write their own version using the .NET Framework 2.0. You can buy a little wireless key fob with USB Receiver manufactured by a no-name company and billed as a "Wireless USB Security Device" from a number of online retailers that is meant to lock your computer when you leave and unlock when you return. However, the software it comes with is terrible. So, we figure—Some Assembly Required. (Hopefully the company will ready this article and start using our software!) We'll also extend the application with all-new functionality using plugins written in Visual Basic!

The Hardware

What a fantastic idea this was. A little green button (US$15 at NewEgg) that you attach to your key ring. It acts as a "presence" indicator for your computer. It knows when you arrive and it knows when you leave and it performs actions like locking your computer, turning down the volume or running certain tasks. Brilliant, right? Wrong. The hardware is great, but the included software is some odd little app circa 1995 whose idea of "locking" your computer is covering all your applications with its own big non-standard window and forcing you to enter a password to override. No, not your Windows Login password, another totally different and application-specific password. Yikes.

Also, this little application wasn't extensible in any way, and there didn't appear to be any COM or .NET libraries included to easily receive the device's events. But, the idea and the hardware are just so darned compelling. Greg Hughes and I had talked about writing a better application for this little button a number of times but never took that first step. Thankfully Bryan Batchelder also couldn't sleep and took the first step for us all.

Interfacing with USB and a Little Abstraction

The hardware behind the little green USB Wireless Security Key Fob is two pieces. The button that you clip to your keys that "heartbeats" a short-range (10m) signal, and a small USB receiver that plugs into your computer. Interestingly, when it's plugged in for the first time, it doesn't require drivers. Windows knows about this little thing automatically! How is that possible? When I run msinfo32.exe (You have this little-known application on your system also; give it a run now!) I notice that it's registered itself as a "USB Human Interface Device" (mouse) and a "Game Controller" which are two devices that Windows knows about already. It makes sense that the company creating these little devices would use commonly available USB chipsets like those used in inexpensive mice. Also, no need to write a custom device driver. In the figure below, notice the USB Receiver device's PNP ID is "VID_04b4&PID_7417." That's important: we'll need to use it later when we go looking for it programmatically.

Click here for larger image

(click image to zoom)

Talking to a USB device is a lot different from talking to a Serial Port. Serial Ports have names like "COM3" and no matter what device is plugged into the COM3, it's still COM3. If you want to find a serial port device and you're not sure which port it's on, you have to basically "yell" out programmatically to each of the serial ports on the system saying, "Is that you?" In the USB world, you know the kind of device you're looking for, and you're really not concerned about which port it's on. You just know you want to talk to it.

Unfortunately, there's no support in the Base Class Library (BCL) included with .NET for talking to USB Devices. Most often, if you want to access a USB device from .NET, you'll use a high-level library that the device's manufacturer includes. However, in this case, as we said, the manufacturer includes no libraries that we can take advantage of, so we're at square one. Instead we'll be using a number of Win32 APIs from kernel32.dll, setupapi.dll, and hid.dll ("hid" means Human Interface Device).

We'll start by building up several layers of abstraction, because even though we could just call all this Win32 goodness from our UI, we'd really rather have a nice class called "KeyFob," wouldn't we? Below is a Class Diagram created by Visual Studio 2005 Professional Edition. You can read it from left to right. You might think that KeyFob is the class our application will use, since it's a good logical representation of the little device that we think of as the key fob. However, our application really cares about the concept of presence, and we have to take into consideration that there are multiple key fobs out there, any one of which could walk by our single receiver. So, we need a KeyFobCollection that is a generic Dictionary<string, KeyFob> that our PresenceManager will use. The PresenceManager will manage a list of authorized fobs; that is, KeyFobs that are allowed to affect our system by their presence.

KeyFob has Status, SerialNumber and HandleMessage methods as well as helpful information like IsAuthorized and LastHeartbeat. It has protected items that aren't directly available to us, the most interesting one being a KeyFobReceiver. This class is wholly encapsulated within the KeyFob class and provides the class with information that we on the outside really don't need to know, like the array of bytes of the lastPacket. Moving lower, the KeyFobReceiver has an instance of the UsbStickReceiver class. This is the first class that formally recognizes that this device is a USB device and there is some very low-level I/O going on here. It has an instance of the USBSharp class which is a managed wrapper around all the Win32 DLLs APIs mentioned before.

Click here for larger image

(click image to zoom)

The really crazy and interesting stuff happens in UsbStickReceiver, just above the low-level APIs. The USBSharp class handles marshaling of the various Windows SDK datatypes we'll need to be passing in and receiving. Let's take a look at the FindReceiver method of UsbStickReceiver. Not much can happen in our application until the receiver is plugged in, right?

I've added comments to the code below to explain what's happening and what we're trying to accomplish.

public static UsbStickReceiver FindReceiver()
{
//We haven't found any devices int my_device_count = 0;
//We have no idea where our device is (yet) string my_device_path = string.Empty;
//But we know we'll need all these methods! USBSharp.USBSharp myUsb = new USBSharp.USBSharp();
//And we need to get the Human Interface Device GUID myUsb.CT_HidGuid(); //Let the system know we're looking for active devices myUsb.CT_SetupDiGetClassDevs(); //Get ready... int result = -1;
int device_count = 0;
int size = 0;
int requiredSize = 0;

//While nothing goes wrong while(result != 0)
{
//Starting with device 0... result = myUsb.CT_SetupDiEnumDeviceInterfaces(device_count); //Let me know how much room I'm going to need to get info about this device int resultb = myUsb.CT_SetupDiGetDeviceInterfaceDetail(ref requiredSize, 0);
//Cool, store that size = requiredSize; //Gimme the info you've got resultb = myUsb.CT_SetupDiGetDeviceInterfaceDetailx(ref requiredSize, size);
//Did we find the USB Receiver? Remember it's name from earlier? if(myUsb.DevicePathName.IndexOf("vid_04b4&pid_7417") > 0)
{
//Sweet, it's device # "device_count," let's store this! my_device_count = device_count; my_device_path = myUsb.DevicePathName; //Bail, we're done! break;
}
device_count++;
}

if(my_device_path == string.Empty)
{
Exception devNotFound = new Exception(@"Device could not be found.");
throw(devNotFound);
}

return new UsbStickReceiver(my_device_count, my_device_path);
}
This is a pretty good example of abstraction. The name of the method is "FindReceiver" and it takes no parameters. It returns a UsbStickReceiver to the caller, KeyFobReceiver, and it hinds a LOT of stuff. In this example also, the methods we call are all managed methods on the UsbSharp class which, in turn, hides all the unmanaged Win32 goop. Each class has its responsibility and does just that. 'Twas Bryan who did all this good work and for that we thank him.

Another interesting thing to note is that Human Interface Devices (HIDs) can use File Handles to provide us access to their data, so later UsbStickReceiver will take the devicePath retrieved in the code above and do

resultb = myUsb.CT_CreateFile(devicePath); 

and then take the resulting file handle HidHandle and  

fs = new FileStream(new Microsoft.Win32.SafeHandles.
SafeFileHandle((IntPtr)myUsb.HidHandle, false),
FileAccess.Read, 5, true);
to get the data. Be sure to read the code and have the Class Diagram close by for reference. It's really interesting. 

Squashing Bugs and Reading the Manual

One small but interesting aside: as Bryan and I (and the folks who used the initial version of this application) were testing this application, we noticed that some users' systems just couldn't find the USB receiver when it was plugged into a USB hub. They'd have to move it around until it got found. It wasn't until we did a line-by-line review of the code in UsbStickReceiver.cs and compared each method call to the MSDN documentation that we discovered that we'd been passing an incorrect parameter to one of the wrapped Win32 methods.


//Wrong. We were passing in the previous device's resultb value, 
//which caused random and unpredictable weirdness.
int resultb = myUsb.CT_SetupDiGetDeviceInterfaceDetail(ref requiredSize, resultb);

//Right. Odd as it may seem, the MSDN documentation explicitly //says to pass in "0" for the second parameter. Whatever, dude. //It makes the whole thing work! int resultb = myUsb.CT_SetupDiGetDeviceInterfaceDetail(ref requiredSize, 0);
Bugs like this are really hard to find and fix because it DID work. Most of the time. It worked so most-of-the-time that we figured it was a hardware problem. Hard to debug, but rewarding now that we can reliably find the USB Receiver now.

User-specific Settings

By now we've got our PresenceManager. Next stop SettingsManager. Since more than one person may be using a single machine and will each likely have their own key fob, we'll want them to each have their own user-specific settings. We wanted to get a path that is user-specific and application-specific. We also wanted to create a directory and reasonable default settings file if needed.

//This constructor is private because SettingsManager is accessed via a Factory 
private SettingsManager()
{
doc = new XmlDocument();
string appDirPath = Path.Combine(
System.Environment.GetFolderPath(
System.Environment.SpecialFolder.ApplicationData),
"Usb Wireless Security");
configFilePath = Path.Combine(appDirPath, "SettingsV2.xml");
CreateIfNeeded(appDirPath, configFilePath);
doc.Load(configFilePath);
}

protected void CreateIfNeeded(string directory, string file)
{
const string DEFAULT_SETTING_FILE =
@"<?xml version=""1.0"" encoding=""utf-8"" ?>
<Settings><KeyFobs />
<PresenceWindow>5</PresenceWindow>
<OverridePassword /><DisabledPlugins/></Settings>"
;
DirectoryInfo di = new DirectoryInfo(directory);
if(!di.Exists) { di.Create(); }

FileInfo fi = new FileInfo(file);
if(!fi.Exists)
{
FileStream fs = fi.Create();
StreamWriter sr = new StreamWriter(fs);
sr.Write(DEFAULT_SETTING_FILE);
sr.Close();
}
}
There's a lot of new Settings functionality build into .NET 2.0, but for our needs a simple XML file loaded into an XmlDocument was easy and very few lines of code. Each to his or her own. The number one thing to get out of this snippet is that your application should operate on the "Principle of Least Surprise." That means it shouldn't do, or need to do, anything that surprises the user. It should just work, and if it doesn't have something available, it should make it. In our example, it's reasonable for the user to assume they have their own settings, so we put our settings in the C:\documents and settings\<username>\Application Data folder that we retrieved by using 
System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData)

and we create the folder if it's not there, as well as a new settings file with reasonable defaults if one is missing. It's little things like this that will keep your users happy and you off the support phone.

Extending our Application with Plugins (of all languges!)

Creating applications is fun, but extending applications is the most fun of all. This application is just begging to be extended. Our application is listening for key fob activity via the PresenceManager, which will send an event when it detects activity from our USB receiver. Now we want the application to announce these "presence events" to any one who cares to listen—that means all of us.

public void HandlePresenceNotification(PresenceNotificationEventArgs e)
{
foreach(Plugin plugin in Plugins)
{
if(plugin.Enabled)
{
plugin.Worker.HandlePresenceNotification(e);
}
}
}
To create a plugin, we create a new project from within Visual Studio. This time we'll use Visual Basic. We'll create a plugin that will put a message in the Windows Event Log everytime a KeyFob message is received. This will be useful not only for auditing, but debugging. It will also provide a record of anyone else wearing a KeyFob who might walk by my desk.

Every plugin will include a reference to our UsbSecurity.Core assembly. Note that even though the Core assembly is written in C#, it can be utilized by any .NET language like VB. Our UsbSecurity.Core assembly includes a base class that plugins must derive from called PresencePluginBase. If we look at PresencePluginBase in the Object Browser we can see that it provides us with a number of virtual methods like HandlePresenceNotification and WorkstationLocked that we can take advantage of.

Click here for larger image

(click image to zoom)

We'll start by deriving our class from PresensePluginBase after adding a reference to UsbWireless.Core. We also import the System.Diagnostics namespace so we can use the EventLog class from the BCL.

Imports System
Imports UsbWirelessSecurity
Imports System.Text
Imports System.Diagnostics

Namespace DefaultPlugins

'When the host exe finds us, point them to our Configurator! <PresencePluginConfigurator("Event Logging Plugin")> _
Class EventLoggerPlugin
Inherits PresencePluginBase

End Class End Namespace
In addition to a base class for plugins, we require a custom attribute to be placed on each plugin class. Since each plugin will appear in a ListBox in our Application's UI, we'd like to know what the plugin developer wants displayed. A custom attribute is an easy way for the plugin developer to add a "post-it note" in their code to let us know additional information. Note the <PresencePluginConfigurator("Event Logging Plugin"> attribute in the VB code above.

Next we'll override each of the virtual methods from PresencePluginBase and log the details of each message to the EventLog.


Imports System
Imports UsbWirelessSecurity
Imports System.Text
Imports System.Runtime.InteropServices

Namespace DefaultPlugins

'When the host exe finds us, point them to our Configurator! <PresencePluginConfigurator("Event Logging Plugin")> _
Class EventLoggerPlugin
Inherits PresencePluginBase

Dim logName As String = "USB Wireless Security" Public Overrides Sub HandleMessage(ByVal m As
UsbWirelessSecurity.KeyFobMessage)
MyBase.HandleMessage(m)

If (Not m.MessageType = KeyFobMessageType.Heartbeat) Then Using aLog As New EventLog(logName)
aLog.Source = logName
aLog.WriteEntry( _
String.Format("Message Received: Device {0} reports {1}.", _
m.SerialNumber, m.MessageType.ToString()))
End Using
End If End Sub Public Overrides Sub WorkstationLocked()
MyBase.WorkstationLocked()
Using aLog As New EventLog(logName)
aLog.Source = logName
aLog.WriteEntry("Workstation Locked")
End Using
End Sub Public Overrides Sub WorkstationUnlocked()
MyBase.WorkstationUnlocked()
Using aLog As New EventLog(logName)
aLog.Source = logName
aLog.WriteEntry("Workstation Unlocked")
End Using
End Sub Public Overrides Sub HandlePresenceNotification(ByVal e As
UsbWirelessSecurity.PresenceNotificationEventArgs)
MyBase.HandlePresenceNotification(e)

If (Not e.NotificationType = PresenceNotificationType.Heartbeat) Then Using aLog As New EventLog(logName)
aLog.Source = logName
aLog.WriteEntry( _
String.Format("Presence Received: Device {0} reports {1}.", _
e.KeyFob.SerialNumber, e.NotificationType.ToString()))
End Using
End If End Sub End Class End Namespace
In order to prevent the EventLog filling with a heartbeat message every 250ms, we ignore those messages.

Click here for larger image

(click image to zoom)

Conclusion

With a clean hardware abstraction layer and a plugin architecture, you can enable your code-savvy users to extend your application with new functionality. Here's some ideas that we've been kicking around to extend the USB Wireless Security Application. Bryan and I hope that folks will take up the challenge and begin to exploit this great little device using Visual Studio.

  • Send an email or SMS when the device leaves or returns
  • Start the default screensaver
  • Save all your files
  • Start Defragmenting or start Drive Cleanup
  • Set Skype to "Away" when you leave
  • Stop playing all music applications
  • Create a centralized Web Service that each system calls when it sees a key fob to create a companywide "presence notification service."

Enjoy expanding on the existing project and remember not to fear the words "Some Assembly Required!"


Big thanks to Bryan Batchelder for the original idea and for the amazing work on the hardware abstraction layer to get the USB key fob to be heard from .NET!

Follow the Discussion

  • jacky xujacky xu

    The Project is so cool ,I use it for reference. thanks.

    If you have update ,Please send to  my Email:

    exu7036@sina.com

  • CricketLoverCricketLover

    I'll try this with my USB game pad!!!

  • PhilPhil

    Top Coding by all u guys, I also purchased one of these and thought nice device, REALLY POOR Software..

  • JayJay

    Not much of a VB/C programmer, but anyone get this to work for Vista?  I have the latest driver info loaded from the vendor that supports vista, but I don't see the the same HID info.

  • PaulPaul

    Great Article!   I was wondering if anyone has ever connected (browsed) a digital camera that is connected to a USB Port?

    I know software exists that does so, such as Kodak EasyShare, but it can really hog your memory.

    It looks like this article will provide help, but any additional help would be appreciated.

  • Ken ChristensenKen Christensen

    This is very cool.  It is something I have wanted to build for a long time.  The application for this is very compelling and is as follows.  If my PC can recognize my presence it can automatically go to sleep and power-up in such a way as to maximize sleep time and not annoy me with wake-up delays.  The idea is that the PC will begin wake-up when I am approaching it and can be fully awake when I sit down to use it.  Such a device can decrease the disabling of power management (usually, users do disable power management) and result in much greater energy savings – a truly green PC.  

    Thoughts on this anyone?

  • Ranganathan SridharanRanganathan Sridharan

    I am looking for any wireless interface with a couple of buttons that has a .NET library. Basically I want it to select from a couple of options  to collect data. I want the .NET interface because I want the data collection to be controllable over the internet as well. I think this can be used for other really cool applications like controlling your media applications wirelessly. Thanks for any pointers.

  • Clint RutkasClint I'm a "developer"

    @Ranganathan:  http://www.thinkgeek.com/computing/input/88ee/

  • Victor J AlvarezVictor J Alvarez

    I actually got the computer to "go to sleep" using the code provided on SourceForge. The problem is that I can't get it to wake up from sleep. Since the device doesn't register itself as a mouse or keyboard, the computer doesn't allow for power management to be set on the device.

    Any recommendations? Does anyone know how to add drivers to the device?

  • lostdreamer_nl at hotmail dot comlostdreamer​_nl at hotmail dot com

    First of all.... WOW.

    I tried this thing a few years ago and after having a few rounds with it's software it was now collecting dust for a year or 2.

    Just wondering: I'm trying to adjust the code to accept multiple USB recievers (so not only can i see if i'm around, but also; where in the house) I allready have 3 sticks with different IDs, but dont exactly know which classes I should edit to make the program accept multiple USBsticks at the same time.

    If anyone has any suggestion, It'd be great.

  • Clint RutkasClint I'm a "developer"

    @Cosmin are you sure your device path is correct?  One issue could be this article was written with Windows XP, things may have changed.  I don't have the hardware to verify

  • CosminCosmin

    I have a problem... myUsb.CT_CreateFile(my_device_path) returns 0, so it fails, and i have no handle. Can anyone help ? I'm using windows 7 and visual studio 2008.

    my_device_path is ok.

  • Clint RutkasClint I'm a "developer"

    @Yllsa all depends how that hardware works.  

  • YllsaYllsa

    Great article! I am trying to program using a USB PANIC Button

    (http://www.johnbruin.net/index.php/2008/11/30/new-software-for-your-usb-panic-button/)

    to simply act as a physical manifestation of the button on the program form. (So all it needs to do is trigger an event of some sort.)

    I tried adding the references to the .dll from system32 folder, especially HID.dll, but it gives me an error that says:

    "A reference to 'C:\WINDOWS\system32\hid.dll' could not be added. Please make sure that the file is accessible, and that it is a valid assembly or COM component."

    I tried to add the .dll by Right Click Project -> Add Reference -> Browse. I might be trying to add it wrong.

    Thanks for any advice.

  • Clint RutkasClint I'm a "developer"

    @priyath yes but a lot depends on what hardware you picked and how you want to communicate.  If you picked a chip that mounts as a serial port, it is rather simple then.  If you need to make a driver, then that raises the difficulty greatly.

    Phidgets are a great low-cost, easy to use, "Just works" type thing.  Micro framework boards like the netdunio and Fez Domino are also decent possiblities as well.

  • priyathpriyath

    Any idea on how to read a custom made electronic device from c#/VB

    eg:- display temperature on PC reading from usb

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.