使用serialport.open()时出现I/O异常错误

26

最终更新

问题出在我们的固件上。有点尴尬,但我很高兴我们能够继续前进,而且我可以把学习Java的事情再拖一天。我的答案在下面。

更新

所以我已经放弃了这个问题。我认为这是一个与API相关的错误,但我没有时间、资源和技能来解决它。我认为有些硬件会让Windows无法正常工作。我已经下载了Eclipse,切换到Java,看看是否有效。如果不行,我会回到这里。然而,我非常希望解决这个问题,如果有人有时间或意愿深入研究这个问题,我很想看看你们的成果。显然,我会时不时地回来查看。请确保在评论中使用 '@' 提醒我。


原始帖子

我知道还有其他人在处理这个问题,但我希望有人能帮助我。我正在尝试连接到COM端口,但当我尝试使用serialport.Open()命令时,我遇到了一个I/O异常:

System.IO.IOException: The parameter is incorrect.

   at System.IO.Ports.InternalResources.WinIOError(Int32 errorCode, String str)
   at System.IO.Ports.InternalResources.WinIOError()
   at System.IO.Ports.SerialStream.InitializeDCB(Int32 baudRate, Parity parity, Int32 dataBits, StopBits stopBits, Boolean discardNull)
   at System.IO.Ports.SerialStream..ctor(String portName, Int32 baudRate, Parity parity, Int32 dataBits, StopBits stopBits, Int32 readTimeout, Int32 writeTimeout, Handshake handshake, Boolean dtrEnable, Boolean rtsEnable, Boolean discardNull, Byte parityReplace)
   at System.IO.Ports.SerialPort.Open()
   at *programtitlehere.cs*:line 90

我正在使用Stellaris LM4F232来模拟一个COM端口。我可以使用Termite(终端程序)打开、访问并且获得良好的结果,但是每当我尝试使用Visual Studio时,它甚至无法连接,我会得到这个错误。现在我甚至不知道这个错误的意思,尽管我尝试在其他地方阅读,但我仍然感到迷茫。
有人能否向我解释一下这里发生了什么,也许我可以开始尝试弄清楚这个问题?我可以包含更多的代码,但老实说那里面没有什么东西;所有串行端口设备的属性都是正常的,并且只发生在这个设备上(我可以使用相同的细节轻松使用MSP430)。
我的代码如下,供希望查看的人使用(请注意,这只是一个“沙盒”,而不是实际程序,但症状是相同的):
try
{
    serialPort1.PortName = "COM5";
    serialPort1.Open();
    if (serialPort1.IsOpen == true)
    {
        textBox1.Text = "CONNECTED";
    }
    else
    {
        textBox1.Text = "NOT CONNECTED";
    }
}
catch (Exception ex)
{
    MessageBox.Show("Error: " + ex.ToString(), "ERROR");
}

其他设置都是通过属性管理器完成的(唯一的区别是波特率设置为230400;其他所有设置均为默认设置)。我可以用这个(MSP430)打开COM4,它在所有方面都与另一个设备相同。我可以用Termite打开COM5,所以我知道连接是好的)。不,我不想同时打开它们。如果您需要更多信息,请告诉我,我可以发布更多信息。

编辑:我已经尝试了三天,仍然没有成功。我真的不明白为什么我可以通过终端程序访问此COM端口,但不能通过自己的程序访问,因为我看不到任何区别。有没有可以“检查”COM端口以查看其属性的程序(除了Windows管理器)?我变得非常沮丧,并且在我的项目中陷入了停滞状态,直到我解决这个问题...

编辑2:我已经找到了一个明显的解决方法,但是我还没有让它在这里起作用。现在我得到了几个不同的I/O错误,但至少它是动态的(不确定是否有进展)。我还了解到这是一个.NET bug,自2.0以来就存在。我仍然希望得到任何帮助,但如果我弄清楚了,我会回报的。Zach的代码(上面链接的解决方法)如下:

using System;
using System.IO;
using System.IO.Ports;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles;

namespace SerialPortTester
{
    public class SerialPortFixer : IDisposable
    {
        public static void Execute(string portName)
        {
            using (new SerialPortFixer(portName))
            {
            }
        }

        #region IDisposable Members

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

        #endregion

        #region Implementation

        private const int DcbFlagAbortOnError = 14;
        private const int CommStateRetries = 10;
        private SafeFileHandle m_Handle;

