如何从C#调用Win32API SetCommTimeouts?

时间:2014-12-05 13:43:18

标签: c# .net serial-port

我在通过C#的Win32 API执行串行通信时遇到问题。无论我在调用SetCommTimeouts()时使用哪个值,除非收到一个或多个字符,否则不会返回对ReadFile的调用。

使用.Net System.IO.Port.SerialPort类不是一个选项。它有关于USB连接的COM端口的严重错误,这就是我试图直接使用Win32 API的原因。

问题可能在于编组CommTimeouts结构以便API接收到错误的值吗?

完整的源代码如下:

namespace SerialTest
{
    using System;
    using System.Globalization;
    using System.Runtime.InteropServices;
    using Microsoft.Win32.SafeHandles;

    [Flags]
    internal enum AccessRights : uint
    {
        GenericRead = (0x80000000),
        GenericWrite = (0x40000000),
        GenericExecute = (0x20000000),
        GenericAll = (0x10000000)
    }

    [Flags]
    internal enum ShareModes : uint
    {
        FileShareRead = 0x00000001,
        FileShareWrite = 0x00000002,
        FileShareDelete = 0x00000004
    }

    internal enum CreationDispositions
    {
        CreateNew = 1,
        CreateAlways = 2,
        OpenExisting = 3,
        OpenAlways = 4,
        TruncateExisting = 5
    }

    internal class CommTimeouts
    {
        public UInt32 ReadIntervalTimeout;
        public UInt32 ReadTotalTimeoutMultiplier;
        public UInt32 ReadTotalTimeoutConstant;
        public UInt32 WriteTotalTimeoutMultiplier;
        public UInt32 WriteTotalTimeoutConstant;
    }

    internal class Kernel32
    {
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern SafeFileHandle CreateFile(
            string lpFileName,
            uint dwDesiredAccess,
            uint dwShareMode,
            IntPtr lpSecurityAttributes,
            uint dwCreationDisposition,
            uint dwFlagsAndAttributes,
            IntPtr hTemplateFile
        );

        [DllImport("kernel32.dll", EntryPoint = "SetCommTimeouts", SetLastError = true)]
        public static extern bool SetCommTimeouts(SafeHandle hFile, CommTimeouts timeouts);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool ReadFile(SafeHandle hFile, [Out] byte[] lpBuffer,
           uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);
    }

    public class SerialTest
    {
        public void Test(string portName)
        {
            if (portName.Length > 5) portName = @"\\.\" + portName;
            var hPort = Kernel32.CreateFile(portName,
                                            (uint) (AccessRights.GenericRead | AccessRights.GenericWrite),
                                            0, // Not shared
                                            IntPtr.Zero, // Security attributes,
                                            (uint) CreationDispositions.OpenExisting,
                                            0,
                                            IntPtr.Zero // Template file
                );

            if (hPort.IsInvalid)
            {
                throw new Exception("Could not open port " + portName + ". Error: " + Marshal.GetLastWin32Error().ToString(CultureInfo.InvariantCulture));
            }

            try
            {
                // Set timeout so call returns immediately
                var timeouts = new CommTimeouts
                    {
                        ReadIntervalTimeout = 0xFFFFFFFF,
                        ReadTotalTimeoutMultiplier = 0,
                        ReadTotalTimeoutConstant = 0,
                        WriteTotalTimeoutMultiplier = 0,
                        WriteTotalTimeoutConstant = 0
                    };

                if (!Kernel32.SetCommTimeouts(hPort, timeouts))
                {
                    var error = Marshal.GetLastWin32Error();
                    throw new Exception("Could not set timeouts. Error: " + error.ToString(CultureInfo.InvariantCulture));                    
                }

                var buf = new byte[1];
                uint readBytes;
                if (!Kernel32.ReadFile(hPort,
                                       buf,
                                       1,
                                       out readBytes,
                                       IntPtr.Zero))
                {
                    var error = Marshal.GetLastWin32Error();
                    throw new Exception("Could not read. Error: " + error.ToString(CultureInfo.InvariantCulture));
                }
            }
            finally
            {
                hPort.Close();
            }
        }
    }
}

1 个答案:

答案 0 :(得分:2)

我在网上找到的SetCommTimeouts定义不正确。感谢理查德,我现在使用了正确的定义。

static extern bool SetCommTimeouts(IntPtr hFile, [In] ref COMMTIMEOUTS
lpCommTimeouts);