Freeze on SerialPort.Open / DeviceIoControl / Getc

2020-03-05 06:49发布

问题:

I have a C program which opens a handle to a COM port, writes some bytes to it, reads some bytes out, then closes the handle and exits. However, when I run the program like 10 times in a row, it starts to take very long to complete the GetCommState function and to get stuck in the SetCommState function. The same thing happens in C# with a simple SerialPort object.

The only fix I could find is reconnecting the device to the port. Is there some more elegant way to get rid of this freeze? Is it maybe just some PC configuration error?

Update

I've rewritten the code to make it use DeviceIoControl instead of SetCommState. However, it's exactly the same problem here.

device = 
    CreateFileW(
        L"\\\\.\\COM3", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 
        FILE_FLAG_OVERLAPPED, NULL);

static int SetBaudRate (HANDLE device) {
    int error = 0;
    int success = 0;
    OVERLAPPED overlapped = {0};
    overlapped.hEvent = CreateEvent(NULL, TRUE, 0, NULL);
    if (overlapped.hEvent) {
        SERIAL_BAUD_RATE baudRate = {0};
        baudRate.BaudRate = SERIAL_BAUD_115200;
        error = 
            DeviceIoControl(
                device, IOCTL_SERIAL_SET_BAUD_RATE, &baudRate, 
                sizeof(SERIAL_BAUD_RATE), NULL, 0, NULL, &overlapped);
        if (error || (!error && GetLastError() == ERROR_IO_PENDING)) {
            DWORD bytes = 0;
            if (GetOverlappedResult(device, &overlapped, &bytes, TRUE)) {
                success = 1;
            }
        }
    }
    CloseHandle(overlapped.hEvent);
    return success;
}

First problem: DeviceIoControl doesn't return immediately (although called asynchronously) and hangs for about two minutes. Second problem: It fails with error code 121 (ERR_SEM_TIMEOUT: "The semaphore timeout period has expired.") after those two minutes.

  • The used driver is the standard windows driver usbser.sys
  • Any idea on why the function call doesn't return immediately? And if not, how to set a shorter timeout for the function?
  • Any idea on why the function is failing?

Update 2

Sample C# code which freezes also (like the C program above):

using System;
using System.IO.Ports;

sealed class Program {
    static void Main (string[] args) {
        int i = 0;
        while (true) {
            Console.WriteLine(++i);
            SerialPort p = 
                new SerialPort("com3", 115200, Parity.None, 8, StopBits.One);
            p.DtrEnable = true;
            p.RtsEnable = true;
            p.ParityReplace = 0;
            p.WriteTimeout = 10000;
            p.ReadTimeout = 3000;
            try {
                p.Open();
                Console.WriteLine("Success!");
            } catch (Exception e) {
                Console.WriteLine(e.GetType().Name + ": " + e.Message);
            }
            p.Close();
            Console.ReadLine();
        }
    }
}

A sample output is the following:

1 (device not yet connected)
IOException: The port 'com3' does not exist.

2 (device connected but not yet in windows device manager)
IOException: The port 'com3' does not exist.

3
IOException: The port 'com3' does not exist.

4 (device connected and recognized)
Success!

5
Success!

[...] (with about one second between each enter press)

15
Success!

16 (device still connected and recognized - nothing touched! after two minutes of freeze, semaphore timeout exactly as in the C version)
IOException: The semaphore timeout period has expired.


17 (device disconnected during the two minutes of freeze. it then returns instantly)
IOException: A device attached to the system is not functioning.


18 (device still disconnected - note that the exception is a different one than the one in the beginning although it's the same case: device not connected)
IOException: The specified port does not exist.

19
IOException: The port 'com3' does not exist.

回答1:

This is a working part of my c++ class for handling Serial Port communication changed a bit to accommodate C and your needs. If it doesn't work then its probably your driver for virtual port that's faulty

 DCB SerialPortSettings;
 HANDLE SerialPort;

int OpenPort(WCHAR* PortName,int BaudRate)
{

    SerialPort = CreateFileW(PortName,
        GENERIC_READ|GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_OVERLAPPED,
        NULL);

    if(SerialPort==INVALID_HANDLE_VALUE)
        return -2;

    RtlZeroMemory(&SerialPortSettings,sizeof(DCB));
    SerialPortSettings.DCBlength = sizeof(DCB);
    if(!GetCommState(SerialPort,&SerialPortSettings))
    {
        CloseHandle(SerialPort);
        return -3;
    }

    //8n1 RS485
    SerialPortSettings.BaudRate = BaudRate;
    SerialPortSettings.ByteSize = 8;
    SerialPortSettings.Parity = NOPARITY;
    SerialPortSettings.StopBits = ONESTOPBIT;
    SerialPortSettings.fRtsControl = RTS_CONTROL_TOGGLE;

    if(!SetCommState(SerialPort,&SerialPortSettings))
    {
        CloseHandle(SerialPort);
        return -4;
    }


    return 0;

}

Edit: Try downloading a driver from http://www.ftdichip.com/Drivers/VCP.htm, as the problem you are describing is most likely a driver or a device issue. Worked for Etan :)