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

发布于 2024-08-02 09:33:00 字数 2123 浏览 8 评论 0原文

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

在回答我的问题之前,让我向您展示一下如何从串口写入和读取数据(这只是读取功能,因为写入的结构完全相同,所以没有必要同时介绍它们) 。

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;

在你问我为什么在这个函数等待读取操作完成时打开串行端口进行重叠操作之前,这是我的解释 - 只有当以这种方式打开串行端口时,我才能指定 WaitCommEvent() 方法等待事件的时间。如果我打开非重叠操作的端口,WaitCommEvent() 将阻塞,直到串行端口上出现事件,而该事件并不总是发生,导致调用线程永远阻塞。

尽管如此,让我们集中讨论上面的 Read() 函数。

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

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

如果您告诉我上述陈述是否属实并发表评论,我将不胜感激。

预先非常感谢您。

I've been doing serial communications lately so I prepared a class being a simple interface to all those Windows API functions responsible for reading, writing, etc. All I/O operations inside this class are handled asynchronously.

Before I go to my question, let me show you how I write and read data from serial port (this is only the reading function, because the structure of the writing one is exactly the same so there is no point in presenting both of them).

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;

Before you ask me why I open serial port for overlapped operations while this function waits for the read operation to finish, here's my explanation - only when opening serial port this way can I specify the time WaitCommEvent() method waits for events. If I opened the port for non-overlapped operations, WaitCommEvent() would block until an event appears on serial port which could not always happen causing the calling thread to block forever.

Nevertheless, let us concentrate on the above Read() function.

1) First of all, I wait without any time limit for the event to be set. Is there a possibility that the current thread would block forever for some reason because of that? I do not know whether I can be 100% sure that the event will sooner or later be set by the thread performing the read operation asynchronously. I know that when serial port's read timeouts are all set to zero, the read operation will not finish until the given number of bytes is read, but this is a behaviour I'm aware of. My question concerns unexpected situations that would cause the event never to be set and the WaitFor() method to wait forever - is it likely to happen?

2) WaitFor() may return wrError which informs that some error occured during the wait operation (but this is not connected with the overlapped read operation at all, right?). Therefore I think I should no longer wait for the read operation to finish then, for the event handle might no longer be usable, right?
So I call the CancelIO() method to cancel the read operation, wait for the event to be set by the thread asynchronously performing the cancelled reading and only then raise exception. I wait for the reading to be cancelled by that thread because If I left my Read() method immediately (without cancelling I/O), I would cause that thread to write its data (the overlapped record data) to the local variables that would be no longer valid then, right?
On the other hand, is there a danger that the current thread would be blocked forever because of the WaitFor(INFINITE) call before raising exception?

I will be grateful if you tell me if the above statements are true or not and comment on them, please.

Thank you very much in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

鲜血染红嫁衣 2024-08-09 09:33:00

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

我使用 TurboPower Async 来接收 GPS 消息,但还有很多其他免费可用的:http://www.efg2.com/Lab/Library/Delphi/IO/PortIO .htm

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

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

Just out of curiousity: why don't you use an existing serial component?

I use TurboPower Async for receiving GPS messages, but there are plenty of others freely available: http://www.efg2.com/Lab/Library/Delphi/IO/PortIO.htm

Most of those allow you to do serial communication at a much higher level, abstracting away all the lower level IO and thread-stuff for you.

That way you only have to write an onreceive handler to receive, and call send() to send stuff.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文