How do I set serial special characters?

2019-06-25 11:34发布

问题:

In my ongoing quest to interface with some legacy equipment, I've discovered that the vendor supplied software sets the special characters to:

00 00 00 00 11 13 

But the SerialPort class of .NET set them to:

1A 00 00 1A 11 13 

I suppose that I have two questions:

  1. What do these bytes mean?
  2. How can I tell SerialPort to use a specific set of special characters?

The latter is all I really care about, but I suspect the former is going to be useful to know.


Update: The following doesn't work:

byte[] specialchars = {
    0x00,
    0x00,
    0x00,
    0x00,
    0x11,
    0x13
};

this.port.NewLine = System.Text.Encoding.ASCII.GetString(specialchars);

Update 2: As requested, here are Portmon logs for the vendor supplied app (filtered to remove the many thousands of IOCTL_SERIAL_GET_COMMSTATUS entries) and for my attempt to match even the first exchange.

回答1:

NewLine is not what you are looking for. It's the plain old 'new line' sequence, e.g. CR LF or LF alone.

The special characters are handled like this:

  • EOF — set to 0x1a, you cannot change it in .NET
  • ERR — set by SerialPort.ParityReplace
  • BRK — don't know
  • EVT — set to 0x1a, you cannot change it in .NET
  • XON — set to 0x11, you cannot change it in .NET, and it doesn't even usually make sesn
  • XOFF — set to 0x13, you cannot change it in .NET, and it doesn't even usually make sesn

It may be helpful for you to study the Win32 DCB structure as well. It's used by .NET internally to set the state of the serial port.



回答2:

You can add an extenstion to the serialPort in c# :

http://social.msdn.microsoft.com/Forums/vstudio/en-us/89b88e89-5814-4819-8b50-7caa3faf5f54/xonxoff-values-in-net20-serialport-class?forum=csharpgeneral

for the other fields you can change :

dcbType.GetField("XonChar"); // "XonChar", "XoffChar", "ErrorChar", "EofChar", "EvtChar"

Code :

            using System;
            using System.ComponentModel;
            using System.IO.Ports;
            using System.Reflection;
            using System.Runtime.InteropServices;
            using System.Security;
            using System.Security.Permissions;
            using Microsoft.Win32.SafeHandles;

            class Program
            {
                static void Main(string[] args)
                {
                    using (var port = new SerialPort("COM1"))
                    {
                        port.Open();
                        port.SetXonXoffChars(0x12, 0x14);
                    }
                }
            }

            internal static class SerialPortExtensions
            {
                [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
                public static void SetXonXoffChars(this SerialPort port, byte xon, byte xoff)
                {
                    if (port == null)
                        throw new NullReferenceException();
                    if (port.BaseStream == null)
                        throw new InvalidOperationException("Cannot change X chars until after the port has been opened.");

                    try
                    {
                        // Get the base stream and its type which is System.IO.Ports.SerialStream
                        object baseStream = port.BaseStream;
                        Type baseStreamType = baseStream.GetType();

                        // Get the Win32 file handle for the port
                        SafeFileHandle portFileHandle = (SafeFileHandle)baseStreamType.GetField("_handle", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(baseStream);

                        // Get the value of the private DCB field (a value type)
                        FieldInfo dcbFieldInfo = baseStreamType.GetField("dcb", BindingFlags.NonPublic | BindingFlags.Instance);
                        object dcbValue = dcbFieldInfo.GetValue(baseStream);

                        // The type of dcb is Microsoft.Win32.UnsafeNativeMethods.DCB which is an internal type. We can only access it through reflection.
                        Type dcbType = dcbValue.GetType();
                        dcbType.GetField("XonChar").SetValue(dcbValue, xon);
                        dcbType.GetField("XoffChar").SetValue(dcbValue, xoff);

                        // We need to call SetCommState but because dcbValue is a private type, we don't have enough
                        //  information to create a p/Invoke declaration for it. We have to do the marshalling manually.

                        // Create unmanaged memory to copy DCB into
                        IntPtr hGlobal = Marshal.AllocHGlobal(Marshal.SizeOf(dcbValue));
                        try
                        {
                            // Copy their DCB value to unmanaged memory
                            Marshal.StructureToPtr(dcbValue, hGlobal, false);

                            // Call SetCommState
                            if (!SetCommState(portFileHandle, hGlobal))
                                throw new Win32Exception(Marshal.GetLastWin32Error());

                            // Update the BaseStream.dcb field if SetCommState succeeded
                            dcbFieldInfo.SetValue(baseStream, dcbValue);
                        }
                        finally
                        {
                            if (hGlobal != IntPtr.Zero)
                                Marshal.FreeHGlobal(hGlobal);
                        }
                    }
                    catch (SecurityException) { throw; }
                    catch (OutOfMemoryException) { throw; }
                    catch (Win32Exception) { throw; }
                    catch (Exception ex)
                    {
                        throw new ApplicationException("SetXonXoffChars has failed due to incorrect assumptions about System.IO.Ports.SerialStream which is an internal type.", ex);
                    }
                }

                [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
                private static extern bool SetCommState(SafeFileHandle hFile, IntPtr lpDCB);
            }