Tracking device changes with Tcl

Published

Windows can send notifications to applications advising them of changes in device configuration. The most common manifestation of this is file managers popping up a window showing the contents of a USB pen drive when it is plugged in and automatically closing it when the drive is ejected. This post describes how to hook into these notifications within a Tcl application.

NOTE: Receiving asynchronous notifications in Tcl generally requires the event loop to be running. The sample interactive sessions below assume the commands are executed in wish or in a Tcl shell with the event loop active.

Getting notified of device changes in Windows from within Tcl requires the TWAPI extension. The start_device_notifier command registers a callback to invoked when a device notification is received from Windows.

The most common use of device notifications is to learn of changes in available drives, for example insertion or removal of USB pen drives or CD/DVD media. The following command illustrates this by registering a callback that simply prints arguments passed to it.

% proc print_args args {puts [join $args {, }]}
% set notifier [twapi::start_device_notifier print_args -deviceinterface volume]
devnotifier#1

The return value from the command is a handle that we will use later to turn off notifications.

Now insertion of a pen drive causes the following to be printed resulting from the callback being invoked four times.

devnotifier#1, devnodes_changed
devnotifier#1, devicearrival, volume, E:, 
devnotifier#1, devnodes_changed
devnotifier#1, devnodes_changed

The first argument to the callback is the device notification handle that triggered the callback. The second argument is the notification event type. Insertion of the pen drive causes the device tree to change triggering events of type devnodes_changed. However, these are usually of no interest. More commonly useful is the devicearrival event which signals that a new volume with drive letter E is now available. In a real application, this may bring up an file dialog or prompt the user whether the drive should be backed a la Google Drive etc.

Conversely, removal of the pen drive results in the following sequence of events.

devnotifier#1, devnodes_changed
devnotifier#1, deviceremovecomplete, volume, E:, 
devnotifier#1, devnodes_changed
devnotifier#1, devnodes_changed
devnotifier#1, devnodes_changed

Again, only the deviceremovalcomplete event is of interest indicating that drive E is no longer available. A real application would likely take action only on the devicearrival and deviceremovalcomplete events, ignoring the rest.

The stop_device_notifier command should be used to turn off notifications when no longer required.

% twapi::stop_device_notifier $notifier

The device notifications are not limited to drives and volumes. We can choose to receive all notifications by leaving off the -deviceinterface option. Now plugging in a pen drive and unplugging a USB mouse results in a whole bunch of notifications.

% set notifier [twapi::start_device_notifier print_args]
devnotifier#2
devnotifier#2, devnodes_changed
devnotifier#2, devicearrival, deviceinterface, {A5DCBF10-6530-11D2-901F-00C04FB951ED}, \\?\USB#VID_0930&PID_6544#0022CFF6B88BC3114417AE03#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
devnotifier#2, devicearrival, deviceinterface, {53F56307-B6BF-11D0-94F2-00A0C91EFB8B}, \\?\USBSTOR#Disk&Ven_TOSHIBA&Prod_TransMemory&Rev_1.00#0022CFF6B88BC3114417AE03&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
devnotifier#2, devicearrival, deviceinterface, {53F5630D-B6BF-11D0-94F2-00A0C91EFB8B}, \\?\STORAGE#Volume#_??_USBSTOR#Disk&Ven_TOSHIBA&Prod_TransMemory&Rev_1.00#0022CFF6B88BC3114417AE03&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}#{53f5630d-b6bf-11d0-94f2-00a0c91efb8b}
devnotifier#2, devnodes_changed
devnotifier#2, devnodes_changed
devnotifier#2, devnodes_changed
devnotifier#2, devicearrival, deviceinterface, {6AC27878-A6FA-4155-BA85-F98F491D4F33}, \\?\SWD#WPDBUSENUM#_??_USBSTOR#Disk&Ven_TOSHIBA&Prod_TransMemory&Rev_1.00#0022CFF6B88BC3114417AE03&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}#{6ac27878-a6fa-4155-ba85-f98f491d4f33}
devnotifier#2, devicearrival, deviceinterface, {F33FDC04-D1AC-4E8E-9A30-19BBD4B108AE}, \\?\SWD#WPDBUSENUM#_??_USBSTOR#Disk&Ven_TOSHIBA&Prod_TransMemory&Rev_1.00#0022CFF6B88BC3114417AE03&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}#{f33fdc04-d1ac-4e8e-9a30-19bbd4b108ae}
devnotifier#2, devnodes_changed
devnotifier#2, deviceremovecomplete, deviceinterface, {378DE44C-56EF-11D1-BC8C-00A0C91405DD}, \\?\HID#VID_04F2&PID_0939#8&6cb6ab8&0&0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd}
devnotifier#2, deviceremovecomplete, deviceinterface, {4D1E55B2-F16F-11CF-88CB-001111000030}, \\?\HID#VID_04F2&PID_0939#8&6cb6ab8&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
devnotifier#2, deviceremovecomplete, deviceinterface, {A5DCBF10-6530-11D2-901F-00C04FB951ED}, \\?\USB#VID_04F2&PID_0939#7&d029972&0&2#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
devnotifier#2, devnodes_changed
% twapi::stop_device_notifier $notifier

Notice some differences from the previous example. The first set of notifications are from the insertion of the pen drive. However, now the notifications are for the raw device interfaces as opposed to volumes. The second set of notifications are generated by unplugging the mouse.

The -deviceinterface option can also be used to restrict notifications to a particular device interface type. Device interfaces are identified by GUIDs defined in the Windows SDK with the prefix GUID_DEVINTERFACE_. In the mouse-related notifications above, the GUID {378de44c-56ef-11d1-bc8c-00a0c91405dd} corresponds to the mouse device interface, the GUID {4d1e55b2-f16f-11cf-88cb-001111000030} corresponds to the HID (Human Interface Device) interface and the GUID {a5dcbf10-6530-11d2-901f-00c04fb951ed} corresponds to the USB device interface. Since the mouse driver interface conforms to all three, notifications are generated for each.

Note: You can have multiple notifiers active if desired.

Most applications will not be concerned with device interfaces and only need the volume notifications that were described earlier.