SerialTray – Terminal Emulator Launcher

Zonemicro MicrocontrollerIn my rapidly diminishing spare time, from time to time I engage into ridiculous activities such as programming microcontrollers similar to the one pictured here (check out this site for similar goodies). Typically, the data exchange with these devices occurs via a serial port and I use a USB to serial adapter since COM ports are hard to come by these days.

For the terminal emulator software, I personally like to use Tera Term. However, as with other emulators, one of the annoying things is that it requires you to specify the COM port (i.e. COM5) for the connection, but any given time you plug the USB cable, you may not know what the resulting COM port is. Similarly, if you have multiple devices connected, things get confusing as well.

USB-to-SerialEnter ‘Serial Tray’. Basically, it is a tiny app that resides in the Windows System Tray. When you right-click its icon, it lists available COM ports along with their friendly names. Check out this article on the associated coding struggle.

The application comes with an XML file (because that’s how I roll in terms of configuration) where you can configure a 3rd party application that should be launched when a particular port is selected. In my case, I launch Tera Term. Within the same XML configuration, you can specify application parameters that can include a variable %PortNumber%, which will resolve to the number of COM port you’ve selected (e.g. ‘1’ for ‘COM1’).


<?xml version="1.0" encoding="utf-8" ?>
    <add key="CustomPortCommand" value="C:\Program Files\TTERMPRO\ttermpro.exe" />
    <add key="CustomCommandArguments" value="/C=%PortNumber% /B=56000" />

The above configuration will execute command “C:\Program Files\TTERMPRO\ttermpro.exe /C=5 /N=56000”¬†when you click on the friendly name of the COM5 port in the tray.

Serial TrayThat’s pretty much it. No installer, as it does not need one. Just copy the files to a folder and tray away.

Let me know your thoughts and feelings on the matter,


Revision History:

v1.0 – Initial Revision

Free Download:

  1. Timur

    Hmf.. Do you guys by chance have some steps I could take to reproduce this issue? Do the devices that are now disconnected still show up in Device Manager in any form?

    I guess my approach would start with exporting the key tree for an active device and exporting the key tree for the previously connected device and doing a diff on them, in hopes of identifying additional value settings that pertain to the state (like ConfigFlags or ActiveService maybe – just guessing)..

  2. mediumrare

    The scenario I have is related to using COM ports for Bluetooth connections.
    So in one case a user has installed Windows Bluetooth drivers and COM 4 had been assigned as one of the Windows Bluetooth ports – then I am not sure what the user did next ( maybe uninstalled the drivers – but the assignments persisted in the registry) However at some point after that – they installed a USB-serial convertor device and this also was “assigned” to COM 4.
    Thus in my app I am utilizing a slightly modified version of your registry scanning method – but it is finding the Bluetooth COM 4 first in the registry and then my app concludes (wrongly) that COM 4 is a Bluetooth port. The only reason it is looking at COM 4 in the first place is because it is currently active as a USB-Serial converter port.
    Here is a link I found that explains how to show hidden devices in Device Manager:
    I have checked the ActiveService and ConfigFlags for the Windows Bluetooth Com ports – but they are not displayed in the registry for these ports. The path to these is HKEY_LOCAL_MACHINE\SYSTEM\ControlSet002\Enum\BTHENUM\. All the windows virtual bluetooth com ports are listed here but I don’t see the difference between “active” and “inactive” ones.

  3. Timur

    Well, I have a couple of suggestions but sadly no solutions.
    First, I think you should modify the enumeration code to return a list instead of the dictionary. The list could contain an object (e.g. Dictionary) with COM port and the associated friendly name.. This way, at least you will solve the “last one wins” scenario, and get the ‘USB-Serial Converter’ along with the disconnected Bluetooth device.
    Next step would be to figure out how to determine if the device is disconnected and remove it from the list. If Device Manager can do this, so can we (i hope :-)). I’d still try to do the diff on the …\enum\deviceName keys and check for anything obvious. The next step would maybe be to use the ClassId and see if there is anything relevant in the registry in keys like HKEY_LOCAL_MACHINE\SYSTEM\ControlSetxxx\Control\Class\{yourclassId} or other keys referencing that ClassId.
    Please check back if you figure it out. Good luck!

  4. mediumrare

    Thank you for the above guidance. I have added an extra check inside my slightly modified MineRegistryForPortName and this will only attempt to read get the FriendlyName of a COM port from DEVICEMAP\SERIALCOMM if it also has a Control subkey. This works for all devices I have tested so far, but there may be something out there that fails this check. Apologies for the formatting of what follows:

    private void LatestMineRegistryForPortName(string startKeyPath, Dictionary targetMap, List portsToMap)
    if (targetMap.Count >= portsToMap.Count)

    using (RegistryKey currentKey = Registry.LocalMachine)
    using (RegistryKey currentSubKey = currentKey.OpenSubKey(startKeyPath, false))
    if (currentSubKey != null)
    string[] currentSubkeys = currentSubKey.GetSubKeyNames();
    if (currentSubkeys.Contains("Device Parameters") && currentSubkeys.Contains("Control") &&
    startKeyPath != "SYSTEM\\CurrentControlSet\\Enum")
    object portName = Registry.GetValue("HKEY_LOCAL_MACHINE\\" +
    startKeyPath + "\\Device Parameters", "PortName", null);
    if (portName == null ||
    portsToMap.Contains(portName.ToString()) == false)

    object friendlyPortName = Registry.GetValue("HKEY_LOCAL_MACHINE\\" + startKeyPath, "FriendlyName", null);
    string friendlyName = "N/A";

    if (friendlyPortName != null)
    friendlyName = friendlyPortName.ToString();
    if (friendlyName.Contains(portName.ToString()) == false)
    friendlyName = string.Format("{0} ({1})", friendlyName, portName);
    targetMap[portName.ToString()] = friendlyName;
    foreach (string strSubKey in currentSubkeys)
    LatestMineRegistryForPortName(startKeyPath + "\\" + strSubKey, targetMap, portsToMap);
    catch (Exception ex)
    loggingService.WriteException("MineRegistryForPortName", ex);

    Let me know if any improvements can be made to this.

Comments are closed.