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

6

27

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.

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.


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:

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.

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

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.

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...

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:

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.

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.

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:

Screen capture from 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.

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 ignores it. 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.

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 research I have learned that you cannot ignore this exception and still connect to the serial port using .NET's library.

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.

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

Negative answered 14/2, 2013 at 22:31 Comment(26)
What settings are you using to connect? Can we please see the code?Anaerobe
I just added my code to the original post. I don't know how to add code in the comments...Negative
My only guess it that your properties are somehow wrong. Perhaps try lowering the baud rate down to 9600.Anaerobe
No dice :( Same error message.Negative
Does the error message point to anything in particular? I don't have experience with this and I'm not too sure what I am looking at.Negative
If you go to Control PanelAdministrative ToolsComputer ManagementDevice ManagerPorts, find the virtual port and open its properties you should be able to see its setting under the Port Settings tab. I would try matching these settings.Anaerobe
That is how I started and everything is identical.Negative
Um. Have you tried switching it off and back on again?Anaerobe
I've tried all the 'obvious' methods; I think this goes deeper and was hoping the issue was somehow shown in the IO code. I'm going to be in motion for a few hours and may not check back straight away. Thanks for trying though :)Negative
Hi tmwoods, Can you please post Zach's code into your question? Thanks, it's customary in StackOverflow (and Superuser) questions/answers to include a summary of the contents of a link or the highlights that specifically answer the question. The goal of SE sites is to become a resource of knowledge, of answers, for years to come. With a link-only reference to an answer, people must dig through another resource to locate an answer he/she might not be sure about. Most importantly, if your link were to ever break, your answer is useless for anyone who visits this page in the future. Good luck!Contrapositive
Of course I will. Thanks for the tip :)Negative
Just wondering... Have you tried running VS in elevated mode (e.g. as Administrator)?Navar
I have not; but I definitely will try it the next time I have a chance :) EDIT: I ran to the lab to try it. No luck :(Negative
3 things i'd try: 1. give admin permissions to the exe you are running, not only to the visual studio 2. assign a different port number, try use COM1-COM4, some devices are designed (not well) to work only with the 'low' com ports. 3. try using this little open source app to connect msmvps.com/blogs/coad/archive/2005/03/23/SerialPort-2800_RS_2D00_232-Serial-COM-Port_2900-in-C_2300_-.NET.aspxGiselle
I definitely will; I have tried the second one (changing port numbers) but I'm pretty excited to try your open source app. Once I get to the lab later today (hopefully) I will report back.Negative
Alas, no luck. Throws the same error. So far Termite is the only program that will open it (tried Realterm and PuTTy, as well as three third-party apps, this open source and my own). Bummer.Negative
I've seen similar issues with a prolific based USB serial port where any .NET code would fail to open it if it's been used by any native Win32 program previously. The only solution I had was to re plug the USB port.Karwan
Do you mean physically re-plug it? Because that hasn't worked for me. Or is that some lingo that I am too new to understand? (I am hoping for the latter)...Negative
@Negative Yes, as in plug in again.Karwan
@Deanna: In that case, yes, I have tried that. I've also tried running this in safe mode, through a linux environment and a few others to make sure it isn't being used in a secondary program. No luck still :(Negative
@Negative hey! i am facing this same problem as you!!! i am using the STM32F2xx to emulate it as VCP, and I have same problem as you, cant open in .NET but opens in terminal, may I know how did u solve the firmware part?Settling
@Settling Hi Tim! It was that we were using an older library for the TI chip (I see you are using ST). We simply replaced it with a newer library and it worked. I know that isn't the news you need to fix your problem. I will try to take a look at the two libraries to see any differences but honestly it's been a while since I looked at this code and it's a bit fuzzy as to what actually changed. Sorry I couldn't be more help!Negative
@Negative hey! i managed to trace the problem in my firmware, when the PC open the serial port communication, it will ask for the firmware VCP information for baudrate, parity, and data bit length, in my firmware the callback for VCP communication doesnt contain info about those things, and when PC tries to open it causes problem because the firmware VCP is not giving any of those information which causes the error! i think this is the problem, as i fixed it till now i dont have the problem! you mind to check ur firmware if the same behavior exists between ur old and new firmware? to confirm?Settling
@Settling I declared all that right in the firmware (i.e. it is static, not dynamic). I set those values permanently and just did the same thing in my Visual Studio code (I was doing Windows Forms). I did this with both (old & new) versions of my firmware.Negative
@Negative then i not sure! haha because in my firmware (using STM32F2xx) there is a callback for the USB VCP (CDC_Control_FS -> cmd == CDC_GET_LINE_CODING), here i dint declare my baudrate, parity and data bit! when the PC open the serial port, the firmware will make a callback into this functioon, to check for the params! I guess my problem and yours is different?Settling
Hi @Negative , Did you try to Run GC.Collect() before getting information from port?Rust
N
1

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 pic sums 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).

Negative answered 19/3, 2013 at 19:36 Comment(4)
Re "changed up a few functions": Can you be more specific? There is not a lot to go on.Quackery
(The link seems to be broken - "The connection has timed out. The server at weknowmemes.com is taking too long to respond.")Quackery
It was just a link to the 'My code doesn't work/I don't know why/My code works/I don't know why' meme.Negative
As far 'changed up a few functions' honestly I have no idea. It was a while back now and realize how useless me writing that was.Negative
F
8

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.

The best way to tackle this is by using Sysinternals' Portmon utility; 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.


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 emulated serial 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.

Fireplace answered 15/2, 2013 at 1:29 Comment(7)
I am out of the office for the evening but when I get in tomorrow I will give it a go :)Negative
Can you recommend a more up-to-date port monitoring software package? The only version I can find of this isn't compatible with Windows 7 (it would appear the last time they updated this software was for Win98). It says it is copyrighted to 2012, but I can't get it to connect and all the help files are either missing or explained for a Win98 environment.Negative
I tried Eltima Serial Port Monitor, see the main body of the question above.Negative
Erm, are we talking about SysInternals' PortMon utility? It most certainly runs on Windows 7. Do note that you have to run it elevated, right-click + Run as Administrator.Fireplace
I already deleted it but I'm sure that would have fixed the problem. Eltima works great and I see where the invalid parameter is occuring, with both Termite and my program. Now I am trying to figure out how to safely ignore the exception and open the port. I don't want to mark the question as solved until I do this. Thanks, any more input is appreciated :)Negative
So I wrote a program to run test connecting with baud rates between 1200 and 500,000 bps (with a suitable pause between each). It took a while to run the whole thing but there was no luck (I paused it a few times to test the connction with Termite just in case). This thread explains what it is that I actually think is the issue. So what I'm going to do is try to write some custom functions using the Win32 API and then release them because too many people suffer from this. I'll report back probably within a week once I either fail or accomplish this.Negative
The BaudRate for comports should be almost always set to 9600, try that out pleaseSavannahsavant
S
7

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

