当我没有通过GetTimeout&设置超时设置时将USB-UART转换器连接到笔记本电脑后的SetTimeout,ReadFile变为阻塞状态。但是,一旦我通过SetTimeOut设置它们,ReadFile就不会再阻塞,即使没有读取给定的字节数,它也会返回true。
当ReadFile返回true但dwRead参数显示为0,因为没有数据通过串口进入我的PC,我得出结论,ReadFile函数必须超时。但是,使用GetLastError返回0.如何在我的C ++程序中验证ReadFile实际上是否超时?
答案 0 :(得分:2)
将SetCommTimeouts()
与ReadFile()
一起使用时,COMMTIMEOUTS
文档说明了:
ReadIntervalTimeout
通信线路上下一个字节到达之前允许的最长时间,以毫秒为单位。 如果任意两个字节的到达间隔超过此数量,则ReadFile
操作完成,并返回任何缓冲的数据。值为零表示不使用间隔超时。
如果发生超时,则读取完成(ReadFile()
返回TRUE),超时之前缓冲的字节数反映在dwRead
变量中。因此,如果dwRead
小于您要求ReadFile()
读取的字节数,您将知道是否发生超时。如果没有缓冲数据,dwRead
将为0.如果dwRead
等于您要求的字节数,则自ReadFile()
以来没有超时在读取最后一个请求的字节时退出。
答案 1 :(得分:-4)
最简单准确的方法是使用NT api NtReadFile并检查最终的操作状态。超时< => iosb.Status == STATUS_TIMEOUT。 kernel32 api ReadFile - 此状态的丢失信息(STATUS_TIMEOUT),因为它只检查STATUS_PENDING和0>状态
对于同步工作,可以使用如下代码:
void SyncTest(POBJECT_ATTRIBUTES poa)
{
HANDLE hFile;
IO_STATUS_BLOCK iosb;
NTSTATUS status = NtOpenFile(&hFile, FILE_ALL_ACCESS, poa, &iosb, FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT);
if (0 <= status)
{
SERIAL_TIMEOUTS st = { 4000, 1, 0, 1, 0 };
status = NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_SERIAL_SET_TIMEOUTS, &st, sizeof(st), 0, 0);
DbgPrint("time %x[%x,%p]\n", status, iosb.Status, iosb.Information);
if (0 <= status)
{
UCHAR buf[256];
status = NtReadFile(hFile, 0, 0, 0, &iosb, buf, sizeof(buf), 0, 0);
DbgPrint("read %x [%x,%p]\n", status, iosb.Status, iosb.Information);
//assert(status == iosb.Status);
if (status == STATUS_TIMEOUT)
{
DbgPrint("timeout\n");
}
}
NtClose(hFile);
}
}
for asynchronous:
class __declspec(novtable) IoObject
{
friend class UserIrp;
protected:
HANDLE _hFile;
private:
LONG _dwRef;
protected:
virtual ~IoObject()
{
if (_hFile) NtClose(_hFile);
}
virtual void OnIoComplete(NTSTATUS status, ULONG_PTR Information, ULONG code, PVOID pv) = 0;
public:
NTSTATUS BindIoCompletion();
void AddRef()
{
InterlockedIncrement(&_dwRef);
}
void Release()
{
if (!InterlockedDecrement(&_dwRef)) delete this;
}
IoObject()
{
_hFile = 0;
_dwRef = 1;
}
};
class UserIrp : public IO_STATUS_BLOCK
{
friend IoObject;
IoObject* _pObj;
PVOID _pv;
LONG _dwRef;
ULONG _code;
static VOID WINAPI OnComplete(NTSTATUS Status, ULONG_PTR Information, UserIrp* This)
{
This->_pObj->OnIoComplete(Status, Information, This->_code, This->_pv);
This->Release();
}
~UserIrp()
{
_pObj->Release();
}
public:
NTSTATUS CheckStatus(NTSTATUS status)
{
if (NT_ERROR(status))
{
OnComplete(status, Information, this);
}
return status;
}
void AddRef()
{
InterlockedIncrement(&_dwRef);
}
void Release()
{
if (!InterlockedDecrement(&_dwRef)) delete this;
}
UserIrp(IoObject* pObj, ULONG code, PVOID pv) : _pObj(pObj), _dwRef(1), _code(code), _pv(pv)
{
pObj->AddRef();
}
};
NTSTATUS IoObject::BindIoCompletion()
{
return RtlSetIoCompletionCallback(_hFile, (LPOVERLAPPED_COMPLETION_ROUTINE)UserIrp::OnComplete, 0);
}
class MySerial : public IoObject
{
void OnIoComplete(NTSTATUS status, ULONG_PTR Information, ULONG code, PVOID pv)
{
DbgPrint("OnIoComplete(%x, %p, %.4s, %p)\n", status, Information, &code, pv);
switch (code)
{
case 'time':
if (0 <= status)
{
if (PUCHAR buf = new UCHAR[256])
{
if (UserIrp* Irp = new UserIrp(this, 'read', buf))
{
static LARGE_INTEGER ByteOffset;
status = Irp->CheckStatus(NtReadFile(_hFile, 0, 0, Irp, Irp, buf, 256, &ByteOffset, 0));
DbgPrint("read begin = %x\n", status);
return ;
}
delete buf;
}
}
break;
case 'read':
DbgPrint("read end(%x, %p)\n", status, Information);
if (status == STATUS_TIMEOUT)
{
DbgPrint("timeout\n");
}
delete pv;
break;
}
}
virtual ~MySerial()
{
DbgPrint("--MySerial<%p>\n", this);
}
public:
MySerial()
{
DbgPrint("++MySerial<%p>\n", this);
}
NTSTATUS Open(POBJECT_ATTRIBUTES poa)
{
IO_STATUS_BLOCK iosb;
return NtOpenFile(&_hFile, FILE_ALL_ACCESS, poa, &iosb, FILE_SHARE_VALID_FLAGS, 0);
}
NTSTATUS SetTimeouts(ULONG ms)
{
if (UserIrp* Irp = new UserIrp(this, 'time', 0))
{
SERIAL_TIMEOUTS st = { ms, 1, 0, 1, 0 };
return Irp->CheckStatus(ZwDeviceIoControlFile(_hFile, 0, 0, Irp, Irp, IOCTL_SERIAL_SET_TIMEOUTS, &st, sizeof(st), 0, 0));
}
return STATUS_INSUFFICIENT_RESOURCES;
}
};
void AsyncTest(POBJECT_ATTRIBUTES poa)
{
if (MySerial* p = new MySerial)
{
if (0 <= p->Open(poa) && 0 <= p->BindIoCompletion())
{
NTSTATUS status = p->SetTimeouts(4000);
DbgPrint("set timeout=%x\n", status);
}
p->Release();
}
}