我创建了一个异步处理串口的类。我用它来与调制解调器通信。我不知道为什么,但有时,当我关闭我的应用程序时,我得到蓝屏并重新启动计算机。我一步一步地记录了我的代码,但是当BSOD出现并且我的计算机重新启动时,我记录数据的文件只包含空格。因此我不知道,BSOD的原因是什么。
我仔细查看了我的代码,发现了问题的几个可能原因(我正在寻找所有可能导致访问未分配内存并导致AV异常的原因。)
当我重新考虑异步操作的想法时,我想到了一些事情。请验证这些是否正确:
1)WaitCommEvent()获取指向重叠结构的指针。因此,如果我在函数内部调用WaitCommEvent()然后离开函数,重叠的结构不能是局部变量,对吧?事件掩码变量和事件句柄也是,对吗?
2)ReadFile()和WriteFile()也接受变量的引用或指针。因此,在重叠的读或写操作完成之前,所有这些变量必须是可访问的,对吗?
3)我只调用一次WaitCommEvent()并在循环中检查其结果,同时做其他事情。因为我不知道如何终止异步操作(是否可能?),当我销毁保持串口句柄的类时,我首先关闭句柄,然后在使用的重叠结构中等待事件调用WaitCommEvent()函数时。我这样做是为了确保为comm事件异步等待的线程不会访问被破坏的类的任何字段。这是一个好主意还是愚蠢的?
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上测试过(如果重要的话)。我可以保留代码,还是危险?也许我误解了文档,这就是我的问题的原因。我为提出这么多问题而道歉,但我真的需要确定这一点。
感谢。
答案 0 :(得分:5)
作为标准用户运行的应用程序永远不能导致错误检查(a.k.a.BSOD)。 (并且以管理员身份运行的应用程序必须完全不受此限制。)要么遇到驱动程序错误,要么硬件坏了。
默认情况下,Windows配置为在发生错误检查时在%SystemRoot%\minidump
中保存小型转储。您可以通过在WinDbg中加载minidump文件,配置WinDbg以使用Microsoft公共符号存储,以及在WinDbg中运行!analyze -v
命令来确定有关崩溃的更多信息。至少,这应该确定哪个驱动程序可能有故障(虽然我猜它是你的调制解调器驱动程序)。
答案 1 :(得分:4)
是的,您需要在重叠操作期间保持TOverlapped
结构可用。您将在某个时刻调用GetOverlappedResult
,GetOverlappedResult
表示它应该接收指向启动重叠操作时使用的结构的指针。如果需要,事件掩码和句柄可以存储在局部变量中;无论如何,你将在TOverlapped
结构中获得它们的副本。
是的,ReadFile
和WriteFile
使用的缓冲区必须保持有效。他们不会在内部使用自己的本地副本。 ReadFile
的文档甚至这样说:
此缓冲区必须在读取操作期间保持有效。在读取操作完成之前,调用者不得使用此缓冲区。
如果您不遵守该规则,那么您可能会读入无保留的堆栈空间,这很容易导致各种意外行为。
要取消重叠的I / O操作,请使用CancelIo
。在您确定相关操作已终止之前,您必须不释放TOverlapped
记录的内存。同样,对于您正在阅读或写作的缓冲区。 CancelIo
不会立即取消操作,因此即使在您调用缓冲区后,您的缓冲区仍可能正在使用。