        private SerialPortFixer(string portName)
        {
            const int dwFlagsAndAttributes = 0x40000000;
            const int dwAccess = unchecked((int) 0xC0000000);

            if ((portName == null) || !portName.StartsWith("COM", StringComparison.OrdinalIgnoreCase))
            {
                throw new ArgumentException("Invalid Serial Port", "portName");
            }
            SafeFileHandle hFile = CreateFile(@"\\.\" + portName, dwAccess, 0, IntPtr.Zero, 3, dwFlagsAndAttributes,
                                              IntPtr.Zero);
            if (hFile.IsInvalid)
            {
                WinIoError();
            }
            try
            {
                int fileType = GetFileType(hFile);
                if ((fileType != 2) && (fileType != 0))
                {
                     throw new ArgumentException("Invalid Serial Port", "portName");
                }
                m_Handle = hFile;
                InitializeDcb();
            }
            catch
            {
                hFile.Close();
                m_Handle = null;
                throw;
            }
        }

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int FormatMessage(int dwFlags, HandleRef lpSource, int dwMessageId, int dwLanguageId,
                                                StringBuilder lpBuffer, int nSize, IntPtr arguments);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool GetCommState(SafeFileHandle hFile, ref Dcb lpDcb);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool SetCommState(SafeFileHandle hFile, ref Dcb lpDcb);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool ClearCommError(SafeFileHandle hFile, ref int lpErrors, ref Comstat lpStat);

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

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern int GetFileType(SafeFileHandle hFile);

        private void InitializeDcb()
        {
            Dcb dcb = new Dcb();
            GetCommStateNative(ref dcb);
            dcb.Flags &= ~(1u << DcbFlagAbortOnError);
            SetCommStateNative(ref dcb);
        }

        private static string GetMessage(int errorCode)
        {
            StringBuilder lpBuffer = new StringBuilder(0x200);
            if (
                FormatMessage(0x3200, new HandleRef(null, IntPtr.Zero), errorCode, 0, lpBuffer, lpBuffer.Capacity,
                              IntPtr.Zero) != 0)
            {
                return lpBuffer.ToString();
            }
            return "Unknown Error";
        }

        private static int MakeHrFromErrorCode(int errorCode)
        {
            return (int) (0x80070000 | (uint) errorCode);
        }

        private static void WinIoError()
        {
            int errorCode = Marshal.GetLastWin32Error();
            throw new IOException(GetMessage(errorCode), MakeHrFromErrorCode(errorCode));
        }

        private void GetCommStateNative(ref Dcb lpDcb)
        {
            int commErrors = 0;
            Comstat comStat = new Comstat();

            for (int i = 0; i < CommStateRetries; i++)
            {
                if (!ClearCommError(m_Handle, ref commErrors, ref comStat))
                {
                     WinIoError();
                }
                if (GetCommState(m_Handle, ref lpDcb))
                {
                     break;
                }
                if (i == CommStateRetries - 1)
                {
                     WinIoError();
                }
            }
        }

        private void SetCommStateNative(ref Dcb lpDcb)
        {
            int commErrors = 0;
            Comstat comStat = new Comstat();

            for (int i = 0; i < CommStateRetries; i++)
            {
                 if (!ClearCommError(m_Handle, ref commErrors, ref comStat))
                 {
                     WinIoError();
                 }
                 if (SetCommState(m_Handle, ref lpDcb))
                 {
                     break;
                 }
                 if (i == CommStateRetries - 1)
                 {
                     WinIoError();
                 }
            }
        }

        #region Nested type: COMSTAT

        [StructLayout(LayoutKind.Sequential)]
        private struct Comstat
        {
            public readonly uint Flags;
            public readonly uint cbInQue;
            public readonly uint cbOutQue;
        }

        #endregion

        #region Nested type: DCB

        [StructLayout(LayoutKind.Sequential)]
        private struct Dcb
        {
            public readonly uint DCBlength;
            public readonly uint BaudRate;
            public uint Flags;
            public readonly ushort wReserved;
            public readonly ushort XonLim;
            public readonly ushort XoffLim;
            public readonly byte ByteSize;
            public readonly byte Parity;
            public readonly byte StopBits;
            public readonly byte XonChar;
            public readonly byte XoffChar;
            public readonly byte ErrorChar;
            public readonly byte EofChar;
            public readonly byte EvtChar;
            public readonly ushort wReserved1;
        }

        #endregion

        #endregion
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            SerialPortFixer.Execute("COM1");
            using (SerialPort port = new SerialPort("COM1"))
            {
                port.Write("test");
            }
        }
    }
}

编辑3:第6天:我还在努力解决这个问题。我的水供应很少,但是我仍在苦苦挣扎。我感到有人一定会来帮忙的。谁找到这个日志,请把我的遗骸带回加拿大并找到Nicole。告诉她我爱她。

但说真的,我不知道是什么原因导致了这个问题。我想知道是不是纯粹在嵌入式方面引起的;也许是由于它是USB On-The-Go(OTG),或者因为该设备也能够成为主机。有没有人遇到过这个问题?虽然我可以使用Termite(一个终端程序,对于那些刚加入我们的观众们来说),但这并不能解释为什么。我一直在尝试找到一个开源终端程序,a)可用并且b)看a)。像往常一样,如果我在这里发现了问题,我会报告的,因为我现在已经发现了无数论坛,听起来人们从2006年就有了这个问题。

编辑4:所以根据给出的建议,我下载了一个端口监视软件应用程序(我得到了Eltima Serial Port Monitor),看起来确实是波特率的问题:

Screen capture from Eltima

但是奇怪的是,无论我设置什么波特率,它都失败了。还有,有人能解释一下上下的意思吗?我试着在谷歌上搜索,但关键词太普遍了。通常情况下,我会继续报告任何变化。
此外,为了记录,我可以使用115200的波特率(与Termite相同)连接Eltima。不幸的是,在Visual Studio中这样做行不通。
编辑5:我们的情节出现了惊喜的转折。当Termite连接到相关的COM端口时,我正在监视发生了什么,突然间!Termite抛出了与我的程序完全相同的错误,但它却忽略了它。天才,对吧?粗心,但它起作用了。现在我需要学习如何忽略IOExceptions。我将在弄清楚后再报告。
编辑6:事实证明这是一个波特率问题,但它更深入。我一直在使用Eltima串行端口监控软件,它非常直观和易于使用。我会推荐它。经过一些研究,我了解到你不能忽略这个异常并仍然使用.NET库连接到串行端口。
所以我必须深入了解Win32 API并编写自己的代码。我找到了一些相关页面,但说实话我以前从未做过这样的事情,所以在报告之前可能需要一段时间,但我一定会弄清楚并回复大家。有太多人遭受这个问题的困扰。
我发现了很多论坛和网站,可以看到完全相同的症状,但除了说“是的,.NET很糟糕”之外,没有什么实质性的帮助。我计划编写一个完整的静态库类,然后发布到我的网站、这里和所有其他地方。希望.NET能够注意到这个问题(这个错误从2.0版本就存在)。

1
我唯一的猜测是你的属性设置可能有误。也许尝试将波特率降低到9600。 - Paul Ruane
1
如果您前往控制面板管理工具计算机管理设备管理器端口,找到虚拟端口并打开其属性,您应该能够在端口设置选项卡下看到其设置。我建议尝试匹配这些设置。 - Paul Ruane
1
嗯,你试过关掉它然后再重新开启吗? - Paul Ruane
1
不知道你有没有尝试以提升的模式运行VS(例如,作为管理员)? - atlaste
1
我会尝试以下三件事情:1. 给你运行的exe文件赋予管理员权限,而不仅仅是给Visual Studio赋予权限。2. 分配一个不同的端口号,尝试使用COM1-COM4,一些设备(设计不良)只能与“低”串口一起工作。3. 尝试使用这个小型开源应用程序进行连接 https://msmvps.com/blogs/coad/archive/2005/03/23/SerialPort-_2800_RS_2D00_232-Serial-COM-Port_2900_-in-C_2300_-.NET.aspx - Adiel Yaacov
显示剩余21条评论
5个回答

8

这是来自串口驱动程序的信息;它对其中一个设置不满意。考虑到波特率是一个好的候选项,驱动程序通常只允许最高达115200。尽管当这是一款专用的CAN总线产品时,这不应该是限制。

解决这个问题的最佳方法是使用Sysinternals的Portmon实用程序;您可以看到发送给驱动程序的内容。首先观察终止符,这是您已知可行的基准线。然后调整SerialPort属性,直到您在PortMon中看到的初始化命令与Termite匹配。只有值,不包括顺序。如果这也不奏效,则将其带到停车场并用汽车反复碾压几次,然后购买另一个品牌。


更新:这确实看起来是一个波特率问题。这是.NET中的一个问题;它不会像您的终端仿真器程序那样忽略驱动程序的错误返回代码。由于您正在与一个模拟串口通信,因此实际值并不重要。但是,CAN总线速度可能存在问题;速率是可变的,我不清楚它们是如何协商的。在旧时代,这往往是通过DIP开关完成的,驱动程序可能希望您通过波特率设置指定速度。盒子或手册上应该有相关信息。典型速度为40、250或500 kbit/s。制造商肯定会知道;给他们打个电话。


