Delphi 重叠I/O操作期间的串行端口和处理错误

Delphi 重叠I/O操作期间的串行端口和处理错误,delphi,serial-port,overlapped-io,Delphi,Serial Port,Overlapped Io,我最近一直在做串行通信,所以我准备了一个类作为所有负责读、写等的Windows API函数的简单接口。这个类中的所有I/O操作都是异步处理的 在开始我的问题之前,让我向您展示如何从串行端口写入和读取数据(这只是读取函数,因为写入函数的结构完全相同,因此没有必要同时显示这两个函数) 函数TSerialPort.Read(var pBuffer;const lBufferSize:Cardinal):Cardinal; 变量 lOverlapped:重叠; 红衣主教; lEvent:TEvent;

我最近一直在做串行通信,所以我准备了一个类作为所有负责读、写等的Windows API函数的简单接口。这个类中的所有I/O操作都是异步处理的

在开始我的问题之前,让我向您展示如何从串行端口写入和读取数据(这只是读取函数,因为写入函数的结构完全相同,因此没有必要同时显示这两个函数)

函数TSerialPort.Read(var pBuffer;const lBufferSize:Cardinal):Cardinal; 变量 lOverlapped:重叠; 红衣主教; lEvent:TEvent; 开始 lEvent:=TEvent.Create(nil,True,False,”); 尝试 FillChar(lOverlapped,SizeOf(lOverlapped),0; lOverlapped.hEvent:=lEvent.Handle; 如果不是ReadFile(fserialperhandle、pBuffer、lBufferSize、Result、@lOverlapped),那么 开始 lLastError:=GetLastError; 如果(lLastError ERROR\u IO\u PENDING)和(lLastError ERROR\u SUCCESS),则 引发异常。创建(SysErrorMessage(lLastError)); case lEvent.WaitFor(无限)of 警告: 如果未获得重叠结果(fserialPerHandle、lOverlapped、Result、False),则 引发异常。创建(SysErrorMessage(GetLastError)); 雷罗: 开始 lLastError:=lEvent.LastError; //这是对Windows.CancelIo(fserialPerHandle)的调用; 如果Self.CancelIO()那么 等待(无限); 引发异常。创建(SysErrorMessage(lLastError)); 结束; 结束; 结束; 最后 FreeAndNil(lEvent); 结束; 结束; 在您问我为什么在这个函数等待读取操作完成时打开串行端口进行重叠操作之前,我先解释一下-只有这样打开串行端口时,我才能指定WaitCommEvent()方法等待事件的时间。如果我为非重叠操作打开端口,WaitCommEvent()将一直阻塞,直到串行端口上出现事件为止,该事件不可能总是发生,从而导致调用线程永远阻塞

不过,让我们集中讨论上面的Read()函数

1) 首先,我无时间限制地等待事件的设置。是否有可能因为某种原因导致当前线程永远阻塞?我不知道是否可以100%确定事件迟早会由异步执行读取操作的线程设置。我知道,当串行端口的读取超时全部设置为零时,读取操作将在读取给定的字节数之前不会完成,但我知道这是一种行为。我的问题涉及一些意外情况,这些情况会导致事件永远不会被设置,WaitFor()方法永远等待—是否可能发生这种情况

2) WaitFor()可能返回wrError,通知在等待操作期间发生了一些错误(但这与重叠读取操作根本没有关系,对吗?)。因此,我认为我不应该再等待读取操作完成,因为事件句柄可能不再可用,对吗? 因此,我调用CancelIO()方法来取消读取操作,等待线程异步执行取消的读取来设置事件,然后引发异常。我等待该线程取消读取,因为如果我立即离开Read()方法(不取消I/O),我会导致该线程将其数据(重叠记录数据)写入不再有效的局部变量,对吗? 另一方面,在引发异常之前,是否存在由于WaitFor(无限)调用而导致当前线程永久阻塞的危险

如果您能告诉我上述陈述是否属实,并对其进行评论,我将不胜感激


非常感谢您。

出于好奇:为什么不使用现有的串行组件

我用它来接收GPS信息,但还有很多其他的免费提供:

其中大多数允许您在更高的级别上进行串行通信,为您抽象出所有较低级别的IO和线程内容


这样,您只需编写一个
onreceive
处理程序来接收,然后调用
send()
来发送内容。

出于好奇:为什么不使用现有的串行组件呢

我用它来接收GPS信息,但还有很多其他的免费提供:

其中大多数允许您在更高的级别上进行串行通信,为您抽象出所有较低级别的IO和线程内容


这样,您只需编写一个
onreceive
处理程序来接收,然后调用
send()
来发送内容。

谢谢您的建议。你可能是对的,但我还是想知道我问题的答案。这是我的好奇心,或者更确切地说是想扩大我在这个话题上的知识面。谢谢你的建议。你可能是对的,但我还是想知道我问题的答案。这是我的好奇心,或者更确切地说是我想扩大我在这个话题上的知识面。
function TSerialPort.Read(var pBuffer; const lBufferSize: Cardinal): Cardinal;
var
  lOverlapped: OVERLAPPED;
  lLastError: Cardinal;
  lEvent: TEvent;
begin
  lEvent := TEvent.Create(nil, True, False, '');
  try
    FillChar(lOverlapped, SizeOf(lOverlapped), 0);
    lOverlapped.hEvent := lEvent.Handle;

    if not ReadFile(FSerialPortHandle, pBuffer, lBufferSize, Result, @lOverlapped) then
    begin
      lLastError := GetLastError;
      if (lLastError <> ERROR_IO_PENDING) and (lLastError <> ERROR_SUCCESS) then
        raise Exception.Create(SysErrorMessage(lLastError));

      case lEvent.WaitFor(INFINITE) of
        wrSignaled:
          if not GetOverlappedResult(FSerialPortHandle, lOverlapped, Result, False) then
            raise Exception.Create(SysErrorMessage(GetLastError));

        wrError:
          begin
            lLastError := lEvent.LastError;
            //this is a call to Windows.CancelIo(FSerialPortHandle);
            if Self.CancelIO() then
              lEvent.WaitFor(INFINITE);
            raise Exception.Create(SysErrorMessage(lLastError));
          end;
      end;
    end;
  finally
    FreeAndNil(lEvent);
  end;
end;