Winapi WaitForSingleObject()在未发生等待事件的情况下收到信号 我用Win API编写C++中的COM端口类。现在,我用连接的Rx和Tx引脚在RS232上测试功能

Winapi WaitForSingleObject()在未发生等待事件的情况下收到信号 我用Win API编写C++中的COM端口类。现在,我用连接的Rx和Tx引脚在RS232上测试功能,winapi,visual-c++,Winapi,Visual C++,我遇到了一些奇怪的问题。我使用单独的线程从COM端口读取。在线程中,我使用SetCommMask、WaitCommEvent和WaitForSingleObject等待字符到达缓冲区。但是,WaitForSingleObject倾向于在没有实际接收任何字符的情况下退出 我假设这是由于错误地使用了上述函数造成的,但后来我发现,过早退出并不是每次都会发生(第一次总是按照预期的方式工作) 在第二步中,线程进入等待状态并退出一段时间,然后继续执行ReadFile,在那里它无限期地等待,因为缓冲区是空的,

我遇到了一些奇怪的问题。我使用单独的线程从COM端口读取。在线程中,我使用
SetCommMask
WaitCommEvent
WaitForSingleObject
等待字符到达缓冲区。但是,
WaitForSingleObject
倾向于在没有实际接收任何字符的情况下退出

我假设这是由于错误地使用了上述函数造成的,但后来我发现,过早退出并不是每次都会发生(第一次总是按照预期的方式工作)

在第二步中,线程进入等待状态并退出一段时间,然后继续执行
ReadFile
,在那里它无限期地等待,因为缓冲区是空的,不发送数据,也不使用总超时

我已经被建议只使用
ReadFile
并仅处理我获取的数据,但是我使用另一个线程检查通信通道是否已断开,现在我需要区分等待数据和读取数据

调用
ClearCommError
以使用
ReadFile
检查输入缓冲区不是一个选项,因为在这种情况下
InQue
始终为0。因此,我无法判断
ReadFile
实际上是在读取还是在等待

    //following code runs in separate thread
    DWORD dwEventMask1, dwEventMask2, LastError, Status;
    OVERLAPPED Overlapped; HANDLE Serial_Port_Handle;
    std::string stringBuffer("");
    const size_t ReadBufferLength = 256;
    char tempBuffer[ReadBufferLength];

    GetCommMask(Serial_Port_Handle, &dwEventMask1);



    if (dwEventMask1)   // Before starting the thread I check the state of Input Buffer with GetCommError().
    {                   // If Buffer is not empty, CommMask is set to 0 signaling there is no need for waiting.
        Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

        //wait for comm event
        if (!WaitCommEvent(Serial_Port_Handle, &dwEventMask1, &Overlapped))
        {
            if ((LastError = GetLastError()) == ERROR_IO_PENDING)
            {
                Waiting = true; //signal bool for synchronization purposes
                if ((Status = WaitForSingleObject(Overlapped.hEvent, INFINITE)) == WAIT_OBJECT_0)
                {
                    GetCommMask(Serial_Port_Handle, &dwEventMask2);
                    Waiting = false;
                    CloseHandle(Overlapped.hEvent); 
                    // I close handle and set all members of Overlapped struct to 0
                } 

                if (dwEventMask2 !== dwEventMask1) // check if wait have not exited because of SetCommMast()
                    return;
            }
        }
    }
    do  // this loop reads from input buffer until empty
    {
        Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);//set up read overlapped operation

        if (ReadFile(Serial_Port_Handle, tempBuffer, ReadBufferLength - 1, &NoBytesRead, &Overlapped)) //start read opperation
        { //Read operation done on 1 go
            GetOverlappedResult(Serial_Port_Handle, &Overlapped, &NoBytesRead, FALSE); //get NoBytesRead

            CloseHandle(Overlapped.hEvent)
            stringBuffer.append(tempBuffer, (size_t) NoBytesRead); // save read data
        }
        else if ((LastError = GetLastError()) == ERROR_IO_PENDING) //operation not yet complete
        {
            GetOverlappedResult(Serial_Port_Handle, &Overlapped, &NoBytesRead, TRUE); // wait for completion
            stringBuffer.append(tempBuffer, (size_t)NoBytesRead);
        }
        else
        {
            CloseHandle(Overlapped.hEvent)
            return;
        }
    } while ((NoBytesRead == (ReadBufferLength - 1)) && NoBytesRead);
    // Loop runs while tempBuffer's full capacity is used.
    // I realize that since I don't use Total Timeout there is a possibility
    // of the loop getting stuck. If you can suggest other solution than using
    // Total Timeout or use GetCommError() to get input buffer state, please do so.
    return;
此代码稍微简化了(检查返回值等)

1) 你们中有人经历过这样的行为吗


2) 我在代码中使用重叠操作。在操作退出后,我总是使用
CloseHandle
并在将重叠结构用于另一个操作之前重新初始化它。这是正确的还是重置结构就足够了?

