【问题标题】:Asynchronous Serial I/O on Win32 -- How To Create An Alert EventWin32 上的异步串行 I/O -- 如何创建警报事件
【发布时间】:2020-05-09 01:44:48
【问题描述】:

我尝试按照 Allen Denver 的 article 来实现异步串行端口 I/O。本文中没有提到的是如何或在何处以有用的方式调用主程序循环中的函数。在将它们放入每秒返回的循环之后,很明显不需要定期调用 ReadFile()。我尝试将 WaitCommEvent() 放入循环中,这样只有在字符到达端口时才会进行读取。这导致了一连串的错误。在尝试将代码简化为可以在此处发布的内容时,即使我尝试以 OVERLAPPED 模式打开文件也开始失败,因此我似乎无法发布有用的示例。但是,我将发布与基本概念类似的内容,即如果我可以简单地使用异步事件(例如串行端口上显示的字符)来触发它们,我并不真正关心读取和写入是否必须是同步的。 Allen Denver 文章中的异步函数相当复杂,从他的示例中我不清楚如何触发对到达端口的字符的读取。

在下面的代码中,我试图等待字符到达的 while(1) 循环体中有五行被注释掉。 WaitCommEvent() 错误似乎引用了挂起的 I/O,即使端口以重叠模式打开(在原始代码中),并且发送和接收函数都有自己的 OVERLAPPED 结构。解决这个问题的正确方法是什么?

