Del
Del

Reputation: 80

C# - Multithreading using Multiple Input Devices

I am currently writing a C# application that will accept input from multiple USB connected input devices. Using the Raw Input library, I am able to identify the input device and then complete an action based on the input device.

Example: Device A input is received as a series of keystrokes, a string is built, a database is queried using the 'built' string as a parameter, Printer A prints the results.

The issue I am having is latency when multiple devices input values at the same time. Device A input is received at the same time as Device B; Printer A prints immediately but Printer B experiences somewhat of a delay in printing. The delay is no longer than a second, but that is one second too long for this projects intended purpose. I am using barcode scanners so I will say that the input of the entire string is lightning fast for lack of a better quantitative measurement.

I don't think my question requires any code on my behalf but if needed to help answer the question then let me know and I can provide a snippet. I am simply looking for an example or a strategy to use to go about multithreading input to output if that makes sense.

Any help is appreciated.

EDIT (Adding current code):

private readonly RawInput _rawinput;
const bool CaptureOnlyInForeground = true;

public InputForm()
{
    InitializeComponent();

    _rawinput = new RawInput(Handle, CaptureOnlyInForeground);
}

string Scanner1ID;
string Scanner2ID;
string Printer1IP;
string Printer2IP;

int NumberIdentifier;
string ScannedItem;
string ScannedKey;

PLabel NewLabel = New PLabel();

//Various methods are used to identify each scanner and printer, print commands are sent via the TcpClient class in conjunction with a StreamWriter.

void StartScanning(object sender, EventArgs e)
{
    AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

    _rawinput.AddMessageFilter();
    Win32.DeviceAudit();
    _rawinput.KeyPressed += OnItemScanned;
}

private void OnItemScanned(object sender, RawInputEventArg e)
{
    ScannedKey = e.KeyPressEvent.VKeyName.Replace("D", "");
    if (int.TryParse(ScannedKey, out NumberIdentifier)
    {
        ScannedItem += ScannedKey;

        if (e.KeyPressEvent.VKey == 13 || ScannedItem.Length == 10)
        {
            InOutList.Items.Add(ScannedItem); //Adds to list in form
            if (e.KeyPressEvent.DeviceHandle.ToString() == Scanner1ID)
            {
                NewLabel.querySQL(ScannedItem);
                Printer1Writer.Write(NewLabel.Results);
                Printer1Writer.Flush();
             }
             else if (e.KeyPressEvent.DeviceHandle.ToString() == Scanner2ID)
            {
                NewLabel.pingSQL(ScannedHWB);
                if (Client2Printer2.Connected)
                {
                    NewLabel.querySQL(ScannedItem);
                    Printer2Writer.Write(NewLabel.Results);
                    Printer2Writer.Flush();
                }    
            }

            ScannedItem = "";
        }
    }
}

I may have left something out but hopefully this is enough.

Upvotes: 1

Views: 637

Answers (1)

Eric J.
Eric J.

Reputation: 150138

According to the article you link

Each window whose handle is associated with a registered device as described in the previous section must therefore check the messages it receives and take appropriate action when a WM_INPUT one is detected.

Windows message processing is a form of cooperative multi-tasking. A new message cannot be processed from the message queue until the current one returns from processing.

If your code is responding to WM_INPUT by doing whatever it needs to in the database, no other events can process until that is done.

You can solve this by using a producer/consumer pattern. Have your code that handles WM_INPUT put information necessary for processing into a structure such as a BlockingCollection, and have a separate thread read from the BlockingCollection and do the work.

Depending on your needs, you might have one BlockingCollection per raw input device, or just a single one. If the latter, part of the information put into the BlockingCollection would probably be an identifier of the raw device that generated the input.

Upvotes: 2

Related Questions