我今晚已经下班了,但明天上班后我会尝试一下 :) - tmwoods
你能推荐一个更加现代化的端口监控软件包吗?我找到的唯一版本与Windows 7不兼容(似乎最后一次更新该软件是为Win98)。虽然它的版权归2012年所有,但我无法让它连接,并且所有的帮助文件都要么丢失了,要么都是针对Win98环境进行解释的。 - tmwoods
我尝试了Eltima串口监视器,请参见上面的主体问题。 - tmwoods
我们在谈论SysInternals的PortMon实用程序吗?它肯定可以在Windows 7上运行。请注意,您必须以管理员身份运行它,右键单击+以管理员身份运行。 - Hans Passant
我已经删除了它,但我确信那会解决问题。Eltima很好用,我看到无效参数出现在Termite和我的程序中。现在我正在尝试找出如何安全地忽略异常并打开端口。在我完成这个之前,我不想将问题标记为已解决。谢谢,欢迎提供更多意见 :) - tmwoods
显示剩余2条评论

6

我遇到了和这个帖子中报告的类似问题,但我成功地解决了它!

我正在使用STM32F2xx进行虚拟串口通讯!

实际上,这是我的固件问题。我忘记在我的USB回调中包含串口设置!

从PC和固件连接串口的过程:

  1. 当PC打开串口通讯时,PC会向“配置端点”发送一些命令
  2. 在固件中,它将有一个回调函数,并且固件将提供所有USB信息(他们称之为USB描述符)
  3. USB信息是每个端点的配置(例如延迟、数据大小传输和USB类型 - 高速或低速)
  4. 一旦固件完成发送所有信息,PC将确认并成功打开USB通讯
  5. 然后,PC会发送一个命令从固件获取串口设置
  6. 串口设置是波特率、数据奇偶校验和位长度。
  7. 在固件中,应该将串口设置回复给PC(我的错误就出在这里;我没有将任何串口设置发送回PC
  8. 如果成功,PC将开始串口通讯!
  9. 如果失败,PC将给出打开串口错误(但请注意,这个错误有时会被绕过)

在STM32固件代码中:

static int8_t CDC_Control_FS (uint8_t cmd, uint8_t* pbuf, uint16_t length)
{
    switch (cmd) {
       case CDC_GET_LINE_CODING:
        {
            // I was missing this part
            uint32_t baudrate = 9600;
            pbuf[0] = (uint8_t)(baudrate);
            pbuf[1] = (uint8_t)(baudrate >> 8);
            pbuf[2] = (uint8_t)(baudrate >> 16);
            pbuf[3] = (uint8_t)(baudrate >> 24);
            pbuf[4] = 0;
            pbuf[5] = 0;
            pbuf[6] = 8;
            break;
        }:
....

1
当我遇到类似的问题时,这个方法也对我有效。具体来说,每当设备断开和重新连接时,它会出现在设备管理器中,并被识别为USB串行COM端口,但不能被任何软件使用,且.NET会抛出“系统上附加的设备无法正常运作”的异常。问题是通过确保USBUART设备(Cypress PSoC5LP)固件正确设置活动端口,并在检测到USB配置变化时重新初始化CDC接口(发送端口设置)来解决的。 - K_Trenholm

5
我遇到了相同的情况。我正在尝试将串行通信连接到我的3G USB Dongle(华为E303F)位于/dev/ttyUSB0。我在RaspbianRaspberry Pi 2)中使用Mono。在我的开发PC和macOS上,我的程序运行良好。但是当我将其部署到Raspbian时,我在Serial.Open()上遇到了IOException Broken Pipe错误。
我调试了三天,并尝试了所有可能的解决方案。最终我发现我必须设置...
serialPort.DtrEnable = true;
serialPort.RtsEnable = true;

在调用 .Open() 之前。


1
我们的惊险故事到此结束。一切都是固件问题(即嵌入式设备上的代码)。我们改变了一些函数,基本上四处试探、添加和整理代码,然后,代码就能正常工作了。这张图片很好地概括了这一切。该死的固件!!但是,我在我的(冗长的)问题中描述的错误仍然存在于许多人身上,我知道还有很多人遇到了这个问题。我只能说祝你好运,并且要四次检查你的固件(显然,现在三次检查不够了)。

关于“更改了一些函数”的事情,你能具体说明一下吗?现在提供的信息不够详细。 - Peter Mortensen
链接似乎已经失效 - “连接超时。 weknowmemes.com的服务器响应时间太长。” - Peter Mortensen
这只是一个指向“我的代码不起作用/我不知道为什么/我的代码可以运行/我也不知道为什么”的梗的链接。 - tmwoods
就“改变了一些函数”而言,老实说我不知道。那是很久以前的事情了,现在意识到我写那个有多么无用。 - tmwoods

-1

我曾经遇到过同样的问题,将波特率设置为1就解决了


很奇怪,这对我也起作用了。我想知道为什么。 - danpop

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接