开发者

Can I get the device handle for a serial port that I am accessing using the .net SerialPort class?

I am writing a C# .NET app that accesses an USB device which exposes a serial port interface. I am using the handy .NET SerialPort class, which works just fine.

My problem is that I need to catch the DBT_DEVICEQUERYREMOVE event, but I don't get the event if I register using DBT_DEVTYP_DEVICEINTERFACE. I do get DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE, but no DBT_DEVICEQUERYREMOVE.

From my web research it seems that I need to register using DBT_DEVTYP_HANDLE, which requires a handle, s开发者_如何学运维uch as that returned by CreateFile. Since the SerialPort class does not expose this handle, I'm wondering if there is some other way to get the handle (or some other way to get the event of interest).


I think you could use a bit of reflection to get the handle, I don't know of a better way to get the handle that the .NET Framework is currently using. The SerialPort uses an internal type called SerialStream. This Stream has the handle you want. Since I don't have a serial port to test on, this code is a bit of a guesswork:

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);

This will give you a SafeFileHandle, which is a wrapper for handles. You could call DangerousGetHandle to get the actual IntPtr.

This handle only has a valid value after you call Open on the SerialPort, and becomes invalid when it's disposed. You should check the IsInvalid and IsClosed values of the handle first before using DangerousGetHandle.


What is also a solution is to grab the Serialport implementation of .Net and change it to your liking.

This is what I did for a project where I needed access to the handle, and because I had timing/async issues with SerialPort. So I boiled it down to the bear necessities.

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;
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