/* WINSIO.c                                                        2020-01-22 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int ConfigureSerialPort(HANDLE hPort)
{
  int Status;
  DCB dcb = {0};
  COMMTIMEOUTS timeouts;

  dcb.DCBlength = sizeof(dcb);

  Status = GetCommTimeouts(hPort, &timeouts);
  GetCommTimeouts(hPort, &timeouts);
  timeouts.ReadIntervalTimeout          = 50;
  timeouts.ReadTotalTimeoutConstant     = 50;
  timeouts.ReadTotalTimeoutMultiplier   = 50;
  timeouts.WriteTotalTimeoutConstant    = 50;
  timeouts.WriteTotalTimeoutMultiplier  = 10;
  if (!SetCommTimeouts(hPort, &timeouts)) {
    printf("Error setting timeouts on port!\n");
  }

  Status = GetCommState(hPort, &dcb);
  dcb.BaudRate =          19200;
  dcb.Parity =            NOPARITY;
  dcb.fBinary =           TRUE;             // Binary mode; no EOF check 
  dcb.fParity =           FALSE;            // Enable parity checking 
  dcb.fOutxCtsFlow =      FALSE;            // No CTS output flow control 
  dcb.fOutxDsrFlow =      FALSE;            // No DSR output flow control 
  dcb.fDtrControl =       DTR_CONTROL_DISABLE; // DTR flow control type 
  dcb.fDsrSensitivity =   FALSE;            // DSR sensitivity 
  dcb.fTXContinueOnXoff = FALSE;            // XOFF continues Tx 
  dcb.fOutX =             FALSE;            // No XON/XOFF out flow control 
  dcb.fInX =              FALSE;            // No XON/XOFF in flow control
  dcb.fErrorChar =        FALSE;            // Disable error replacement 
  dcb.fNull =             FALSE;            // Disable null stripping 
  dcb.fRtsControl =       RTS_CONTROL_DISABLE; // RTS flow control 
  dcb.fAbortOnError =     FALSE;            // Do not abort reads/writes on err
  dcb.ByteSize =          8;                // Number of bits/byte, 4-8 
  dcb.StopBits =          ONESTOPBIT;       // 0,1,2 = 1, 1.5, 2
  dcb.EvtChar =           0x84;             // 'T' 

  if (!SetCommState (hPort, &dcb)) {
    printf("Unable to configure serial port!\n");
  }
  return 0;
};
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int sendpckt(HANDLE hComm, unsigned length, unsigned char * pckt)
{
    unsigned long NbytesWritten = 0;
    int result;
    DWORD dwCommEvent;
    OVERLAPPED oWrite;

    oWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (oWrite.hEvent == NULL)
      return FALSE; // Error creating overlapped event handle.

    result = WriteFile(hComm, pckt, length, &NbytesWritten, NULL);
    //result = WriteFile(hComm, pckt, length, &NbytesWritten, &oWrite);

    if (!result) printf("Err: %d\n", GetLastError());
    WaitCommEvent(hComm, &dwCommEvent, &oWrite);

//    printf("Wrote? %d:%d\n", result, NbytesWritten);
    CloseHandle(oWrite.hEvent);
    return 0;
};
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int recvpckt(HANDLE hComm, unsigned char * pckt)
{
  int Status, idx = 0, len = 0;
  unsigned long Nbytes;
  unsigned char chRead;
  OVERLAPPED oRead;

    do {
      //Status = ReadFile(hComm, &chRead, 1, &Nbytes, &oRead);
      Status = ReadFile(hComm, &chRead, 1, &Nbytes, NULL);
      if (Status) {
      pckt[idx++] = chRead;
      if(Nbytes > 0) len = idx;
       }
      }
     while(Nbytes > 0);
  return len;
};
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int args(int argc, char * argv[], char * port) 
{
  static int i;
  i = atoi(argv[1]);
  sprintf(port, "\\\\.\\COM%d", i);
  return i;
};
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void main(int argc, char *argv[]) {

  HANDLE hPort;
  int result, len, Status;
  DWORD dwCommEvent;
  OVERLAPPED o;
  unsigned idx = 0;
  unsigned char TXBUF[] = "T", RXBUF[2048], port[64];

  if (argc > 1) result = args(argc, argv, port);
   SetConsoleTitle(port); // To specify serial port number on open 
  hPort = CreateFile (port, GENERIC_READ | GENERIC_WRITE, 0, NULL,
                       //OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); 
                       OPEN_EXISTING, 0, NULL); 

  ConfigureSerialPort(hPort);
    if (hPort == INVALID_HANDLE_VALUE) {
     printf("Error in openning serial port\n");
     exit(0); // No point in going on if serial port didn't open
     }
    else
     printf("Serial Port Open\n");

    Status = SetCommMask(hPort, EV_RXCHAR);
  if (Status == FALSE) printf("Error! Setting CommMask\n");

  o.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  if (o.hEvent == NULL) printf("Error creating overlapped event; abort.\n");

  while (1) {

    sendpckt(hPort, 1, TXBUF);
    printf("Sending 0x%.2X\n", TXBUF[0]);

//  Status = WaitCommEvent(hPort, &dwCommEvent, &o); // Wait for chars
//  if (Status == FALSE)
//    printf("Error Setting WaitCommEvent()%d\n", GetLastError());
//  else { // If WaitCommEvent() == TRUE then Read the received data 

//    printf("Chars Recveived\n");  
    len = recvpckt(hPort, RXBUF);
    if (len > 0) 
    {
    printf("RCVD(%d): ", len);
    }

  for (idx=0; idx<len; idx++)
   {
    printf("%d:0x%.2X ", idx+1, RXBUF[idx]);
   }

//    }
    SleepEx(1000, TRUE); // How often to look for data in RX buffer
  }
  CloseHandle(o.hEvent); // Close the Event Handle
  CloseHandle(hPort); // Close the serial port
}

在收到 Rita Han 非常感谢的反馈后发布附加代码。从其他帖子中可以清楚地看出,我的同步示例没有理解重点。所以我附上了最有效的异步版本,但还有很多不足之处。如果可以将其简化为 Rita Han 示例的长度,那就太棒了。此示例执行“功能”,这意味着它既发送又接收。问题是,正如最初所说,它需要定期轮询端口以查找存在的字符,这是一个非常大的拖累。以下代码是 Allen Denver 代码的几乎相同的再创作,它不提供我正在寻找的基于事件的响应,但它确实发送和接收字符。我目前正在通过带有两个串行端口的空调制解调器电缆运行它,因此很容易观察到这种行为。通过更改 SleepEX() 语句的超时值,可以清楚地了解对轮询串行端口的依赖。尽管 Rita Han 的反馈很有用,但代码没有表现出我正在寻找的行为。如果有人可以采取以下示例,并使其响应到达串行端口的字符而不是等待轮询,那就是我正在寻找的。我需要使用 ReadFileEX() 吗?还有其他方法可以注册需要在这里混合的事件吗? Joseph M. Newcomer 在此article 中对所有串行事件的使用打了折扣,称它们仅存在于 16 位 Windows 兼容性,然后继续提供一个比 Allen Denver 所做的更复杂的串行端口编程示例。真的需要这么难吗?

/* WINAIO.c                                                        2020-01-22 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int ConfigureSerialPort(HANDLE hPort) {
  int Status;
  DCB dcb = {0};
  COMMTIMEOUTS timeouts;

  dcb.DCBlength = sizeof(dcb);

  Status = GetCommTimeouts(hPort, & timeouts);
  GetCommTimeouts(hPort, & timeouts);
  timeouts.ReadIntervalTimeout = 50;
  timeouts.ReadTotalTimeoutConstant = 50;
  timeouts.ReadTotalTimeoutMultiplier = 50;
  timeouts.WriteTotalTimeoutConstant = 50;
  timeouts.WriteTotalTimeoutMultiplier = 10;
  if (!SetCommTimeouts(hPort, & timeouts)) {
    printf("Error setting timeouts on port!\n");
  }

  Status = GetCommState(hPort, & dcb);
  dcb.BaudRate = 19200;
  dcb.Parity = NOPARITY;
  dcb.fBinary = TRUE; // Binary mode; no EOF check 
  dcb.fParity = FALSE; // Enable parity checking 
  dcb.fOutxCtsFlow = FALSE; // No CTS output flow control 
  dcb.fOutxDsrFlow = FALSE; // No DSR output flow control 
  dcb.fDtrControl = DTR_CONTROL_DISABLE; // DTR flow control type 
  dcb.fDsrSensitivity = FALSE; // DSR sensitivity 
  dcb.fTXContinueOnXoff = FALSE; // XOFF continues Tx 
  dcb.fOutX = FALSE; // No XON/XOFF out flow control 
  dcb.fInX = FALSE; // No XON/XOFF in flow control
  dcb.fErrorChar = FALSE; // Disable error replacement 
  dcb.fNull = FALSE; // Disable null stripping 
  dcb.fRtsControl = RTS_CONTROL_DISABLE; // RTS flow control 
  dcb.fAbortOnError = FALSE; // Do not abort reads/writes on err
  dcb.ByteSize = 8; // Number of bits/byte, 4-8 
  dcb.StopBits = ONESTOPBIT; // 0,1,2 = 1, 1.5, 2
  dcb.EvtChar = 0x84; // 'T' 

  if (!SetCommState(hPort, & dcb)) {
    printf("Unable to configure serial port!\n");
  }
  return 0;
};
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int sendpckt(HANDLE hComm, unsigned length, unsigned char *pckt) {
  unsigned long NbytesWritten = 0;
  OVERLAPPED ovl = {0};
  BOOL fRes;

  ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  if (ovl.hEvent == NULL)
    return FALSE; // Error creating overlapped event handle.

  //Issue Write.
  if (!WriteFile(hComm, pckt, length, & NbytesWritten, & ovl)) {
    if (GetLastError() != ERROR_IO_PENDING) {
      fRes = FALSE; // WriteFile failed, but isn't delayed. Report error...
    } else {
      //Write is pending.
      if (!GetOverlappedResult(hComm, & ovl, & NbytesWritten, TRUE))
        fRes = FALSE;
      else
        fRes = TRUE;
    }
  } else // Write operation completed successfully.
    fRes = TRUE;
  //    printf(" 0X%.2X ", chWrite);
  CloseHandle(ovl.hEvent);
  return fRes;
};
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int recvpckt(HANDLE hComm, unsigned char *pckt) {
  # define READ_TIMEOUT 500 // milliseconds
  int len = 0;
  unsigned long Nbytes;
  unsigned char chRead, BUF[2048];
  BOOL fWaitingOnRead = FALSE;
  OVERLAPPED ovl = {0};
  DWORD dwRes;

  /*
  Status = SetCommMask(hComm, EV_RXFLAG);
  if (Status == FALSE) printf("Error! Setting CommMask\n");
//  else                 printf("Setting CommMask Succesful\n");
//  */

  ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  if (ovl.hEvent == NULL) printf("Error creating overlapped event; abort.\n");

  if (!fWaitingOnRead) {
    // Issue read operation
    if (!ReadFile(hComm, & BUF, 2048, & Nbytes, & ovl))
    //if(!ReadFile(hComm, &chRead, 1, &Nbytes, &ovl))
    {
      if (GetLastError() != ERROR_IO_PENDING) // read not delayed?
        // Error in communications; report it
        printf("Error in Communications...\n");
      else {
        fWaitingOnRead = TRUE;
        //   printf("l:%d ", len); // shows loop alive
      }
    } else {
      if (Nbytes > 0) {
        // read completed immediately
        memcpy(pckt, BUF, Nbytes);
        len = Nbytes;
        printf("Immediate Read Completion\n");
        //HandleASuccessfulRead(lpbuf, dwRead);
      }
    }
  }
  if (fWaitingOnRead) {
    dwRes = WaitForSingleObject(ovl.hEvent, READ_TIMEOUT);
    switch (dwRes) {
      // Read completed.
    case WAIT_OBJECT_0:
      if (!GetOverlappedResult(hComm, & ovl, & Nbytes, FALSE))
        printf("Error in Communications Lower Portion\n");
      // Error in communications; report it.
      else {
        if (Nbytes > 0) {
          // Read completed successfully
          // HandleASuccessfulRead(lpbuf, dwRead);
          // Will run away and execute here every time
          fWaitingOnRead = FALSE;
          memcpy(pckt, BUF, Nbytes);
          len = Nbytes;
          printf("Read Completion After Wait\n");
        }
      }
    case WAIT_TIMEOUT:
      //printf("l:%d %d\n", len,rxstate);
      // Operation isn't complete yet. fWaitingOnRead flag isn't changed
      // since I'll loop back around, and I don't want to issue another read
      // until the first one finishes.
      // Good time to do some background work.
      break;

    default:
      // Error in the WaitForSingleObject; abort.
      // This indicates a problem with the OVERLAPPED structure's event handle
      break;
    }
  }
  CloseHandle(ovl.hEvent);
  return Nbytes;
};
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int args(int argc, char *argv[], char *port) {
  static int i;
  i = atoi(argv[1]);
  sprintf(port, "\\\\.\\COM%d", i);
  return i;
};
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void main(int argc, char *argv[]) {

  HANDLE hPort;
  int result, len, Status;
  DWORD dwCommEvent;
  OVERLAPPED o;
  unsigned idx = 0;
  unsigned char TXBUF[] = "T", RXBUF[2048], port[64];

  if (argc > 1) result = args(argc, argv, port);
  SetConsoleTitle(port); // To specify serial port number on open 
  hPort = CreateFile(port, GENERIC_READ | GENERIC_WRITE, 0, NULL,
    OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
  //OPEN_EXISTING, 0, NULL); 

  ConfigureSerialPort(hPort);
  if (hPort == INVALID_HANDLE_VALUE) {
    printf("Error in openning serial port\n");
    exit(0); // No point in going on if serial port didn't open
  } else
    printf("Serial Port Open\n");

  Status = SetCommMask(hPort, EV_RXCHAR);
  if (Status == FALSE) printf("Error! Setting CommMask\n");

  o.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  if (o.hEvent == NULL) printf("Error creating overlapped event; abort.\n");

  while (1) { // This is the loop for getting work done.

    sendpckt(hPort, 1, TXBUF);
    printf("Sending 0x%.2X\n", TXBUF[0]);

    //  Status = WaitCommEvent(hPort, &dwCommEvent, &o); // Wait for chars
    //  if (Status == FALSE)
    //    printf("Error Setting WaitCommEvent()%d\n", GetLastError());
    //  else { // If WaitCommEvent() == TRUE then Read the received data 

    //    printf("Chars Recveived\n");  
    len = recvpckt(hPort, RXBUF);
    if (len > 0) {
      printf("RCVD(%d): ", len);
    }

    for (idx = 0; idx < len; idx++) {
      printf("%d:0x%.2X ", idx + 1, RXBUF[idx]);
    }

    //    }
    SleepEx(1000, TRUE); // How often to look for data in RX buffer
    // And how often to send data to the other port
  }
  CloseHandle(o.hEvent); // Close the Event Handle
  CloseHandle(hPort); // Close the serial port
}

【问题讨论】:

  • 你需要异步 I/O 吗?并在 I/O 完成时使用 3 种可能的事件 - 麦汁方式来获取通知
  • 您介意对文本进行格式化以使其更易于阅读吗?
  • OVERLAPPED 在 I/O 激活之前必须有效。结果,如果您在函数中将其声明为局部变量 - 在 I/O 激活之前,您无法退出函数。一般来说,你绝不能为OVERLAPPED使用本地变量
  • 我需要 OVERLAPPED I/O 的 ONLY 原因是为了防止 ReadFile() 在等待数据到达时阻塞所有其他操作。否则我必须不断地轮询端口以获取数据。如果以非重叠方式执行,WaitCommEvent() 会阻止执行,这也将其排除在外。
  • @KyleG - 异步 - 这是基于事件的编程。为此,您需要绝对的另一个代码结构。您开始 I/O,而不是等待它完成。相反,您有线程,它等待事件(用户输入、apc、iocp 数据包)并根据事件执行操作。并更好地在文件中使用 apc 或 iocp,而不是重叠事件。

标签: c asynchronous winapi


【解决方案1】:
  • 如果要异步读写,请在CreateFile 中使用FILE_FLAG_OVERLAPPED 打开串口。
  • 对于ReadFile 中的lpNumberOfBytesRead 参数:如果这是异步操作,则使用NULL 作为此参数以避免潜在的错误结果。您可以通过InternalHighOVERLAPPED 查看读取长度。
  • ReadFileWriteFile 可能会导致错误代码ERROR_IO_PENDING,它不是失败;它指定写操作异步等待完成。

以下是你可以参考的例子:

int sendpckt(HANDLE hComm, unsigned length, unsigned char * pckt)
{
    BOOL result;
    DWORD dwCommEvent;
    OVERLAPPED oWrite = { 0 };
    DWORD errCode;

    result = SetCommMask(hComm, EV_TXEMPTY);
    if (!result) printf("Err: %d\n", GetLastError());

    result = WriteFile(hComm, pckt, length, NULL, &oWrite);
    if (result == FALSE)
    {
        errCode = GetLastError();

        if (errCode != ERROR_IO_PENDING)
            printf("Error! Setting CommMask\n");
    }

    OVERLAPPED commOverlapped = { 0 };

    commOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (commOverlapped.hEvent == NULL)
        return FALSE; // Error creating overlapped event handle.

    assert(commOverlapped.hEvent);

    result = WaitCommEvent(hComm, &dwCommEvent, &commOverlapped);
    if (!dwCommEvent)
        printf("Error Setting WaitCommEvent()%d\n", GetLastError());
    else
    {
        // If WaitCommEvent() == TRUE then Read the received data 
        if (dwCommEvent & EV_TXEMPTY)
        {
            printf("Send complete.\n");
        }

    }

    CloseHandle(oWrite.hEvent);
    CloseHandle(commOverlapped.hEvent);

    return 0;
};
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int recvpckt(HANDLE hComm, unsigned char * pckt)
{
    BOOL result;
    int len = 0;
    OVERLAPPED oRead = { 0 };
    DWORD errCode;
    DWORD dwCommEvent;

    result = SetCommMask(hComm, EV_RXCHAR);
    if (!result) printf("Err: %d\n", GetLastError());

    result = ReadFile(hComm, pckt, 2048, NULL, &oRead);
    if (result == FALSE)
    {
        errCode = GetLastError();

        if (errCode != ERROR_IO_PENDING)
            printf("nError! Setting CommMask\n");
    }

    OVERLAPPED commOverlapped = { 0 };

    commOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (commOverlapped.hEvent == NULL)
        return FALSE; // Error creating overlapped event handle.

    assert(commOverlapped.hEvent);

    result = WaitCommEvent(hComm, &dwCommEvent, &commOverlapped);
    if (!dwCommEvent)
        printf("Error Setting WaitCommEvent()%d\n", GetLastError());
    else
    {
        if (dwCommEvent & EV_TXEMPTY)
        {
            printf("Chars Recveived\n");
            len = oRead.InternalHigh;
        }

    }

    CloseHandle(oRead.hEvent);
    CloseHandle(commOverlapped.hEvent);

    return len;
};

void main(int argc, char *argv[]) {

    HANDLE hPort;
    int len;
    unsigned idx = 0;
    unsigned char TXBUF[] = "T", RXBUF[2048], port[64] = "COM8";

    SetConsoleTitle(port); // To specify serial port number on open 
    hPort = CreateFile(port, GENERIC_READ | GENERIC_WRITE, 0, NULL,
        OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); 

    ConfigureSerialPort(hPort);
    if (hPort == INVALID_HANDLE_VALUE) {
        printf("Error in openning serial port\n");
        exit(0); // No point in going on if serial port didn't open
    }
    else
        printf("Serial Port Open\n");

    while (1) {

        sendpckt(hPort, 1, TXBUF);
        printf("Sending 0x%.2X\n", TXBUF[0]);

        len = recvpckt(hPort, RXBUF);
        if (len > 0)
        {
            printf("RCVD(%d): \n", len);
        }

        for (idx = 0; idx < len; idx++)
        {
            printf("%d:0x%.2X \n", idx + 1, RXBUF[idx]);
        }

        SleepEx(1000, TRUE); // How often to look for data in RX buffer
    }

    CloseHandle(hPort); // Close the serial port
}

更新:

另一种方法是使用WaitForSingleObject 而不使用SetCommMaskWaitCommEvent。以读操作为例:

int recvpckt(HANDLE hComm, unsigned char * pckt)
{
    BOOL result;
    int len = 0;
    OVERLAPPED oRead = { 0 };
    DWORD errCode;
    DWORD dwCommEvent;

    oRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (oRead.hEvent == NULL)
        return FALSE; // Error creating overlapped event handle.

    assert(oRead.hEvent);

    result = ReadFile(hComm, pckt, 2048, NULL, &oRead);
    if (result == FALSE)
    {
        errCode = GetLastError();

        if (errCode != ERROR_IO_PENDING)
            printf("nError! Setting CommMask\n");
    }

    DWORD dwWaitResult = WaitForSingleObject(oRead.hEvent, INFINITE);
    switch (dwWaitResult)
    {
    case WAIT_OBJECT_0:
        printf("Received.\n");
        break;
    case WAIT_TIMEOUT:
        printf("Timeout.\n");
        break;
    case WAIT_FAILED:
        printf("Failed.\n");
        break;
    case WAIT_ABANDONED:
        printf("Abandoned.\n");
        return FALSE;
    }

    CloseHandle(oRead.hEvent);

    return len;
};

【讨论】:

  • 您的代码充满了错误。例如只取sendpcktoWrite 的生命周期可以小于使用它的 I/O 生命周期。您在并发的 2 个 I/O 请求中使用相同的 oWrite - 再次错误,每个 I/O 请求的重叠必须是唯一的。只需关闭oWrite.hEvent - 但在这种情况下,您创建它的目的是什么?
  • 你能指出我“在并发的 2 个 I/O 请求中使用相同的 oWrite”吗?
  • sendpckt - 你将&amp;oWrite 传递给WriteFile 然后传递给WaitCommEvent 但首先 - 所以你在2个请求中混合相同的oWrite。您可能在 I/O 完成之前退出 sendpckt。结果当它完成时 - 堆栈中的任意位置将被破坏(当内核将数据写入堆栈时,oWrite )。 oWrite 里面的事件你是为了什么而创建的?你不使用它! recvpckt 中更糟糕的情况 - 你对 oRead 犯了同样的错误。您同时在 2 个 api 中使用它。 oRead.InternalHigh; 将来自 ReadFileWaitCommEvent ? :)
  • @RbMm 感谢您的好心 cmets。我更新了我的答案。
  • 更新后的操作仍然毫无意义——这不是异步 I/O。只需以同步模式打开文件并进行同步读/写
