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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14885288/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 13:20:18  来源:igfitidea点击:

I/O exception error when using serialport.open()

c#winapiserial-portioexceptionbaud-rate

提问by tmwoods

FINAL UPDATE

最后更新

It was our firmware the whole time. Embarrassing to a degree, but I'm happy we can move forward and I can put learning Java off for another day. My answer is below.

它一直是我们的固件。尴尬到一定程度,但我很高兴我们可以继续前进,我可以将学习 Java 推迟一天。我的回答如下。

UPDATE

更新

So I have more or less given up on this. I think it is a bug that goes down to the API, but I have neither the time, resources nor skill-set to get to the bottom of it. I think there exists some hardware to whom Windows just gives the middle finger. I have downloaded Eclipse, switched to Java and will try to see if that works. If not, you'll see me back here. However, I would absolutely love to solve this and so if anyone has the time or inclination to dig deep into this one, I'd love to see what you come up with. Obviously I will be checking back here from time to time. Please make sure you '@' me in your comments so I am alerted.

所以我或多或少地放弃了这一点。我认为这是一个归结为 API 的错误,但我既没有时间、资源也没有技能来深入了解它。我认为存在一些硬件,Windows 只是给了他们中指。我已经下载了 Eclipse,切换到 Java,并将尝试看看它是否有效。如果没有,你会在这里看到我。但是,我绝对很想解决这个问题,所以如果有人有时间或有兴趣深入研究这个问题,我很想看看你想出了什么。显然,我会不时回到这里查看。请确保您在评论中“@”我,以便我收到提醒。



ORIGINAL POST

原帖

I know there are a few other people dealing with this issue, but I was hoping someone could help me. I am trying to connect to a COM port, but I am getting an I/O exception when I try to use the serialport.Open()command:

我知道还有其他一些人在处理这个问题,但我希望有人能帮助我。我正在尝试连接到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

I am using a Stellaris LM4F232 to emulate a COM port. I can open, access and I get good results using Termite (a terminal program), but whenever I try with Visual Studio it won't even connect, and I get this error. Now I don't even really know what this error means and despite trying to read elsewhere, I still feel lost.

我正在使用 Stellaris LM4F232 来模拟 COM 端口。我可以使用 Termite(一个终端程序)打开、访问并获得良好的结果,但是每当我尝试使用 Visual Studio 时,它甚至都无法连接,并且出现此错误。现在我什至不知道这个错误意味着什么,尽管尝试在其他地方阅读,但我仍然感到迷茫。

Can anyone explain to me what is happening here and maybe I can begin to try to figure this out? I can include more code, but to be honest there isn't much there; all the properties of the serial port device are as normal, and it is only happening with this device (I can use an MSP430 no problem with the same details).

任何人都可以向我解释这里发生了什么,也许我可以开始尝试解决这个问题?我可以包含更多代码,但老实说,那里没有多少;串口设备的所有属性都正常,只有这个设备才会发生(我可以使用 MSP430 没有问题,细节相同)。

My code is shown below for people who would like to see it (note this is just a 'sandbox', not the actual program, but the symptoms are identical):

我的代码如下所示,供想要查看它的人使用(请注意,这只是一个“沙箱”,而不是实际程序,但症状是相同的):

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

and the other settings are done with the property manager (the only difference is baud is set to 230400; all others are on their default). I can open up COM4 with this (an MSP430) which for all intents and purposes is an identical device. I can open COM5 with Termite, so I know the connection is good). And no, I am not trying to open them at the same time. If you need more information let me know and I can post more.

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

EDIT: I'm on day three of trying to figure this out and still no luck. I don't really understand why I can access this COM port through a terminal program and not my own when, as near as I can see, there is absolutely no difference. Is there a program that can 'examine' a COM port to see the properties of it (besides Windows manager I mean)? I'm getting pretty frustrated and am sort of at a stand still in my project until I figure this out...

编辑:我正在尝试解决这个问题的第三天,但仍然没有运气。我真的不明白为什么我可以通过终端程序而不是我自己的程序访问这个 COM 端口,就我所见,这完全没有区别。是否有程序可以“检查”COM 端口以查看其属性(我的意思是除了 Windows 管理器)?我变得非常沮丧,并且在我的项目中处于停滞状态,直到我弄清楚这一点......

EDIT2: I've found an apparent workaround, but I've yet to get it to work here. Now I get a few different I/O errors, but at least it is motion (not sure if it is progress). I've also learned that this is a .NET bug, which has existed since 2.0. I'd still love any help, but if I figure it out I will report back. Zach's code (the workaround linked above) is shown below:

EDIT2:我找到了一个明显的解决方法,但我还没有让它在这里工作。现在我收到了一些不同的 I/O 错误,但至少它是运动(不确定它是否是进度)。我还了解到这是一个 .NET 错误,它从 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");
            }
        }
    }
}

EDIT3: Day 6: I'm still plugging away at this. My water rations are low, but still I struggle on. I feel help must surely be on the horizon. Whoever finds this journal bring my remains back to Canada and find Nicole. Tell her I love her.

