我需要一个从串行端口读取数据的函数,或者如果没有在某个时间间隔内返回数据,则需要返回一个函数。例如。在GNU / Linux上,您可以使用poll()
或select()
+ read()
。 Windows有什么类似的东西吗?
下面是我尝试过的方法:它应该可以工作,但是函数GetOverlappedResult()是错误的或没有充分记录的;而不是读取的字节数,它什么也不报告(甚至不为零,例如,如果我不初始化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;
}
}
最佳答案
真的很难估计。但由于有了一个论坛和大量的实验,我终于找到了一种方法。这是代码:
/**
* 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;
}
那么它是怎样工作的?
FILE_FLAG_OVERLAPPED
标志SetCommMask()
将EV_RXCHAR
设置为传入数据是我们唯一感兴趣的事件。 @ ScottMcP-MVP的答案也是错误的:
SetCommTimeouts()
没有用。乍一看,这个函数和COMMTIMEOUTS
看起来确实像您想要的,这使我感到困惑。 ☺关于c++ - Win32 API:如何读取序列,如果不是数据,则在超时后退出,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25364525/