2016-07-13 20 views
0

Wenn ich keine Timeout-Einstellungen über GetTimeout setzen & SetTimeout, nachdem ich meinen USB-UART-Konverter an meinen Laptop angeschlossen habe, blockiert ReadFile. Sobald ich sie jedoch über SetTimeOut gesetzt habe, blockiert ReadFile nicht mehr und gibt true zurück, selbst wenn die angegebene Anzahl an Bytes nicht gelesen wurde.Wie finde ich heraus, dass die ReadFile-Funktion abgelaufen ist?

Wenn ReadFile true zurückgibt, aber der Parameter dwRead 0 sagt, weil über den seriellen Port keine Daten in meinen PC gelangten, folge daraus, dass die ReadFile-Funktion abgelaufen sein muss. Die Verwendung von GetLastError gibt jedoch 0 zurück. Wie kann ich in meinem C++ - Programm verifizieren, dass die ReadFile-Datei tatsächlich abgelaufen ist?

+0

Hat es irgendwelche Bytes übertragen? – EJP

+1

* Wenn ReadFile true zurückgibt, aber der dwRead-Parameter 0 ergibt, [...] schließe ich, dass die ReadFile-Funktion ein Zeitlimit haben muss * - für eine serielle Schnittstelle halte ich dies für eine sichere Schlussfolgerung. Wenn es irgendeinen anderen Grund gäbe, wäre der Anruf fehlgeschlagen. –

+0

OK, ich werde das in meinem Programm verwenden. Zu diesem Zeitpunkt habe ich nur den USB-UART-Konverter an meinen Laptop angeschlossen, am anderen Ende des Konverters befindet sich kein Gerät, so dass Daten eingelesen werden. – quantum231

Antwort

2

Bei der Verwendung von SetCommTimeouts() mit ReadFile(), die COMMTIMEOUTS Dokumentation sagt:

ReadIntervalTimeout
The maximum time allowed to elapse before the arrival of the next byte on the communications line, in milliseconds. If the interval between the arrival of any two bytes exceeds this amount, the ReadFile operation is completed and any buffered data is returned. A value of zero indicates that interval time-outs are not used.

Wenn ein Timeout auftritt, wird die Lese abgeschlossen ist (ReadFile() TRUE zurück), und die Anzahl von Bytes, die vor dem Timeout gepuffert worden war, verstrichen spiegelt sich in Ihrer dwRead Variable wider. Sie werden also wissen, wenn eine Zeitüberschreitung aufgetreten ist, wenn dwReadweniger als die Anzahl der Bytes ReadFile() zu lesen gelesen wurde. Wenn keine gepufferten Daten vorhanden sind, wird dwRead 0 sein. Wenn dwRead ist gleich die Anzahl der Bytes, die Sie angefordert haben, gab es keine Zeitüberschreitung seit ReadFile() beendet, wenn das letzte angeforderte Byte gelesen wurde.

+0

Sehr gut. Seltsames Design, * nicht Krieg *? Vergleichen Sie 'read()': es blockiert, bis mindestens ein Byte übertragen wurde oder das Ende des Streams oder ein Fehler aufgetreten ist, einschließlich eines Timeouts. Kein Unsinn über Timeouts * zwischen * Bytes, wodurch der Timeout-Wert sehr schwierig sinnvoll zu ermitteln ist. – EJP

+0

'read()' kann Timeouts zwischen Bytes verarbeiten, das Setzen eines Lese-Timeouts kann dazu führen, dass es mit einem 'ETIMEDOUT' Fehlercode fehlschlägt, gleiches gilt für ein' recv() '. Ich denke, ein alternativer Entwurf für 'ReadFile()' wäre es gewesen, mit einem 'ERROR_TIMEOUT' Fehlercode zu versagen. –

+0

'read()' kann nicht mit Zeitüberschreitungen zwischen Bytes umgehen, da es die ersten empfangenen Bytes vor dem Timeout zurückgegeben hat. Die Zeitüberschreitung kann nur auftreten * vor * Empfangen von Bytes. – EJP

-4

Am einfachsten und präzisesten Weg verwenden NT Api NtReadFile und überprüfen Sie den endgültigen Status der Operation. Zeitüberschreitung < => iosb.Status == STATUS_TIMEOUT. kernel32 api Readfile - verlorene Daten für diesen Status (STATUS_TIMEOUT), weil es nur für STATUS_PENDING überprüfen und 0> Status

für synchrone Arbeit können Code wie folgt verwendet werden:

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); 
    } 
} 

für asynchrone:

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(); 
    } 
} 
+0

Es gibt sicher eine Menge Möglichkeiten, die Dinge hier zu tun – quantum231

+0

@ quantum231 verwenden native API genaueste Art und Weise - einfach überprüfen Sie letzte NTSTATUS der Operation. in Ihrem Fall - dies wird STATUS_TIMEOUT sein. nicht brauchen überprüfen Anzahl der Bytes, GetLastError, etc – RbMm