EDIT3:第 6 天:我仍在努力解决这个问题。我的饮水量很低,但我仍然在挣扎。我觉得肯定会得到帮助。谁找到这本日记,就把我的遗体带回加拿大找妮可。告诉她我爱她。

But seriously, I have no idea what is causing this problem. I'm wondering if it is purely on the embedded side; maybe because it is USB On-The-Go(OTG), or because the device is capable of being a host also. Has anyone run into that problem? It doesn't explain why I can use Termite (a terminal program, for those viewers just joining us) though. I have been trying to find an open source terminal program that a) works and b) see a). As per usual, I will report back if I discover the issue here as I have now found countless forums where it sounds people have had this issue dating back to 2006.

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

EDIT4: So as per the advice given, I downloaded a port monitoring software application (I got Eltima Serial Port Monitor), and it does look like a baud issue:

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

Screen capture from Eltima

Eltima 的屏幕截图

But strangely no matter what baud I set, it still fails. And also can someone explain what the up/down thing means? I tried googling it, but the keywords are too general. As usual, I will keep reporting back any changes.

但奇怪的是,无论我设置什么波特率,它仍然失败。并且有人可以解释向上/向下的含义吗?我试着用谷歌搜索它,但关键字太笼统了。像往常一样,我会继续报告任何变化。

Also, for the record, I can connect using Eltima at a baud of 115200 (same as Termite). Unfortunately this does not work in Visual Studio.

另外,作为记录,我可以使用 Eltima 以 115200 的波特率进行连接(与 Termite 相同)。不幸的是,这在 Visual Studio 中不起作用。

EDIT5: Our plot takes a surprise twist. I was monitoring what happens when Termite connects to the COM port in question and BLAM! Termite throws the exact same error as my program, but it ignoresit. Genius, right? Sloppy, but it works. Now I need to learn how to ignore IOExceptions. I'll report back when I get it figured out.

EDIT5:我们的情节发生了意外的转折。我正在监视当 Termite 连接到有问题的 COM 端口和 BLAM 时会发生什么!Termite 抛出与我的程序完全相同的错误,但它忽略了它。天才,对吧?马虎,但它的工作原理。现在我需要学习如何忽略 IOExceptions。当我弄清楚时会回来报告。

EDIT6: So as it turns out it is a baud rate issue, but it goes deeper. I have been using Eltima Serial Port Monitoring software, and it is very intuitive and easy to use. I would recommend it. After some researchI have learned that you cannot ignore this exception and still connect to the serial port using .NET's library.

EDIT6:所以事实证明这是一个波特率问题,但它更深入。我一直在使用 Eltima 串口监控软件,它非常直观易用。我会推荐它。经过一些研究,我了解到您不能忽略此异常并仍然使用 .NET 库连接到串行端口。

So I have to go deeper into the Win32 API and write my own. I have found a few pages that touch on this, but to be honest I have never done anything like this before, so it may be a while before I report back, but I will definitely figure this out and get back to everyone. There are way too many who suffer from this problem.

所以我必须更深入地研究 Win32 API 并编写自己的。我找到了几页涉及到这一点,但说实话我以前从未做过这样的事情,所以我可能需要一段时间才能回来报告,但我一定会弄清楚这一点并回复给大家。受这个问题困扰的人太多了。

I have found quite a few forums and websites where I can see the exact same symptoms, but nobody has really done much besides say 'Yeah, .NET sucks'. I plan on writing a full static library class and then publish either on my website, here and wherever else I can. Hopefully .NET will take notice (this bug has existed since 2.0).

我找到了很多论坛和网站,在那里我可以看到完全相同的症状,但除了说“是的,.NET 很烂”之外,没有人真正做过很多事情。我计划编写一个完整的静态库类,然后在我的网站上、这里以及我可以的任何其他地方发布。希望 .NET 会注意到(这个错误自 2.0 以来就存在)。

采纳答案by tmwoods

And so our thrilling tale comes to a close. It was firmware the whole time (i.e. the code on the embedded device). We changed up a few functions and essentially poked around, chopped, added and altogether cleaned up the code and voila, the code is working. This picsums it up pretty well. Curse you firmware!!

就这样,我们惊心动魄的故事结束了。它一直是固件(即嵌入式设备上的代码)。我们更改了一些功能,基本上是四处探索、切碎、添加并完全清理代码,瞧,代码正在运行。这张图总结的很好。诅咒你的固件!!

