Windows中C语言中的异步串行端口通信

asynchronous serial port communication in windows in c

当我尝试运行对串行端口进行一些基本写入的c文件时,出现错误。我尝试异步运行它,因为写入有时需要很长时间才能传输。我的原始版本使它与WriteFile()命令同步运行,效果很好。我是使用OVERLAPPED的新手,希望能对此有所感激和投入。

我得到的错误是:

1
2
3
4
Debug Assertion Failed!
<path to dbgheap.c>
Line: 1317
Expression: _CrtIsValidHeapPointer(pUserData)

当第二个写函数被调用时。

在主要方面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
    {
        //initialized port (with overlapped), DBC, and timeouts

        result = write_port(outPortHandle, 128);
        result = write_port(outPortHandle, 131);
    }




static void CALLBACK write_compl(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped) {
        //write completed. check for errors? if so throw an exception maybe?
        printf("write completed--and made it to callback function\
"
);
    }


int write_port(HANDLE hComm,BYTE* lpBuf) {

   OVERLAPPED osWrite = {0};

   // Create this write operation's OVERLAPPED structure's hEvent.
   osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
   if (osWrite.hEvent == NULL)
      // error creating overlapped event handle
      return 0;

   // Issue write.
   if (!WriteFileEx(hComm, &lpBuf, 1, &osWrite, &write_compl )) {
      if (GetLastError() != ERROR_IO_PENDING) {
         // WriteFile failed, but isn't delayed. Report error and abort.
          printf("last error: %ld",GetLastError());
          return 0; //failed, return false;
      }
      else {
         // Write is pending.
         WaitForSingleObjectEx(osWrite.hEvent, 50, TRUE);   //50 ms timeout

        return -1; //pending
      }
   }
   else {
        return 1; //finished
   }
}

抱歉,那不是完整的代码。我也使用了一组BYTE,而不是常量。但是system(" pause")导致我的调试断言失败错误,并且仔细查看我的代码后,当WriteFileEx()成功时,它从未为重叠结构中的事件设置警报/超时,因此回调函数将永远不会被调用。我解决了这些问题。

我只需要帮助处理/访问在调用ReadFileEx()函数时分配的结构中的单个BYTE(用于存储读取的BYTE,以便可以对其进行处理)。我需要知道如何使用偏移量访问BYTE存储并使重叠的结构为null。将重叠结构设为null就像将其中的句柄设置为INVALID_HANDLE_VALUE一样简单吗?


我认为您有几个问题:

您正在传递一个整数作为指针(您的编译器应对此发出警告,或者最好拒绝编译代码):

result = write_port(outPortHandle, 128);

将此与write_port的定义进行比较:

int write_port(HANDLE hComm,BYTE* lpBuf) {

上面的陈述不符。稍后,您通过获取BYTE *->"&lpBuf"的地址,将指向lpBuf的指针传递给WriteFileEx函数。这不会导致您认为的结果。

即使已解决此问题,只要写入成功排队但仍无法在50毫秒的超时时间内完成,您仍然会遇到潜在的生命周期问题。

使用重叠的I / O时,需要确保读/写缓冲区和重叠的结构保持有效,直到I / O完成,取消或关联的设备关闭为止。在上面的代码中,您使用指向OVERLAPPED结构的指针,该结构位于对WriteFileEx的调用中的堆栈中。如果WriteFileEx在50毫秒内未完成,则挂起的I / O将引用不存在的OVERLAPPED结构,并且(希望)发生访问冲突(或更糟糕的是,在应用程序中的某个位置静默损坏了堆栈数据)。

处理这些生命周期问题(如果性能不是大问题)的规范方法是使用自定义结构,该结构包括OVERLAPPED结构和一些用于读取/写入数据的存储。发布写操作时分配结构,并从I / O完成例程中取消分配结构。将包含的OVERLAPPED结构的地址传递给WriteFileEx,并使用例如offsetof以从完成例程中的OVERLAPPED地址获取自定义结构的地址。

还要注意,WriteFileEx实际上并不使用hEvent成员IIRC。

编辑:添加了代码示例,请注意:

  • 我实际上尚未尝试编译代码,代码可能存在拼写错误或其他问题。
  • 这不是发送数据的最有效方法(为发送的每个字节分配/取消分配内存块)。不过,它应该易于改进。
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
        #include <stddef.h>
        #include
        #include <windows.h>

        // ...
        typedef struct _MYOVERLAPPED
        {
            OVERLAPPED ol;
            BYTE buffer;
        } MYOVERLAPPED, *LPMYOVERLAPPED;
        // ...

        static void CALLBACK write_compl(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
        {
            if (NULL == lpOverlapped)
            {
                assert(!"Should never happen");
                return;
            }

            LPBYTE pOlAsBytes = (LPBYTE)lpOverlapped;
            LPBYTE pMyOlAsBytes = pOlAsBytes - offsetof(MYOVERLAPPED, ol);
            LPMYOVERLAPPED pMyOl = (LPMYOVERLAPPED)pOlAsBytes;

            if ((ERROR_SUCCESS == dwErrorCode) &&
                (sizeof(BYTE) == dwNumberOfBytesTransfered))
            {
                printf("written %uc\
    "
    , pMyOl->buffer);
            }
            else
            {
                // handle error
            }

            free(pMyOl);
        }


        int write_port(HANDLE hComm, BYTE byte) {

           LPMYOVERLAPPED pMyOl = (LPMYOVERLAPPED)malloc(sizeof(MYOVERLAPPED));

           ZeroMemory(pMyOl, sizeof(MYOVERLAPPED));
           pMyOl->buffer = byte;

           // Issue write.
           if (!WriteFileEx(hComm, &pMyOl->buffer, sizeof(BYTE), pMyOl, &write_compl )) {
              if (GetLastError() != ERROR_IO_PENDING) {
                 // WriteFile failed, but isn't delayed. Report error and abort.
                  free(pMyOl);
                  printf("last error: %ld",GetLastError());
                  return 0; //failed, return false;
              }
              else {
                return -1; //pending
              }
           }
           else {
                free(pMyOl);
                return 1; //finished
           }
        }


    1
    2
        result = write_port(outPortHandle, 128);
        result = write_port(outPortHandle, 131);

    lpBuf参数必须是指向缓冲区的指针,而不是常量。

    例如

    1
    2
    3
    4
    5
    char buffer;
    buffer = 128;
    result = write_port(outPortHandle, &buffer);
    buffer = 131;
    result = write_port(outPortHandle, &buffer);

    您真正想要做的是还传递缓冲区长度。

    例如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        char buffer[]  = { 128, 131 };
        result = write_port(outPortHandle, &buffer, sizeof(buffer));

    int write_port(HANDLE hComm,BYTE* lpBuf, size_t length) {

       ...

       // Issue write.
       if (!WriteFileEx(hComm, &lpBuf, length, &osWrite, &write_compl )) {
       ...