I am using STM32F2xx for the VCP!

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

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

  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)

In STM32 firmware code:

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;
        }:
....
Settling answered 14/11, 2014 at 8:10 Comment(1)
This also did the trick for me when I was running into a similar issue. Specifically, whenever the device would disconnect and reconnect, it would appear in device manager and be recognized as a USB-Serial COM port, but would be unusable by any software and .NET would throw the "A device attached to the system is not functioning" exception. Issue was fixed by ensuring that the USBUART device (Cypress PSoC5LP) firmware was properly setting the active port and re-initializing the CDC interface (sending over the port settings) whenever a change in USB Configuration was detected.Brusque
V
5

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 Mono in 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 Pipe error on Serial.Open().

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().

Vierra answered 8/1, 2017 at 14:34 Comment(0)
N
1

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 pic sums 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).

Negative answered 19/3, 2013 at 19:36 Comment(4)
Re "changed up a few functions": Can you be more specific? There is not a lot to go on.Quackery
(The link seems to be broken - "The connection has timed out. The server at weknowmemes.com is taking too long to respond.")Quackery
It was just a link to the 'My code doesn't work/I don't know why/My code works/I don't know why' meme.Negative
As far 'changed up a few functions' honestly I have no idea. It was a while back now and realize how useless me writing that was.Negative
S
0

I had a similar problem.
I've solved it by going into device manager. Under the device connected comport I uninstalled the driver with the COM-port I was using. Then I disconnected the cable and reconnected it. And this worked out somehow. Maybe it could help you as well.

Staggers answered 23/3 at 16:54 Comment(0)
J
-1

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

Jeepers answered 8/5, 2018 at 15:54 Comment(1)
Strangely that worked for me too. I would like to know why.Quietus

© 2022 - 2024 — McMap. All rights reserved.