使用Set8087CW、SetMXCSR和TWebBrowser屏蔽浮点异常

9

由于我在使用TWebBrowser和TEmbeddedWB时偶尔会收到“浮点除以零”异常,我发现我需要掩盖除以零异常Set8087CW或SetMXCSR。

问题1:最佳方法是什么:

  1. 在应用程序启动时尽早屏蔽这些异常,然后永远不再触及它们(该应用程序是多线程的)?
  2. 使用OnBeforeNavigateOnDocumentComplete事件来屏蔽/取消屏蔽异常? (文档加载后是否可能出现异常?)

问题2:屏蔽仅“除以零”,而不屏蔽其他内容的最佳“命令” - 如果应用程序是32位,则是否需要屏蔽64位异常?

我正在使用的应用程序始终可用TWebBrowser控件来显示电子邮件内容。

此外,如果有人可以澄清 - 这是微软的TWebBrowser控件的特定错误还是Delphi / C ++ Builder和Microsoft工具之间的差异?如果在Visual C ++应用程序中托管TWebBrowser并出现除以零错误会发生什么 - 它不会被转换为异常,但然后会发生什么 - Visual C ++如何处理“除以零”异常?

这有点奇怪,因为微软很长一段时间没有注意到这个问题 - 而且Embarcardero也没有注意到。因为有效地屏蔽浮点异常也会屏蔽该特定目的下的程序异常。

更新

经过一些检查,我的最终解决方案是:

SetExceptionMask(GetExceptionMask() << exZeroDivide);

默认情况下从GetExceptionMask()返回:TFPUExceptionMask() << exDenormalized << exUnderflow << exPrecision。很明显,有一些异常已经被屏蔽了——这只是将exZeroDivide添加到屏蔽的异常中。
因此,每次零除法现在都会产生浮点数中的+INF而不是异常。我可以接受这种情况——对于代码的生产版本,它将被屏蔽以避免错误;而对于调试版本,它将被取消屏蔽以便检测浮点数除以零。

try-except 有什么问题吗? - Johan
你能提供一个最小化可重现的示例吗?我在多个应用程序中使用EmbeddedWB,从未遇到过需要掩盖异常的情况... - whosrdaddy
我在TEmbeddedWB中遇到了错误,但这种情况很少发生。问题不在于该组件,而是底层的TWebBrowser。人们可以在各种情况下重现它(通过单击滚动条,仅通过加载内容等)。 - Coder12345
1
@whosrdaddy 在SO上关于这个主题大约有无数个问题。 - David Heffernan
2
当我在TWebBrowser中嵌入YouTube剪辑时,遇到了同样的问题。最终我在应用程序启动时掩盖了异常。自那以后,我的应用程序没有(已知的)影响。 - kobik
1个回答

10

假设您的应用程序代码不需要浮点异常解除掩模,那么最简单的方法是在初始化代码中的某个点屏蔽异常。

最好的做法是这样的:

SetExceptionMask(exAllArithmeticExceptions);

这将在32位目标上设置8087控制字,并在64位目标上设置MXCSR。您可以在Math单元中找到SetExceptionMask。

如果您希望在代码中取消浮点异常屏蔽,则会变得棘手。一种策略是在一个专用线程中运行浮点代码以取消异常屏蔽。这确实可以工作,但前提是您不依赖RTL函数Set8087CW和SetMXCSR。请注意,RTL中控制FP单元的所有内容都通过这些函数路由。例如,SetExceptionMask也是如此。

问题在于Set8087CW和SetMXCSR不是线程安全的。Embarcadero似乎很难相信会制作出在线程上下文中运行且未能保证线程安全性的基本程序例程。但这就是他们所做的。

消除他们留下的混乱非常困难,这样做需要进行相当多的代码修补。缺乏线程安全性是由于(mis)use全局变量Default8087CW和DefaultMXCSR。如果两个线程同时调用Set8087CW或SetMXCSR,则这些全局变量可能会导致一个线程的值泄漏到另一个线程中。

您可以使用未更改全局状态的版本替换Set8087CW和SetMXCSR,但这并不那么简单。全局状态在各个其他地方也被使用。这可能看起来有些自大,但如果您想了解更多信息,请阅读我附加到此QC报告的文档:http://qc.embarcadero.com/wc/qcmain.aspx?d=107411


你不能暂停所有线程,设置异常掩码,然后再恢复它们吗?那叫什么来着?Synchronize - Johan
@Johan 不行,你不能这样做。请阅读我提供的链接文档。 - David Heffernan
如果我使用 SetExceptionMask(TFPUExceptionMask() << exZeroDivide),那么是否会产生相同的效果,至少可以保留其他 FPU 异常(exInvalidOp、exDenormalized、exOverflow、exUnderflow、exPrecision)? 我只需要屏蔽除零操作。 (在我使用的 2010 版本中没有 exAllArithmeticExceptions)。 - Coder12345
你可以做你想做的事情。;-) 我不确定 Web 浏览器控件会抛出哪些异常。你的应用程序中是否进行了任何浮点运算? - David Heffernan
不是真的,如果我这样做了,我可以接受+INF的结果而不是异常。实际上,现在我看到默认情况下一些异常已经被屏蔽了 - 默认状态是TFPUExceptionMask() << exDenormalized << exUnderflow << exPrecision,所以这将添加exZeroDivide到列表中。我会像这样做 - SetExceptionMask(GetExceptionMask() << exZeroDivide);。谢谢你的回答。 - Coder12345
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接