c# - 如何从 C# 调用 Win32API SetCommTimeouts?

标签 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();
            }
        }
    }
}

最佳答案

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

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

关于c# - 如何从 C# 调用 Win32API SetCommTimeouts?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27317259/

相关文章:

c# - 语音识别编程入门问题

c# - 是否可以仅使用一次分配来连接字符串列表?

c# - 如何从 windows 服务使用 windows 身份验证连接到 sql-server?

c# - USB Serial Port Unplugged 但仍在端口列表中

c# - ASP.NET Identity 如何与 Model First 方法一起使用?

c# - WPF 中的屏幕分辨率问题?

c# - WPF/C# - 以编程方式创建和使用单选按钮的示例

Linux - 与设备关联的端口

java - 导出的 JAR 文件未检测到串口

c# - Entity Framework 模型-第一个枚举类型元数据类型