How to do robust SerialPort programming with .NET / C#?

前端 未结 9 1399
心在旅途
心在旅途 2020-12-23 10:22

I\'m writing a Windows Service for communication with a Serial Mag-stripe reader and a relay board (access control system).

I run into problems where the code stops

相关标签:
9条回答
  • 2020-12-23 10:43

    You can't close someone elses connection to a port, the following code will never work:

    if (serialPort.IsOpen) serialPort.Close();
    

    Because your object didn't open the port you can't close it.

    Also you should close and dispose the serial port even after exceptions occur

    try
    {
       //do serial port stuff
    }
    finally
    {
       if(serialPort != null)
       {
          if(serialPort.IsOpen)
          {
             serialPort.Close();
          }
          serialPort.Dispose();
       }
    }
    

    If you want the process to be interruptible then you should Check if the port is open and then back off for a period and then try again, something like.

    while(serialPort.IsOpen)
    {
       Thread.Sleep(200);
    }
    
    0 讨论(0)
  • 2020-12-23 10:47

    How to do reliable async comms

    Don't use the blocking methods, the internal helper class has some subtle bugs.

    Use APM with a session state class, instances of which manage a buffer and buffer cursor shared across calls, and a callback implementation that wraps EndRead in a try...catch. In normal operation, the last thing the try block should do is set up the next overlapped I/O callback with a call to BeginRead().

    When things go awry, catch should asynchronously invoke a delegate to a restart method. The callback implementation should exit immediately after the catch block so that the restart logic can destroy the current session (session state is almost certainly corrupt) and create a new session. The restart method must not be implemented on the session state class because this would prevent it from destroying and recreating the session.

    When the SerialPort object is closed (which will happen when the application exits) there may well be a pending I/O operation. When this is so, closing the SerialPort will trigger the callback, and under these conditions EndRead will throw an exception that is indistinguishable from a general comms shitfit. You should set a flag in your session state to inhibit the restart behaviour in the catch block. This will stop your restart method from interfering with natural shutdown.

    This architecture can be relied upon not to hold onto the SerialPort object unexpectedly.

    The restart method manages the closing and re-opening of the serial port object. After you call Close() on the SerialPort object, call Thread.Sleep(5) to give it a chance to let go. It is possible for something else to grab the port, so be ready to deal with this while re-opening it.

    0 讨论(0)
  • 2020-12-23 10:48

    I think I have come to the conclusion that HyperTerminal does not play well. I've run the following test:

    1. Start my service in "console mode", it starts switching the device on/off (i can tell by it's LED).

    2. Start HyperTerminal and connect to the port. The device stays on (HyperTerminal raises DTR) My service writes to the event log, that it cannot open the port

    3. Stop HyperTerminal, I verify it is properly closed using task manager

    4. The device stays off (HyperTerminal has lowered DTR), my app keeps on writing to the event log, saying it cannot open the port.

    5. I start a third application (the one I need to coexist with), and tell it to connect to the port. I does so. No errors here.

    6. I stop the above mentioned application.

    7. VOILA, my service kicks in again, the port opens successfully, and the LED goes ON/OFF.

    0 讨论(0)
  • 2020-12-23 10:52

    This code seems to work properly. I've tested it on my local machine in a console application, using Procomm Plus to open/close the port, and the program keeps on ticking.

        using (SerialPort port = new SerialPort("COM1", 9600))
        {
            while (true)
            {
                Thread.Sleep(1000);
                try
                {
                    Console.Write("Open...");
                    port.Open();
                    port.DtrEnable = true;
                    Thread.Sleep(1000);
                    port.Close();
                    Console.WriteLine("Close");
                }
                catch
                {
                    Console.WriteLine("Error opening serial port");
                }
                finally
                {
                    if (port.IsOpen)
                        port.Close();
                }
            }
        }
    
    0 讨论(0)
  • 2020-12-23 10:53

    I've tried changing the work-thread like this, with the exact same result. Once HyperTerminal once succeeds in "capturing the port" (while my thread is sleeping), my service won't be able to open the port again.

    public void DoorOpener()
    {
        while (true)
        {
            SerialPort serialPort = new SerialPort();
            Thread.Sleep(1000);
            serialPort.PortName = "COM1";
            serialPort.BaudRate = 9600;
            serialPort.DataBits = 8;
            serialPort.StopBits = StopBits.One;
            serialPort.Parity = Parity.None;
            try
            {
                serialPort.Open();
            }
            catch
            {
            }
            if (serialPort.IsOpen)
            {
                serialPort.DtrEnable = true;
                Thread.Sleep(1000);
                serialPort.Close();
            }
            serialPort.Dispose();
        }
    }
    
    0 讨论(0)
  • 2020-12-23 10:54

    Have you tried leaving the port open in your application, and just turning DtrEnable on/off, and then closing the port when your application closes? i.e:

    using (SerialPort serialPort = new SerialPort("COM1", 9600))
    {
        serialPort.Open();
        while (true)
        {
            Thread.Sleep(1000);
            serialPort.DtrEnable = true;
            Thread.Sleep(1000);
            serialPort.DtrEnable = false;
        }
        serialPort.Close();
    }
    

    I'm not familiar with DTR semantics, so I don't know if this would work.

    0 讨论(0)
提交回复
热议问题