However, the bug described in my (lengthy) question still persists for many people and I know there are lots of people out there who still have it. All I can say is good luck and quadruple check your firmware (apparently triple checking it isn't enough these days).

但是,我的(冗长的)问题中描述的错误对很多人来说仍然存在,我知道还有很多人仍然拥有它。我只能说祝你好运,四重检查你的固件(显然现在三重检查还不够)。

回答by Hans Passant

This comes from the serial port driver; it is unhappy about one of the settings. With baudrate being a good candidate, drivers tend to allow only up to 115200. Albeit that this should not be a restriction when this is a dedicated CAN bus product.

这来自串口驱动;它对其中一项设置不满意。由于波特率是一个很好的候选者,驱动程序往往只允许最多 115200。尽管当这是一个专用的 CAN 总线产品时,这不应该是一个限制。

The best way to tackle this is by using Sysinternals' Portmonutility; you can see what is being sent to the driver. Observe it for Terminate first; that's your known-to-work baseline. Then tinker with SerialPort properties until the initialization commands, as you see them in PortMon, sent by your program matches Termite's. Just the values, not the order. If that doesn't pan out either then take it to the parking lot and back over it with your car several times and buy another brand.

解决此问题的最佳方法是使用 Sysinternals 的Portmon实用程序;您可以看到发送给驱动程序的内容。首先观察它以终止;那是您已知的工作基线。然后修改 SerialPort 属性,直到初始化命令(如您在 PortMon 中看到的那样)由您的程序发送与 Termite 的匹配。只是值,而不是顺序。如果那也不行,那就把它带到停车场,然后用你的车回来几次,然后再买另一个品牌。



Update: it certainly looks like a baudrate problem. That's an issue in .NET; it is not going to ignore the driver's error return code like your terminal emulator programs do. The actual value should not matter since you are talking to an emulatedserial port. There is however a possible issue with the CAN bus speed; rates are variable and it isn't clear to me how they are negotiated. This tended to be done with DIP switches in the olden days, and it may well be that the driver wants you to specify the speed through the baudrate setting. There ought to be something about it on the box or in the manual. Typical speeds are 40, 250 or 500 kbit/s. The manufacturer certainly would know; give them a call.

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

回答by Tim

I faced a similar problem as reported in this thread, but I managed to solve the problem!

我遇到了这个线程中报告的类似问题,但我设法解决了这个问题!

I am using STM32F2xx for the VCP!

我使用 STM32F2xx 作为 VCP!

And indeed it was my firmware problem. I forgot to include serial port settings in my USB callback!

确实是我的固件问题。我忘了在我的 USB 回调中包含串口设置!

The process of connecting a serial port from PC and firmware:

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

  1. When a PC opens a serial port communication, the PC will send some command into the "configuration endpoint"
  2. In the firmware, it would have a callback and the firmware will provide all the USB information (they call it a USB descriptor)
  3. USB information is the configuration of each endpoint, (for example, latency, data size transmission, and type of USB - high speed or low speed)
  4. Once the firmware has completed sending all the information, the PC will acknowledge and USB communication is successfully opened
  5. Then, the PC will send a command to get the serial port settings from the firmware
  6. Serial port settings are baudrate, data parity, and bit length.
  7. In firmware, it should reply the serial port settings back to PC (my mistake occurs here; I didn't not send any serial port settings back to the PC)
  8. If successful, PC will start the serial port communication!
  9. If failed, PC will give an open serial port error (but, do note that this error sometimes is bypassed)
  1. 当PC打开串口通信时,PC会向“配置端点”发送一些命令
  2. 在固件中,它将有一个回调,固件将提供所有 USB 信息(他们称之为 USB 描述符)
  3. USB 信息是每个端点的配置,(例如,延迟、数据大小传输和 USB 类型 - 高速或低速)
  4. 一旦固件完成发送所有信息,PC 将确认并成功打开 USB 通信
  5. 然后,PC 会发送命令从固件中获取串口设置
  6. 串口设置为波特率、数据奇偶校验和位长。
  7. 在固件中,它应该将串口设置回复给PC(我的错误发生在这里;我没有将任何串口设置发送回PC
  8. 如果成功,PC 将开始串口通信!
  9. 如果失败,PC 会给出一个打开串口的错误(但是,请注意这个错误有时会被绕过)

In STM32 firmware code:

在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;
        }:
....

回答by tanutapi

I ran into the same situation. I am trying to connect serial communication to my 3G USB Dongle (Huawei E303F) at /dev/ttyUSB0. I use Monoin Raspbian(Raspberry Pi 2). On my development PC and macOS, my program runs fine. But when I deploy it into Raspbian, I got the IOException Broken Pipeerror on Serial.Open().

我遇到了同样的情况。我正在尝试将串行通信连接到位于 /dev/ttyUSB0 的 3G USB Dongle (Huawei E303F)。我在Raspbian( Raspberry Pi 2) 中使用Mono。在我的开发 PC 和 macOS 上,我的程序运行良好。但是当我将它部署到 Raspbian 时,我在 Serial.Open() 上收到了IOException Broken Pipe错误。

It took me three days of debugging, and I tried all possible solutions. Finally I found that I have to set...

我花了三天的时间调试,我尝试了所有可能的解决方案。最后我发现我必须设置...

serialPort.DtrEnable = true;
serialPort.RtsEnable = true;

Before calling .Open().

在调用 .Open() 之前。

回答by ernesto torres

I had the same problem and setting the baud rate to 1 fixed it!

我遇到了同样的问题,将波特率设置为 1修复了它