Sicppy Ответов: 1

Поля ЩПТ меняться от экземпляра последовательный порт с#


Я пытаюсь эмулировать связь между существующей частью программного и аппаратного обеспечения, используя расширенный монитор последовательного порта, я смог придумать эту информацию о связи.

In/out queue size 1024/512
Purge the serial port: RXABORT, RXCLEAR, TXABORT, TXCLEAR
Set timeouts: ReadInterval=-1, ReadTotalTimeoutMultiplier=0, ReadTotalTimeoutConstant=0, WriteTotalTimeoutMultiplier=0, WriteTotalTimeoutConstant=5000
Baud rate 115200
RTS on
DTR off
Data bits=8, Stop bits=1, Parity=None
Set chars: Eof=0x1A, Error=0x00, Break=0x00, Event=0x00, Xon=0x11, Xoff=0x13
Handflow: ControlHandShake=(), FlowReplace=(TRANSMIT_TOGGLE, RTS_CONTROL), XonLimit=256, XoffLimit=256


Сначала я попробовал использовать встроенный класс SerialPort самостоятельно, но ему не хватает универсальности, чтобы выполнить такую задачу, а затем я попробовал использовать System.Отражение для установки полей DCB, но всякий раз, когда я прихожу к полю, которое начинается со строчной буквы f, например "fParity" или "fDtrControl", оно дает мне исключение NullReferenceException.

Есть ли какой-либо другой способ, которым я могу изменить эти значения, чтобы они соответствовали приведенным выше?