总体而言,这是错误的逻辑。例如,存在以下问题

  • CreateEvent/CloseHandle不应在每次ReadFile/WaitCommEvent时执行
  • GetCommMask/WaitCommEvent的用法也是错误的
  • 无论何种情况,ReadFile中指定的读取数据大小都是固定的
  • 它还包括对@RbMm的评论
您可能希望参考以下文章和源代码重新设计程序:


此外:
我没有注意到为WaitForSingleObject指定了文件句柄(不是事件句柄),正如@Rita Han所指出的那样。
最大的问题是

但是,最好重新设计的情况没有改变。
没有WaitCommEvent的描述,在@Rita Han的答案来源中有重叠。此外,ReadFile中的读取数据大小是固定的


另一方面:
虽然该问题的源代码中没有出现,但WaitCommEvent/WaitForSingleObject可能会生成未在SetCommMask中指定的事件

  • 当WaitCommEvent等待完成时,使用SetCommMask更改事件掩码。

    如果在进行重叠的WaitCommEvent操作时,进程试图使用SetCommMask函数更改设备句柄的事件掩码,则WaitCommEvent会立即返回。lpEvtMask参数指向的变量设置为零

  • 当WaitCommEvent等待完成时,使用相同的重叠结构多次调用WaitCommEvent。

    在单个线程上同时执行多个重叠操作时,调用线程必须为每个操作指定一个重叠结构。每个重叠结构必须为不同的手动重置事件对象指定一个句柄

    线程不应在假定事件将仅由该线程的重叠操作发出信号的情况下重用事件。事件在与正在完成的重叠操作相同的线程上发出信号。在多个线程上使用同一事件可能会导致竞态条件,在竞态条件下,对于其操作首先完成的线程,会正确地通知该事件,而对于使用该事件的其他线程,则会提前通知该事件

文档如上所述,但取决于设备驱动程序/供应商,稍后调用的WaitCommEvent以错误结束,等待完成的WaitCommEvent为带零的lpEvtMask return(如SetCommMask中所述)


对于多个重叠结构变量:
一个常见的编程诀窍是,将一个变量用于多个目的容易出现错误。
如果在异步和/或多线程中进行设计,最好为ReadFile、WriteFile和WaitCommEvent准备至少三个重叠的结构变量

关于启动ReadFile而不考虑输入缓冲区的状态:
这是关于调用固定长度为256字节的ReadFile,而不获取设备驱动程序输入缓冲区中接收数据的大小

事实上,即使所有数据都到达,如果数据小于256字节,它也会一直延迟,直到发生256字节的接收超时

例如,循环一次读取一个字节,直到出现超时错误,这意味着接收数据的结束(1字节读取超时将没有影响)。
或者,如前一篇文章中所回答的,使用ClearCommError获取存储在设备驱动程序输入缓冲区中的数据的大小,并调用ReadFile指定该大小

您正在解释的应用程序端缓冲区处理不会有问题

关于调用SetCommMask时WAIComEvent的行为:
这可能取决于您使用的设备驱动程序

但是,WaitForSingleObject倾向于退出,而不实际退出 接受任何chars

等待事件句柄instea
DWORD errCode = 0;
BOOL result = false;

HANDLE serialDeviceHdl = CreateFile(L"COM8", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (!serialDeviceHdl)
{
    errCode = GetLastError();
    cout << "Open device failed. Error code: " << errCode << endl;
    return 0;
}

OVERLAPPED overlappedForWrite = {};
overlappedForWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

DWORD writenSize = 0;
result = WriteFile(serialDeviceHdl, "hello", 5, &writenSize, &overlappedForWrite);
if (FALSE == result)
{
    errCode = GetLastError();
    if (ERROR_IO_PENDING == errCode)
    {
        cout << "Overlapped I/O operation is in progress." << endl;
    }
    else
    {
        cout << "Write to device failed. Error code: " << errCode << endl;
    }
}

DWORD returnValue = WaitForSingleObject(overlappedForWrite.hEvent, INFINITE);
if (WAIT_OBJECT_0 == returnValue)
{
    cout << "The state of the specified object is signaled." << endl;
}
else
{
    cout << "Wait for single object failed. Error code: " << returnValue << endl;
}

CHAR readBuf[5];
DWORD readSize = 0;
OVERLAPPED overlappedForRead = {};
overlappedForRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

result = ReadFile(serialDeviceHdl, readBuf, 5, &readSize, &overlappedForRead);
if (FALSE == result)
{
    errCode = GetLastError();
    if (ERROR_IO_PENDING == errCode)
    {
        cout << "Overlapped I/O operation is in progress." << endl;
    }
    else
    {
        cout << "Write to device failed. Error code: " << errCode << endl;
    }
}

returnValue = WaitForSingleObject(overlappedForRead.hEvent, INFINITE);
if (WAIT_OBJECT_0 == returnValue)
{
    cout << "The state of the specified object is signaled." << endl;
}
else
{
    cout << "Wait for single object failed. Error code: " << returnValue << endl;
}