09.01.2013

Replacing Win XP Accessibility Features

How to get the input from a serial port (Real hardware or USB) into an application that only handles keyboard inputs?

Earlier Windows Versions had an accessibility feature, that would allow you to do just that, by handling serial inputs as if they were keystrokes. This feature however was discontinued in more recent Windows versions. (Vista and above)

So a tool was needed, that would provide the functionality described above.

This surely should not be a problem in .NET and as it turns out it is not. Central to this is the "SendKeys.SendWait()" Method, provided in the framework as part of the "System.Windows.Forms" namespace. MSDN-Article
public static void SendWait ( string keys )
This method takes a string and basically posts it to the operating system as a series of keystrokes. There are however a couple of characters that need to be treated specially because they act as control characters to the SendKeys.SendWait() Method. Namely these are: ^, ~, +, %, (, ), [, ]

In order to use the method for arbitrarily defined strings, these characters need to be put into curly brackets. To send a '+' for example you would need to call the method like this:
SendKeys.SendWait("{+}");
I wrote a method to do this kind of reformatting for me:
        public static string Reformat(string original)
        {
            var sb = new StringBuilder();
            foreach (char c in original)
            {
                switch (c)
                {
                    case '+': case '^': case '~': case '%':
                    case '(': case ')': case '[': case ']':
                        sb.AppendFormat("{{{0}}}", c);
                        break;
                    default:
                        sb.Append(c);
                        break;
                }
            }
            return sb.ToString();
        }
Note that this uses the AppendFormat() Method of a StringBuilder. (Namespace "System.Text") To add curly brackets in the "Format" context you have to type double brackets, because the curly bracket itself is used as an escape sequence in format-strings.

The relevant part in the program looks like this:
        /// <summary>
        /// Event thrown whenever the serial port received data
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PortOnDataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            while (_port.BytesToRead > 0)
            {
                // PostKeys
                var original = _port.ReadExisting();
                // Reformat string to fit SendKeys.SendWait()
                var reformattedString = Reformat(original);
                try
                {
                    SendKeys.SendWait(reformattedString);
                }
                // Handle exception caused if keys are sent to an application
                // not handling keys
                catch(InvalidOperationException)    
                {
                }
            }
        }
I will make the complete project available on Sourceforge. Comments, questions and suggestions are, as always, very welcome.

17 Kommentare:

  1. Just looking something like this and have download the app under sourceforge and it is pretty awesome. Running nicely under Window 7. Many thanks. :)

    (On a side note, does the app have an extra version that without the additional Save?)

    AntwortenLöschen
  2. Oh sorry, double-checked it should be sth like a Ctrl+O...where can i omit this in the source?

    AntwortenLöschen
    Antworten
    1. Hey, I would like to help you but I am not sure if I understand your question. What do you mean by "without the additional save" ?
      I also don´t understand what you want to do with "Ctrl+O"? Do you want the application to send the combination "Ctrl+O" if you receive a certain character?

      Please specify and I will try to help. :)

      Löschen
    2. Oh, actually I have solved the issue. It should be something wrong related to other Serial to Com program I previously installed (I tried so many under Window 7 before finding yours, all fail. lol). Thanks a lot of your kindly help!! :)

      Löschen
    3. Alright then. Glad to hear, that the program works for you. :)

      Löschen
  3. Sorry, it's me again.

    I have tried to compile your source code downloaded from SourceForge under Viso studio 2010 Express and encounter the following error after running the app (reading some LF Card into Com):

    "SendKeys cannot run inside this application because the application is not handling Windows messages. Either change the application to handle messages, or use the SendKeys.SendWait method."

    Is there anything I need to set before compiling the source code?
    (Sorry I am pretty new to C#)

    AntwortenLöschen
    Antworten
    1. Hi, you can solve that easily by replacing every instance of the "SendKeys.Send()" Method with "SendKeys.SendWait()". I already did this in the samples above. The code on Sourceforge is still using the Send() Method. Which requires special handling on the recipients side.

      So in short: Just use SendWait() instead of Send(). :)

      Löschen
  4. Hi, "SerialPort to Keyboard" is cool SW. Please, can you add option to send "ENTER" at the end of data (aka suffix)? And, is possible to save configuration and autorun program (Auto-enable)? Thanks a lot.

    AntwortenLöschen
    Antworten
    1. I fear I can´t help you with that. The software does not know, where your data ends, because it does not know the format you are using. It just takes the characters that come from the serial port and passes them on to the Operating System.

      As a workaround you could configure your device to send the Enter Key at the end of a transmission. (If that is possible) Alternatively you can grab the code on Sourceforge and adapt it to your needs. :)

      Link: https://sourceforge.net/projects/serialporttokey/
      (Remember to replace "SendKeys.Send(...)" with "SendKeys.SendWait(...)" as discussed in previous comments)

      Löschen
  5. Hi, this program is going to be a huge help at work for the scanners we started using.

    I need to have the program start on boot. Very simple.

    But then I want it to automatically enable with no interaction from the user. What is the best possible way to do this? I have downloaded the source and opened it up in Notepad i've worked with C before but not recently.

    Thanks for the program and code and thanks in advance for any help!

    AntwortenLöschen
    Antworten
    1. Hello Mike,

      as a first step, you should get the express-edition of Microsoft Visual Studio for C#.
      http://www.microsoft.com/visualstudio/eng/products/visual-studio-express-for-windows-desktop

      Using this, you may open the code. Search for the function "private void FrmMainLoad(object sender, EventArgs e)". This function is called, when the window loads. You may want to add your code there.

      One of the problems you will encounter is selecting the right com-port. You may hard-code this or read it from a file. However this is not provided by the code, so you will have to program your own solution or use something from the web.

      As a final note: Remember to replace "SendKeys.Send(...)" with "SendKeys.SendWait(...)" as discussed in previous comments

      If you have further questions, feel free to ask. :)

      Löschen
    2. Thank you so much for helping. I've replaced the SendKeys line and I have been messing with the app. I have not worked with C in a while probably since freshman year at Devry. I feel like the application will work perfectly because it defaults to the first COM port in the list so I really don't need any special configuration I just need to know what command to call in the FrmLoad to make it auto enable when started. I tried KeyDown() and OnKeyDown() with no success. Do you have an idea on what I should include.

      Thank you so much for your assistance.

      Löschen
    3. I dont have our scanner to test rite now but I believe I got this done and working. This is my FrmLoad function now.

      private void FrmMainLoad(object sender, EventArgs e)
      {

      FillPortList();
      FillBaudList();
      StartListening();
      _isListening = true;
      btnTransfer.Text = "Disable";

      }

      Löschen
    4. Hello Mike,

      your solution should indeed work, because you are basically doing what the enable button does. It would however be easier and cleaner (as in no duplicate code) if you just called the function, that handles the buttons "Click" Event. Code for that would look like this:

      private void FrmMainLoad(object sender, EventArgs e)
      {

      FillPortList();
      FillBaudList();
      BtnTransferClick(sender, e);
      }

      Löschen
    5. ^^ That actually does not work Nico... odd

      Löschen
    6. That really is odd. On this machine it does. Tried it on my Laptop, there it doesn´t.

      I think under this circumstance you´re better off keeping your solution.

      Löschen
  6. Thanks for the help Nico very much appreciated always refreshing to come across someone willing to help.

    AntwortenLöschen