2

我创建了一个异步处理串行端口的类。我用它与调制解调器通信。我不知道为什么,但有时,当我关闭我的应用程序时,我会出现蓝屏并且我的计算机会重新启动。我一步一步地记录了我的代码,但是当蓝屏出现并且我的计算机重新启动时,我记录数据的文件只包含空格。因此,我不知道 BSOD 的原因可能是什么。

我仔细查看了我的代码,发现了问题的几个可能原因(我正在寻找所有可能导致访问未分配内存并导致 AV 异常的原因)。

当我重新考虑异步操作的想法时,我想到了一些事情。请验证这些是否正确:

1) WaitCommEvent() 获取一个指向重叠结构的指针。因此,如果我在函数内部调用 WaitCommEvent() 然后离开函数,重叠的结构不能是局部变量,对吧?事件掩码变量和事件句柄也是,对吧?

2) ReadFile() 和 WriteFile() 也采用变量的引用或指针。因此,在重叠的读取或写入操作完成之前,所有这些变量都必须是可访问的,对吧?

3) 我只调用一次 WaitCommEvent() 并在一个循环中检查它的结果,同时做其他事情。因为我不知道如何终止异步操作(有可能吗?),所以当我销毁我的保留串口句柄的类时,我首先关闭句柄,然后在使用的重叠结构中等待事件调用 WaitCommEvent() 函数时。我这样做是为了确保异步等待通信事件的线程不会访问我的类中被破坏的任何字段。这是个好主意还是愚蠢?

try
  CloseHandle(FSerialPortHandle);
  if Assigned(FWaitCommEvent) then
    FWaitCommEvent.WaitFor(INFINITE);      
finally
  FSerialPortHandle := INVALID_HANDLE_VALUE;
  FreeAndNil(FWaitCommEvent);
end;

在我注意到所有这些之前,第一点和第二点中提到的大多数变量都是调用上述三个方法的函数的局部变量。这可能是 BSOD 的原因还是我应该在我的代码中寻找其他一些错误?

当我更正代码时,BSOD 停止发生,但这可能是巧合。你怎么想?

任何想法将不胜感激。提前致谢。


我阅读了 CancelIo() 函数文档,它指出此方法取消调用线程发出的所有 I/O 操作。如果我知道 WaitCommEvent() 是由与调用 CancelIo() 的线程不同的线程发出的,那么在调用 CancelIo() 后等待 FWaitCommEvent 是否可以?

  if Assigned(FWaitCommEvent) and CancelIo(FSerialPortHandle) then
  begin
    FWaitCommEvent.WaitFor(INFINITE);
    FreeAndNil(FWaitCommEvent);
  end;

我检查了在这种情况下会发生什么,调用这段代码的线程没有死锁,即使它没有发出 WaitCommEvent()。我在 Windows 7 上进行了测试(如果重要的话)。我可以保留代码原样还是危险?也许我误解了文档,这就是我提出问题的原因。我很抱歉问了这么多问题,但我真的需要确定这一点。

谢谢。

4

2 回答 2

5

作为标准用户运行的应用程序永远不会导致错误检查(又名 BSOD)。(并且以管理员身份运行的应用程序必须竭尽全力才能做到这一点。)您遇到了驱动程序错误或硬件故障。

默认情况下,Windows 配置为在%SystemRoot%\minidump发生错误检查时保存小型转储。您可以通过在 WinDbg 中加载 minidump 文件、将 WinDbg配置为使用 Microsoft 公共符号存储并!analyze -v在 WinDbg 中运行命令来确定有关崩溃的更多信息。至少,这应该可以确定哪个驱动程序可能有问题(尽管我猜它是您的调制解调器驱动程序)。

于 2009-07-12T21:34:38.977 回答
4
  1. 是的,您确实需要TOverlapped在重叠操作期间保持结构可用。您将GetOverlappedResult在某个时候调用,并GetOverlappedResult说它应该接收一个指向在开始重叠操作时使用的结构的指针。如果需要,可以将事件掩码和句柄存储在局部变量中;TOverlapped无论如何,您将在结构中拥有它们的副本。

  2. 是的,使用的缓冲区ReadFile必须WriteFile保持有效。他们不会制作自己的本地副本以供内部使用。even的文档是ReadFile这样说的:

    此缓冲区必须在读取操作期间保持有效。在读取操作完成之前,调用者不得使用此缓冲区。

    如果您不遵守该规则,那么您可能正在读取未保留的堆栈空间,这很容易导致各种意外行为。

  3. 要取消重叠的 I/O 操作,请使用CancelIo. TOverlapped在确定相关操作已终止之前,不要释放记录的内存,这一点很重要。对于您正在读取或写入的缓冲区也是如此。CancelIo不会立即取消操作,因此即使在您调用它之后,您的缓冲区可能仍在使用中。

于 2009-07-12T21:12:11.313 回答