如何找出ReadFile函数超时?

时间:2016-07-13 23:26:13

标签: c++ winapi serial-port timeout readfile

当我没有通过GetTimeout&设置超时设置时将USB-UART转换器连接到笔记本电脑后的SetTimeout,ReadFile变为阻塞状态。但是,一旦我通过SetTimeOut设置它们,ReadFile就不会再阻塞,即使没有读取给定的字节数,它也会返回true。

当ReadFile返回true但dwRead参数显示为0,因为没有数据通过串口进入我的PC,我得出结论,ReadFile函数必须超时。但是,使用GetLastError返回0.如何在我的C ++程序中验证ReadFile实际上是否超时?

2 个答案:

答案 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();
    }
}