串口重叠和蓝屏死机
我创建了一个异步处理串行端口的类。 我用它与调制解调器通信。 我不知道为什么,但有时,当我关闭应用程序时,我会出现蓝屏并且计算机会重新启动。 我一步步记录了我的代码,但是当出现 BSOD 并且我的计算机重新启动时,我记录数据的文件只包含空格。 因此我不知道 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 上进行了测试(如果有的话)。 我可以保留代码原样吗?或者它很危险吗? 也许我误解了文档,这就是我提出问题的原因。 我很抱歉问了这么多问题,但我确实需要确定这一点。
谢谢。
I created a class that handles serial port asynchronously. I use it to communicate with a modem. I have no idea why, but sometimes, when I close my application, I get the Blue Screen and my computer restarts. I logged my code step by step, but when the BSOD appeared, and my computer restarted, the file into which I was logging data contained only white spaces. Therefore I have no idea, what the reason of the BSOD could be.
I looked through my code carefully and I found several possible reasons of the problem (I was looking for all that could lead to accessing unallocated memory and causing AV exceptions).
When I rethought the idea of asynchronous operations, a few things came to my mind. Please verify whether these are right:
1) WaitCommEvent() takes a pointer to the overlapped structure. Therefore, if I call WaitCommEvent() inside a function and then leave the function, the overlapped structure cannot be a local variable, right? The event mask variable and event handle too, right?
2) ReadFile() and WriteFile() also take references or pointers to variables. Therefore all these variables have to be accessible until the overlapped read or write operations finish, right?
3) I call WaitCommEvent() only once and check for its result in a loop, in the mean time doing other things. Because I have no idea how to terminate asynchronous operations (is it possible?), when I destroy my class that keeps a handle to a serial port, I first close the handle, and then wait for the event in the overlapped structure that was used when calling the WaitCommEvent() function. I do this to be sure that the thread that waits asynchronously for a comm event does not access any fields of my class which is destroyed. Is it a good idea or is it stupid?
try
CloseHandle(FSerialPortHandle);
if Assigned(FWaitCommEvent) then
FWaitCommEvent.WaitFor(INFINITE);
finally
FSerialPortHandle := INVALID_HANDLE_VALUE;
FreeAndNil(FWaitCommEvent);
end;
Before I noticed all these, most of the variables mentioned in point one and two were local variables of the functions that called the three methods above. Could it be the reason of the BSOD or should I look for some other mistakes in my code?
When I corrected the code, the BSOD stopped occuring, but It might be a coincidence. How do you think?
Any ideas will be appreciated. Thanks in advance.
I read the CancelIo() function documentation and it states that this method cancells all I/O operations issued by the calling thread. Is it OK to wait for the FWaitCommEvent after calling CancelIo() if I know that WaitCommEvent() was issued by a different thread than the one that calls CancelIo()?
if Assigned(FWaitCommEvent) and CancelIo(FSerialPortHandle) then
begin
FWaitCommEvent.WaitFor(INFINITE);
FreeAndNil(FWaitCommEvent);
end;
I checked what happens in such case and the thread calling this piece of code didn't get deadlocked even though it did not issue WaitCommEvent(). I tested in on Windows 7 (if it matters). May I leave the code as is or is it dangerous? Maybe I misunderstood the documentation and this is the reason of my question. I apologize for asking so many questions, but I really need to be sure about that.
Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
以标准用户身份运行的应用程序永远不会导致错误检查(也称为 BSOD)。 (以管理员身份运行的应用程序必须竭尽全力才能做到这一点。)要么您遇到了驱动程序错误,要么您的硬件很差。
默认情况下,Windows 配置为在发生错误检查时将小型转储保存在
%SystemRoot%\minidump
中。 您可以通过加载 WinDbg< 中的小型转储文件来确定有关崩溃的更多信息/a>,将 WinDbg 配置为使用 Microsoft 公共符号存储,并在 WinDbg 中运行!analyze -v
命令。 至少,这应该确定哪个驱动程序可能有问题(尽管我猜是您的调制解调器驱动程序)。An application running as a standard user should never be able to cause a bug check (a.k.a. BSOD). (And an application running as an Administrator should have to go well out of its way to do so.) Either you ran into a driver bug or you have bad hardware.
By default, Windows is configured to save a minidump in
%SystemRoot%\minidump
whenever a bug check occurs. You may be able to determine more information about the crash by loading the minidump file in WinDbg, configuring WinDbg to use the Microsoft public symbol store, and running the!analyze -v
command in WinDbg. At the very least, this should identify what driver is probably at fault (though I would guess it's your modem driver).是的,您确实需要在重叠操作期间保持
TOverlapped
结构可用。 您将在某个时刻调用GetOverlappedResult
,并且GetOverlappedResult
表示它应该接收一个指向启动重叠操作时使用的结构的指针。 如果需要,事件掩码和句柄可以存储在局部变量中; 无论如何,您都会在TOverlapped
结构中拥有它们的副本。是的,
ReadFile
和WriteFile
使用的缓冲区必须保持有效。 他们不会制作自己的本地副本以供内部使用。ReadFile
的文档甚至是这样说的:<块引用>
该缓冲区必须在读操作期间保持有效。 在读取操作完成之前,调用者不得使用此缓冲区。
如果您不遵守该规则,那么您可能会读入未保留的堆栈空间,这很容易导致各种意外行为。
要取消重叠 I/O 操作,请使用
CancelIo< /代码>
。 在确定关联操作已终止之前,请勿释放
TOverlapped
记录的内存,这一点至关重要。 对于您正在读取或写入的缓冲区也是如此。CancelIo
不会立即取消操作,因此即使在调用它之后,您的缓冲区可能仍在使用中。Yes, you do need to keep the
TOverlapped
structure available for the duration of the overlapped operation. You're going to callGetOverlappedResult
at some point, andGetOverlappedResult
says it should receive a pointer to a structure that was used when starting the overlapped operation. The event mask and handle can be stored in local variables if you want; you're going to have a copy of them in theTOverlapped
structure anyway.Yes, the buffers that
ReadFile
andWriteFile
use must remain valid. They do not make their own local copies to use internally. The documentation forReadFile
even says so:If you weren't obeying that rule, then you were likely reading into unreserved stack space, which could easily cause all sorts of unexpected behavior.
To cancel an overlapped I/O operation, use
CancelIo
. It's essential that you not free the memory of yourTOverlapped
record until you're sure the associated operation has terminated. Likewise for the buffer you're reading or writing.CancelIo
does not cancel the operation immediately, so your buffers might still be in use even after you call it.