【解决方案2】:

发布基于 Rita Han 示例的完整且实用的解决方案:

/* ExAIO.c                                                         2020-02-20 */
#include <stdio.h>
#include <assert.h>
#include <windows.h>

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int ConfigureSerialPort(HANDLE hComm)
{
  int Status;
  DCB dcb = {0};
  COMMTIMEOUTS timeouts;

  dcb.DCBlength = sizeof(dcb);

  Status = GetCommTimeouts(hComm, & timeouts);
  GetCommTimeouts(hComm, & timeouts); 
  timeouts.ReadIntervalTimeout          = MAXDWORD;
  timeouts.ReadTotalTimeoutMultiplier   = 0;
  timeouts.ReadTotalTimeoutConstant     = 0;
  timeouts.WriteTotalTimeoutMultiplier  = 0;
  timeouts.WriteTotalTimeoutConstant    = 0; 
  if (!SetCommTimeouts(hComm, & timeouts)) {
    printf("Error setting timeouts on port!\n");
  }

  Status = GetCommState(hComm, & dcb);
  dcb.BaudRate          = 19200;
  dcb.Parity            = NOPARITY;
  dcb.fBinary           = TRUE;         // Binary mode; no EOF check 
  dcb.fParity           = FALSE;        // Enable parity checking 
  dcb.fOutxCtsFlow      = FALSE;        // No CTS output flow control 
  dcb.fOutxDsrFlow      = FALSE;        // No DSR output flow control 
  dcb.fDtrControl       = DTR_CONTROL_DISABLE; // DTR flow control type 
  dcb.fDsrSensitivity   = FALSE;        // DSR sensitivity 
  dcb.fTXContinueOnXoff = FALSE;        // XOFF continues Tx 
  dcb.fOutX             = FALSE;        // No XON/XOFF out flow control 
  dcb.fInX              = FALSE;        // No XON/XOFF in flow control
  dcb.fErrorChar        = FALSE;        // Disable error replacement 
  dcb.fNull             = FALSE;        // Disable null stripping 
  dcb.fRtsControl       = RTS_CONTROL_DISABLE; // RTS flow control 
  dcb.fAbortOnError     = FALSE;        // Do not abort reads/writes on error
  dcb.ByteSize          = 8;            // Number of bits/byte, 4-8 
  dcb.StopBits          = ONESTOPBIT;   // 0,1,2 = 1, 1.5, 2
  dcb.EvtChar           = 0x7E;         // Flag

  if (!SetCommState(hComm, & dcb)) {
    printf("Unable to configure serial port!\n");
  }
  return 0;
};
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int sendpckt(HANDLE hComm, unsigned length, unsigned char * pckt)
{
    BOOL result;
    DWORD dwWaitResult, errCode;
    OVERLAPPED oWrite = { 0 };

    oWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (oWrite.hEvent == NULL) return FALSE; // Error creating OL event handle.

    // Initialize the rest of the OVERLAPPED structure to zero.
    oWrite.Internal     = 0;
    oWrite.InternalHigh = 0;
    oWrite.Offset       = 0;
    oWrite.OffsetHigh   = 0;

    assert(oWrite.hEvent);

    result = WriteFile(hComm, pckt, length, NULL, &oWrite);
    if (result == FALSE) {
       errCode = GetLastError();
     if (errCode != ERROR_IO_PENDING) printf("Error! Setting CommMask\n");
    }

    dwWaitResult = WaitForSingleObject(oWrite.hEvent, INFINITE);

    switch(dwWaitResult) {
      case WAIT_OBJECT_0:
        printf("No Wait. Send complete.\n");
        break;
      case WAIT_TIMEOUT:
        printf("Wait Timeout.\n");
        break;
      case WAIT_FAILED:
        printf("Wait Failed.\n");
        break;
      case WAIT_ABANDONED:
        printf("Wait Abandoned.\n");
        break;
      default:
        break;
    }

    CloseHandle(oWrite.hEvent);
    return 0;
};
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int recvpckt(HANDLE hComm, unsigned char * pckt)
{
    BOOL result;
    DWORD dwWaitResult, errCode, BytesRead = 0;
    OVERLAPPED oRead = { 0 };

    oRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (oRead.hEvent == NULL) return FALSE; // Error creating OL event handle.

    // Initialize the rest of the OVERLAPPED structure to zero.
    oRead.Internal     = 0;
    oRead.InternalHigh = 0;
    oRead.Offset       = 0;
    oRead.OffsetHigh   = 0;

    assert(oRead.hEvent);

    result = ReadFile(hComm, pckt, 2048, &BytesRead, &oRead);
    if (result == FALSE) {
        errCode = GetLastError();
      if (errCode != ERROR_IO_PENDING) printf("nError! Setting CommMask\n");
    }

    dwWaitResult = WaitForSingleObject(oRead.hEvent, INFINITE);

    switch (dwWaitResult) {
    case WAIT_OBJECT_0:
        printf("Received: ");
        if (BytesRead > 0) {
          printf("%d byte(s) -- ", BytesRead);
        }
        else printf("Nothing\n");
        break;
    case WAIT_TIMEOUT:
        printf("RX Timeout.\n");
        break;
    case WAIT_FAILED:
        printf("RX Wait Failed: \n");
        if (BytesRead > 0) {
          printf("%d byte(s) -- ", BytesRead);
        }
        else printf("Nothing\n");
        break;
    case WAIT_ABANDONED:
        printf("RX Abandoned.\n");
        return FALSE;
    default:
        break;
    }

    CloseHandle(oRead.hEvent);
    return BytesRead;
};
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int args(int argc, char * argv[], char * port) 
{
  static int i;
  i = atoi(argv[1]);
  sprintf(port, "\\\\.\\COM%d", i);
  return i;
};
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
char *TimeNow(char * TimeString)
{
  SYSTEMTIME st;
  GetSystemTime(&st);
  sprintf(TimeString, "%.2d:%.2d:%.2d.%.3d",
    st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
  return TimeString;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void main(int argc, char *argv[]) 
{
    HANDLE hPort;
    int len;
    unsigned idx=0, portNum;
    unsigned char TXBUF[2048], RXBUF[2048], port[64], timeString[14], SendMe=0;

    if (argc > 1) portNum = args(argc, argv, port);
    else 
    {
      printf("Please identify the serial port number to open.\n");
      exit(0);
    }
    SetConsoleTitle(port); // To specify serial port number on open 

    hPort = CreateFile(port, GENERIC_READ | GENERIC_WRITE, 0, NULL,
        OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); 

    ConfigureSerialPort(hPort);
    if (hPort == INVALID_HANDLE_VALUE) {
        printf("Error in openning serial port\n");
        exit(0); // No point in going on if serial port didn't open
    }
    else
    {
        printf("Serial Port %d Open at %s\n", portNum, TimeNow(timeString));
    }

    while (1) {
        TXBUF[0] = (SendMe++)%256;
        printf("Sending 0x%.2X at %s ", TXBUF[0], TimeNow(timeString));
        sendpckt(hPort, 1, TXBUF);

        len = recvpckt(hPort, RXBUF);
        if (len > 0) {
           printf("%s RCVD[%d byte(s)]: ", TimeNow(timeString), len);
         for (idx = 0; idx < len; idx++)
         {
            printf("%d:0x%.2X ", idx+1, RXBUF[idx]);
         }
          printf("\n");
        }

        SleepEx(1000, TRUE); // How often to send data out TX buffer
    }
    printf("Error: %d", GetLastError());
    CloseHandle(hPort); // Close the serial port
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-05
    • 1970-01-01
    • 2013-06-19
    • 1970-01-01
    • 2015-07-24
    • 2012-08-28
    • 2011-11-25
    相关资源
    最近更新 更多