Это метод расширения, который я написал, позволяющий изменять поля DCB
internal static class SerialPortExtensions
    {
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        public static void SetFlag(this SerialPort port, string flag, object value)
        {
            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(flag).SetValue(dcbValue, value);

                // 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)
            {
                throw;
            }
        }

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


И именно здесь я инициализирую все значения:
COM.Open();
            COM.SetFlag("BaudRate", (UInt32)115200);
            COM.SetFlag("StopBits", (byte)0);
            COM.SetFlag("ByteSize", (byte)8);
            COM.SetFlag("Parity", (byte)0);
            COM.SetFlag("fDtrControl", 0x00);
            COM.SetFlag("fRtsControl", 0x01);
            COM.SetFlag("XonChar", 0x11);
            COM.SetFlag("XoffChar", 0x13);
            COM.SetFlag("XonLim", (ushort)256);
            COM.SetFlag("XoffLim", (ushort)256);



РЕДАКТИРОВАТЬ
При дальнейшем исследовании оказывается, что поля с префиксом f не входят в структуру DCB, используемую SerialPort по какой-то странной причине. Однако есть поле flags, но оно имеет тип UInt32, поэтому я не уверен, как его использовать, если у кого-то есть дополнительная информация о том, как использовать это поле, пожалуйста, отправьте его в качестве ответа или комментария.

1 Ответов

Рейтинг:
6

Sicppy

Хорошо, я решил свою проблему, я опубликую код и объяснение здесь, Если кто-то заинтересуется.

Структура DCB требует класса C++, известного как битовое поле, которого нет в C#, поэтому вместо этого у них есть поле под названием "флаги", хранящееся в виде UInt32 У них есть своя собственная мелархия, созданная там относительно того, как она хранится и читается, но они действительно поместили метод в SerialStream называемый SetDcbFlag который принимает два ints. немного покопавшись в исходном коде .net, я сумел придумать набор констант, приравнивающих исходные поля DCB и их новый код int, я еще не составил список значений для этих флагов, но их можно легко найти в разделе Документация DCB Я использовал system . reflection, чтобы получить доступ к методу, поскольку это был внутренний метод, который должен был использоваться только внутренним.Чистый источник.

Итак, вот он, код, это класс расширения для SerialPort класс, который поставляется на складе с .NET 2.0+. Мой класс расширения добавляет три метода, SetField(string name, object value) который установит любое из полей, не имеющих префикса "f", SetFlag(int Flag, int Value) который позаботится об этих полях с префиксом "f" (я предоставлю список констант для использования с Flag параметр), и, наконец, UpdateComm() это обновит последовательное соединение после того, как вы измените все свои значения, которые изначально были частью SetField метод, но он занимает немного больше времени, чтобы завершить вещи, если он вызывает это каждый раз во время инициализации.

ЗАПИСКА:
Последовательный порт должен быть открыт перед использованием любого из этих методов!

Использование:

SerialPort COM = new SerialPort("COM7");
COM.Open();
COM.DiscardInBuffer();
COM.DiscardOutBuffer();
COM.SetFlag(FBINARY, 1);
COM.SetFlag(FPARITY, 0);
COM.SetFlag(FDTRCONTROL, 0x00);
COM.SetFlag(FRTSCONTROL, 0x01);
COM.SetField("BaudRate", (UInt32)115200);
COM.SetField("StopBits", (byte)0);
COM.SetField("ByteSize", (byte)8);
COM.SetField("Parity", (byte)0);
COM.SetField("XonChar", (byte)0x11);
COM.SetField("XoffChar", (byte)0x13);
COM.SetField("EvtChar", (byte)0x1A);
COM.SetField("XonLim", (ushort)256);
COM.SetField("XoffLim", (ushort)256);
COM.UpdateComm();
/* Do Stuff */
COM.Close();


Константы:
internal const int FBINARY = 0;
internal const int FPARITY = 1;
internal const int FOUTXCTSFLOW = 2;
internal const int FOUTXDSRFLOW = 3;
internal const int FDTRCONTROL = 4;
internal const int FDSRSENSITIVITY = 6;
internal const int FTXCONTINUEONXOFF = 7;
internal const int FOUTX = 8;
internal const int FINX = 9;
internal const int FERRORCHAR = 10;
internal const int FNULL = 11;
internal const int FRTSCONTROL = 12;
internal const int FABORTONOERROR = 14;
internal const int FDUMMY2 = 15;


И, наконец, класс расширения:
internal static class SerialPortExtensions
{
    [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
    public static void SetField(this SerialPort port, string field, object value)
    {
        if (port == null)
            throw new NullReferenceException();
        if (port.BaseStream == null)
            throw new InvalidOperationException("Cannot change fields until after the port has been opened.");

        try
        {
            object baseStream = port.BaseStream;
            Type baseStreamType = baseStream.GetType();

            FieldInfo dcbFieldInfo = baseStreamType.GetField("dcb", BindingFlags.NonPublic | BindingFlags.Instance);
            object dcbValue = dcbFieldInfo.GetValue(baseStream);

            Type dcbType = dcbValue.GetType();
            dcbType.GetField(field).SetValue(dcbValue, value);
            dcbFieldInfo.SetValue(baseStream, dcbValue);
        }
        catch (SecurityException) { throw; }
        catch (OutOfMemoryException) { throw; }
        catch (Win32Exception) { throw; }
        catch (Exception)
        {
            throw;
        }
    }

    [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
    public static void SetFlag(this SerialPort port, int flag, int value)
    {
        object BaseStream = port.BaseStream;
        Type SerialStream = BaseStream.GetType();
        SerialStream.GetMethod("SetDcbFlag", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(BaseStream, new object[] { flag, value });
    }

    [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
    public static void UpdateComm(this SerialPort port)
    {
        object baseStream = port.BaseStream;
        Type baseStreamType = baseStream.GetType();

        FieldInfo dcbFieldInfo = baseStreamType.GetField("dcb", BindingFlags.NonPublic | BindingFlags.Instance);
        object dcbValue = dcbFieldInfo.GetValue(baseStream);

        SafeFileHandle portFileHandle = (SafeFileHandle)baseStreamType.GetField("_handle", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(baseStream);
        IntPtr hGlobal = Marshal.AllocHGlobal(Marshal.SizeOf(dcbValue));
        try
        {
            Marshal.StructureToPtr(dcbValue, hGlobal, false);

            if (!SetCommState(portFileHandle, hGlobal))
                throw new Win32Exception(Marshal.GetLastWin32Error());
        }
        finally
        {
            if (hGlobal != IntPtr.Zero)
                Marshal.FreeHGlobal(hGlobal);
        }
    }

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


NikolaB

Спасибо! Я не мог найти лучшего объяснения и примера. Для меня это очень полезно. Борк прекрасно справится и воспользуется им немедленно.

Sicppy

Нет проблем, мне потребовалось около недели копания, чтобы получить все это, так что я решил разместить его здесь и избавить кого-то от хлопот, радуясь, что кто-то получил от него пользу!

DuanChengHua

Молодец!помоги мне очень сильно.