简体   繁体   English

Win32 API:如何读取串行,或者如果不是数据则在超时内退出

[英]Win32 API: how to read the serial, or exit within a timeout if wasn't a data

I need a function to read data from a serial port or return if none came within a time interval. 我需要一个函数从串口读取数据,如果没有在一个时间间隔内返回,则需要返回。 Eg on GNU/Linux you could use poll() or select() + read() . 例如,在GNU / Linux上,您可以使用poll()select() + read() Anything analogous in Windows? Windows中有类似的东西吗?

Below is something I tried: it ought to work, but the function GetOverlappedResult() is either buggy or not well documented; 下面是我尝试的东西:它应该工作,但函数GetOverlappedResult()要么是错误的,要么没有很好的记录; instead of number of bytes read it reports nothing (not even zero, eg if I don't initialize the lpNumberOfBytesTransferred argument, it just contains junk) . 而不是读取的字节数,它不报告任何内容(甚至不报告为零,例如,如果我不初始化lpNumberOfBytesTransferred参数,它只包含垃圾)

// NOTE: the `file` should be opened in non-blocking mode
long ReadData(HANDLE file, char* buf, long szbuf, int msTimeout){
    OVERLAPPED ReadState = {0};
    ReadState.hEvent = CreateEvent(0, true, false, "☠");
    if (!ReadState.hEvent){
        PrintLastErr();
        return -1;
    }
    unsigned long BytesRead = 0; //number of bytes was read
    if (!ReadFile(file, buf, szbuf, &BytesRead, &ReadState)){
        // This creates a reading event. Below we wait for it to complete, and cancel on timeout
        if (GetLastError() != ERROR_IO_PENDING){
            PrintLastErr();
            return -1;
        }
        // No, I can't use WaitForSingleObject(), it exits with disregard to whether
        // reading is ongoing or no more data left (e.g. in case of a serial port).
        while(1) {
            std::this_thread::sleep_for(std::chrono::milliseconds( msTimeout ));
            unsigned long ret;
            puts("Getting result");
            if (!GetOverlappedResult(file, &ReadState, &ret, false)
                && GetLastError() != ERROR_IO_INCOMPLETE){
                PrintLastErr();
                return -1;
            }
            printf("result is %lu\n",ret);
            if(ret == BytesRead){
                return BytesRead;
            }
            BytesRead = ret;
        }
    } else { //completed immediately
        printf("Bytes read %i\n");
        assert(BytesRead <= LONG_MAX);
        return (long)BytesRead;
    }
}

It was really hard to figure; 真的很难想象; but thanks to a forum and lots of experimentation I finally found a way. 但是由于一个论坛和大量的实验,我终于找到了办法。 Here's the code: 这是代码:

/**
 * Opens a file in overlapped mode.
 * \returns HANDLE to a file, or 0 on fail
 */
HANDLE OpenFile(const char* FileName){
    HANDLE file = CreateFile( FileName,
                              GENERIC_READ | GENERIC_WRITE,
                              0, //we're greedy(or just lazy)
                              0,
                              OPEN_EXISTING,
                              FILE_FLAG_OVERLAPPED,
                              0);
    if (file == INVALID_HANDLE_VALUE){
        return 0;
    }
    if(!SetCommMask(file, EV_RXCHAR)){ // set a mask for incoming characters event.
        return 0;
    }
    return file;
}

/**
 * Waits for data to arrive
 * \param file a file opened in overlapped mode
 * \param msTimeout is a maximum time to wait
 * \returns -1 on system error, 0 on success, and 1 if time out
 */
int WaitForData(HANDLE file, unsigned long msTimeout){
    int ret;
    unsigned long Occured;//returns the type of an occured event
    OVERLAPPED FileEvent = {0};
    FileEvent.hEvent = CreateEvent(0, true, false, 0);
    do{
        if(!WaitCommEvent(file, &Occured, &FileEvent)){
            if(GetLastError() != ERROR_IO_PENDING){
                ret = -1;
                break;
            }
        }
        switch(WaitForSingleObject(FileEvent.hEvent, msTimeout)){
            case WAIT_OBJECT_0: //the requested event happened
                ret = 0; //a success
                break;
            case WAIT_TIMEOUT://time out
                ret = 1;
                break;
            default://error in WaitForSingleObject
                ret = -1;
                break;
        }
        break;
    }while(0);
    CloseHandle(FileEvent.hEvent);
    return ret;
}

/**
 * Reads data from a file
 * \param file a file opened in overlapped mode
 * \param buf a buf for data
 * \param szbuf size of buffer
 * \returns number of bytes read or -1 on fail
 */
unsigned long ReadData(HANDLE file, char* buf, unsigned long szbuf){
    int ret;
    unsigned long BytesRead = 0; //number of bytes was read
    OVERLAPPED ReadState = {0};
    do{
        ReadState.hEvent = CreateEvent(0, true, false, 0);
        if (!GetOverlappedResult(file, &ReadState, &BytesRead, false)){//get how many bytes incame
            ret = ULONG_MAX;
            break;
        }
        if (ReadFile( file,
                      buf,
                      (BytesRead<=szbuf) ? BytesRead : szbuf,
                      0,
                      &ReadState) == 0){
            ret = ULONG_MAX;
            break;
        }
        ret = BytesRead;
    }while(0);
    CloseHandle(ReadState.hEvent);
    return ret;
}

So, how does it work? 那么它是怎样工作的?

  1. Open the port with FILE_FLAG_OVERLAPPED flag 使用FILE_FLAG_OVERLAPPED标志打开端口
  2. Use SetCommMask() to set EV_RXCHAR as the only events we're interested in is incoming data. 使用SetCommMask()EV_RXCHAR设置为我们感兴趣的唯一事件是传入数据。
  3. In a cycle wait for data for a specified amount of time, read if anything came. 在一个循环中等待指定时间内的数据,读取是否有任何结果。

Also @ScottMcP-MVP's answer is wrong: there's no use to SetCommTimeouts() . 另外@ ScottMcP-MVP的答案是错误的: SetCommTimeouts() On the first sight this function and COMMTIMEOUTS looks like exactly what you'd want though, which drove me to hours of confusion. 乍一看,这个功能和COMMTIMEOUTS看起来就像你想要的那样,这让我感到困惑了几个小时。

Call SetCommTimeouts after you open the COM port. 打开COM端口后调用SetCommTimeouts。 This sets ReadFile to return after an interval if no data is received. 如果没有收到数据,这会将ReadFile设置为在间隔后返回。 Then you simply call ReadFile. 然后你只需调用ReadFile。 No overlap or event or GetOverlappedResult is needed. 不需要重叠或事件或GetOverlappedResult。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM