Responding to USB Devices in PowerShell

I recently got a cool USB MIDI keyboard (M-Audio Axiom 49) and software (Propellerhead Reason) combo that lets me play keyboard with all kinds of cool sounds effects and general fun. Despite how cool the system is, it’s not quite as spontaneous as a plain ol’ electronic keyboard. Just to piddle around for a bit, you have to turn on the keyboard, launch Reason, create a new song, add a virtual instrument, and then start playing.

How can PowerShell of all things help the anguish of a struggling keyboard hack?

The answer comes in two parts – creating a template song, and then having PowerShell launch it when you turn on the keyboard.

It turns out that you can resolve a lot of the issues in Reason by just creating a good starter song. Add in the mixer, a synthesizer, and save it to disk – I called it “Default.rns.” When you double-click on this file, Reason starts up with all of the presets you had configured.

The second part is more tricky, unless of course you happen to have Real Ultimate Power.

The core of this solution comes in three parts:

  1. WMI lets you enumerate USB devices on the system.
  2. The Register-WmiEvent cmdlet lets you respond to WMI events.
  3. WMI has a default __InstanceCreationEvent class that lets you respond to ANY new instance being created (Services, Processes, etc.)

The answer to #1 has come up a few times on the Team Blog:

http://blogs.msdn.com/powershell/archive/2007/02/24/displaying-usb-devices-using-wmi.aspx
http://blogs.msdn.com/powershell/archive/2009/01/10/get-usb-using-wmi-association-classes-in-powershell.aspx

I had no idea what the Axiom keyboard was being recognized as, so I did the easy thing. Ran the query once, turned off the keyboard, and then ran it again. Compare-Object to the rescue:

001
002
003
$before = gwmi Win32_USBControllerDevice |% { [wmi] $_.Dependent }
$after = gwmi Win32_USBControllerDevice |% { [wmi] $_.Dependent }
Compare-Object $before $after -PassThru

That gives an object like:

Availability                :
Caption                     : USB Axiom 49
ClassGuid                   : {4d36e96c-e325-11ce-bfc1-08002be10318}
CompatibleID                : {USB\Class_01&SubClass_01&Prot_00, USB\Class_01&SubClass_01, USB\Class_01}
ConfigManagerErrorCode      : 0
ConfigManagerUserConfig     : False
CreationClassName           : Win32_PnPEntity
Description                 : USB Audio Device
DeviceID                    : USB\VID_0763&PID_0199&MI_00\6&2F841C5F&0&0000
ErrorCleared                :
ErrorDescription            :
HardwareID                  : {USB\VID_0763&PID_0199&REV_0105&MI_00, USB\VID_0763&PID_0199&MI_00}
InstallDate                 :
LastErrorCode               :
Manufacturer                : (Generic USB Audio)
Name                        : USB Axiom 49
PNPDeviceID                 : USB\VID_0763&PID_0199&MI_00\6&2F841C5F&0&0000

There’s the ticket. It’s actually a Win32_PnpEntity. While the previous WMI query helped us discover USB entities, now that we know how the keyboard is identified to the system, we can drop the complicated WMI dependency traversal, and turn this specific goal into a simpler query:

Get-WmiObject Win32_PnPEntity –Filter "Name=’USB Axiom 49′"

Now that we know which WMI class represents the keyboard, and the query that selects it specifically, we can use WMI’s __InstanceCreationEvent to monitor for that instance as it comes and goes. The Register-WmiEvent cmdlet makes this a snap:

001
002
003
004
005
006
007
008
009
010
function Register-KeyboardEvent
{
    $query = "SELECT * FROM __InstanceCreationEvent " +
         "WITHIN 5 " +
         "WHERE TargetInstance ISA ‘Win32_PnPEntity’ " +
         "AND TargetInstance.Name = ‘USB Axiom 49′ "

    $null = Register-WmiEvent -Query $query -SourceIdentifier KeyboardMonitor -Action { E:\lee\reason\Default.rns }
}

After calling this function, PowerShell automatically launches Reason with your favourite presets already loaded whenever you turn on the USB keyboard.

3 Responses to “Responding to USB Devices in PowerShell”

  1. Tim writes:

    Lee, how do you determine what to put after -SourceIdentifier … ? I’m trying to customize this for my iPod.

  2. Lee Holmes writes:

    Hi Tim;

    I made it up :) The SourceIdentifier is what you can use to unregister it later:

    PS >Get-Help Register-ObjectEvent -Parameter SourceIdentifier

    -SourceIdentifier
    Specifies a name that you select for the subscription. The name that yo
    on. The default value is the GUID that Windows PowerShell assigns.

    The value of this parameter appears in the value of the SourceIdentifie
    l event objects associated with this subscription.

    Required? false
    Position? 101
    Default value GUID
    Accept pipeline input? false
    Accept wildcard characters? false

  3. Ludovic Besançon writes:

    Hi,
    It works fine and I try to use it on ours PC which use differents scanners.
    With these line I can change the default twain source.
    But …
    What is the best way to start the process ?
    It seems that you need to avec a powershell session opened in background. Isn’t it ?

    Ludovic.

Leave a Reply