Могу ли я получить дескриптор устройства для последовательного порта, к которому я получаю доступ, используя класс .net SerialPort? - PullRequest
1 голос
/ 20 июня 2011

Я пишу приложение на C # .NET, которое обращается к USB-устройству с интерфейсом последовательного порта.Я использую удобный класс .NET SerialPort, который прекрасно работает.

Моя проблема в том, что мне нужно перехватить событие DBT_DEVICEQUERYREMOVE, но я не получаю событие, если я регистрируюсь с помощью DBT_DEVTYP_DEVICEINTERFACE.Я получаю DBT_DEVICEARRIVAL и DBT_DEVICEREMOVECOMPLETE, но не DBT_DEVICEQUERYREMOVE.

Из моего веб-исследования кажется, что мне нужно зарегистрироваться, используя DBT_DEVTYP_HANDLE, для которого требуется дескриптор, такой как возвращаемый CreateFile.Поскольку класс SerialPort не предоставляет этот дескриптор, мне интересно, есть ли какой-нибудь другой способ получить дескриптор (или какой-либо другой способ получить интересующее событие).

Ответы [ 2 ]

4 голосов
/ 22 июня 2011

Я думаю, что вы могли бы немного подумать, чтобы получить дескриптор, я не знаю лучшего способа получить дескриптор, который в настоящее время использует .NET Framework. SerialPort использует внутренний тип, называемый SerialStream. Этот поток имеет ручку, которую вы хотите. Поскольку у меня нет последовательного порта для тестирования, этот код немного догадывается:

var serialPort = new SerialPort();
object stream = typeof(SerialPort).GetField("internalSerialStream", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(serialPort);
var handle = (SafeFileHandle)stream.GetType().GetField("_handle", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(stream);

Это даст вам SafeFileHandle, который является оберткой для ручек. Вы можете позвонить DangerousGetHandle, чтобы получить IntPtr.

Этот дескриптор имеет действительное значение только после вызова Open на SerialPort и становится недействительным, когда он удаляется. Прежде чем использовать DangerousGetHandle, вы должны проверить значения IsInvalid и IsClosed дескриптора.

1 голос
/ 16 февраля 2017

Решение также состоит в том, чтобы взять реализацию * .Net 1001 * Serialport и изменить ее по своему вкусу.

Это то, что я сделал для проекта, где мне нужен был доступ к дескриптору, и потому что у меня были проблемы с синхронизацией / асинхронностью в SerialPort. Таким образом, я сварил это до потребностей медведя.

public class SimpleSerialPort : IDisposable
{
    private const uint GenericRead = 0x80000000;
    private const uint GenericWrite = 0x40000000;
    private const int OpenExisting = 3;
    private const uint Setdtr = 5; // Set DTR high

    private FileStream _fileStream;

    public void Close()
    {
        Dispose();
    }

    public int WriteTimeout { get; set; }

    public SafeFileHandle Handle { get; private set; }

    public void Open(string portName, uint baudrate)
    {
        // Check if port can be found
        bool isValid =
            SerialPort.GetPortNames()
                .Any(x => String.Compare(x, portName, StringComparison.OrdinalIgnoreCase) == 0);
        if (!isValid)
        {
            throw new IOException(string.Format("{0} port was not found", portName));
        }

        string port = @"\\.\" + portName;

        Handle = CreateFile(port, GenericRead | GenericWrite, 0, IntPtr.Zero, OpenExisting, 0, IntPtr.Zero);
        if (Handle.IsInvalid)
        {
            throw new IOException(string.Format("{0} port is already open", portName));
        }

        var dcb = new Dcb();

        // first get the current dcb structure setup
        if (GetCommState(Handle, ref dcb) == false)
        {
            throw new IOException(string.Format("GetCommState error {0}", portName));
        }

        dcb.BaudRate = baudrate;
        dcb.ByteSize = 8;
        dcb.Flags = 129;
        dcb.XoffChar = 0;
        dcb.XonChar = 0;

        /* Apply the settings */
        if (SetCommState(Handle, ref dcb) == false)
        {
            throw new IOException(string.Format("SetCommState error {0}", portName));
        }

        /* Set DTR, some boards needs a DTR = 1 level */
        if (EscapeCommFunction(Handle, Setdtr) == false)
        {
            throw new IOException(string.Format("EscapeCommFunction error {0}", portName));
        }

        // Write default timeouts 
        var cto = new Commtimeouts
        {
            ReadTotalTimeoutConstant = 500,
            ReadTotalTimeoutMultiplier = 0,
            ReadIntervalTimeout = 10,
            WriteTotalTimeoutConstant = WriteTimeout,
            WriteTotalTimeoutMultiplier = 0
        };

        if (SetCommTimeouts(Handle, ref cto) == false)
        {
            throw new IOException(string.Format("SetCommTimeouts error {0}", portName));
        }

        // Create filestream 
        _fileStream = new FileStream(Handle, FileAccess.ReadWrite, 32, false);
    }

    public void Write(byte[] bytes)
    {
        _fileStream.Write(bytes, 0, bytes.Length);
    }

    public void Read(byte[] readArray)
    {
        for (int read = 0; read < readArray.Length;)
        {
            read += _fileStream.Read(readArray, read, readArray.Length - read);
        }
    }

    public byte ReadByte()
    {
        byte[] readsBytes = new byte[1];
        Read(readsBytes);
        return readsBytes[0];
    }

    public void Dispose()
    {
        if (Handle != null)
        {
            Handle.Dispose();
        }

        if (_fileStream != null)
        {
            _fileStream.Dispose();
        }

        _fileStream = null;
        Handle = null;
    }

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode,
        IntPtr securityAttrs, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern bool GetCommState(
        SafeFileHandle hFile, // handle to communications device
        ref Dcb lpDcb // device-control block
        );

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool SetCommState(
        SafeFileHandle hFile, // handle to communications device
        ref Dcb lpDcb // device-control block
        );

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool EscapeCommFunction(
        SafeFileHandle hFile, // handle to communications device
        uint dwFunc
        );

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern bool SetCommTimeouts(
        SafeFileHandle hFile, // handle to comm device
        ref Commtimeouts lpCommTimeouts // time-out values
        );

    [StructLayout(LayoutKind.Sequential)]
    private struct Commtimeouts
    {
        public int ReadIntervalTimeout;
        public int ReadTotalTimeoutMultiplier;
        public int ReadTotalTimeoutConstant;
        public int WriteTotalTimeoutMultiplier;
        public int WriteTotalTimeoutConstant;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct Dcb
    {
        private readonly uint DCBlength;
        public uint BaudRate;
        public uint Flags;
        private readonly ushort WReserved;
        private readonly ushort XonLim;
        private readonly ushort XoffLim;
        public byte ByteSize;
        private readonly byte Parity;
        private readonly byte StopBits;
        public byte XonChar;
        public byte XoffChar;
        private readonly byte ErrorChar;
        private readonly byte EofChar;
        private readonly byte EvtChar;
        private readonly ushort WReserved1;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...