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

Posted

技术标签:

【中文标题】使用 Set8087CW、SetMXCSR 和 TWebBrowser 屏蔽浮点异常【英文标题】:Masking floating point exceptions with Set8087CW, SetMXCSR and TWebBrowser 【发布时间】:2013-10-11 19:42:31 【问题描述】:

由于我在使用 TWebBrowser 和 TEmbeddedWB 时不时收到“浮点除零”异常,我发现我需要屏蔽除零异常 Set8087CW 或 SetMXCSR。

Q1:最好的方法是什么:

    在应用程序启动的早期屏蔽此类异常并且不再接触它们(应用程序是多线程的)? 使用OnBeforeNavigateOnDocumentComplete 事件来屏蔽/取消屏蔽异常? (文档加载后是否有可能发生异常?)

Q2:什么是仅屏蔽“除以零”而不屏蔽其他任何内容的最佳“命令” - 如果应用程序是 32 位,是否也需要屏蔽 64 位异常?

我正在使用它的应用程序有 TWebBrowser 控件可用于显示电子邮件内容。

另外,如果有人能澄清一下 - 这是 Microsoft 的 TWebBrowser 控件的特定错误,还是 Delphi/C++ Builder 和 Microsoft 工具之间的区别?如果我将 TWebBrowser 托管在 Visual C++ 应用程序中,如果出现除零错误会发生什么 - 它不会被转换为异常,但接下来会发生什么 - Visual C++ 将如何处理“除零”异常?

微软这么长时间都没有注意到这个问题有点奇怪——Embarcardero 也没有注意到这个问题也很奇怪。因为屏蔽浮点异常有效地也屏蔽了您自己的程序异常用于该特定目的。

更新

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

SetExceptionMask(GetExceptionMask() << exZeroDivide);

GetExceptionMask() 的默认状态返回:TFPUExceptionMask() &lt;&lt; exDenormalized &lt;&lt; exUnderflow &lt;&lt; exPrecision。很明显,一些异常已经被屏蔽了——这只是将exZeroDivide 添加到被屏蔽的异常中。

因此,现在每个除以零的结果都是 +INF 浮点而不是异常。我可以忍受 - 对于代码的生产版本,它会被屏蔽以避免错误,而对于调试版本,它将被取消屏蔽以检测浮点除以零。

【问题讨论】:

try-except 有什么问题? 你能提供一个SSCE吗?我在各种应用程序中使用 EmbeddedWB,但我从未遇到过屏蔽异常的需要...... 我在TEmbeddedWB 中遇到了错误,而且很少发生。问题不在于该组件,而在于底层TWebBrowser。人们可以在各种情况下重现它(通过单击滚动条,仅通过加载内容等)。 @whosrdaddy 在 SO 上关于这个主题有很多问题 embedding YouTube clips in the TWebBrowser 时我遇到了同样的问题。我最终掩盖了应用程序启动时的异常。从那以后,对我的申请没有(已知的)影响。 【参考方案1】:

假设您不需要在应用程序代码中取消屏蔽浮点异常,那么最简单的做法就是在初始化代码中的某个位置屏蔽异常。

最好的方法是这样:

SetExceptionMask(exAllArithmeticExceptions);

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

如果您希望在代码中不屏蔽浮点异常,那么它会变得很棘手。一种策略是在取消屏蔽异常的专用线程中运行浮点代码。这当然可以,但如果您依赖 RTL 函数 Set8087CWSetMXCSR,则不行。请注意,RTL 中控制 FP 单元的所有内容都通过这些函数进行路由。例如SetExceptionMask 会。

问题是Set8087CWSetMXCSR 不是线程安全的。似乎很难相信 Embarcadero 会如此无能,以至于无法生成在线程上下文上操作但又不能实现线程安全的基本例程。但这就是他们所做的。

要消除他们留下的烂摊子非常困难,而且要做到这一点需要大量的代码修补。线程安全性的缺失归结为(误)使用了全局变量Default8087CWDefaultMXCSR。如果两个线程同时调用Set8087CWSetMXCSR,那么这些全局变量可能具有将值从一个线程泄漏到另一个线程的效果。

您可以将Set8087CWSetMXCSR 替换为不改变全局状态的版本,但遗憾的是没那么简单。全局状态用于其他各种地方。这可能看起来不谦虚,但如果您想了解更多有关此问题的信息,请阅读我附在此 QC 报告中的文档:http://qc.embarcadero.com/wc/qcmain.aspx?d=107411

【讨论】:

你不能暂停所有线程,设置异常掩码,然后再次恢复它们。又是什么电话。 Synchronize? @Johan 不,你不能那样做。阅读我的链接文档。 如果我使用 SetExceptionMask(TFPUExceptionMask() &lt;&lt; exZeroDivide); 来至少保留其他 FPU 异常(exInvalidOp、exDenormalized、exOverflow、exUnderflow、exPrecision),效果是否相同?我只需要用零掩盖除法。 (我使用的 2010 版本中没有 exAllArithmeticExceptions)。 你可以为所欲为。 ;-) 我不确定 Web 浏览器控件究竟会抛出哪些异常。你在你的应用中做任何浮点运算吗? 不是真的,如果我这样做,我可以忍受 +INF 结果而不是异常。实际上,现在我看到默认情况下一些异常已经被屏蔽了——默认状态是TFPUExceptionMask() &lt;&lt; exDenormalized &lt;&lt; exUnderflow &lt;&lt; exPrecision,所以这会将exZeroDivide 添加到列表中。我会这样做 - SetExceptionMask(GetExceptionMask() &lt;&lt; exZeroDivide);。谢谢你的回答。

以上是关于使用 Set8087CW、SetMXCSR 和 TWebBrowser 屏蔽浮点异常的主要内容,如果未能解决你的问题,请参考以下文章

NullInjectorError:没有 t 的提供者! NullInjectorError: StaticInjectorError(ba)[Cw -> t]:

使用github出了些问题?fatal: unable to access;Failed connect to github.com:8087;

npm err ! Error: connect ECONNREFUSED 127.0.0.1:8087

使用github出了些问题?fatal: unable to access;Failed connect to github.com:8087; No error

华为ACL综合应用详解

[java.net.UnknownHostException: DELL-CW: DELL-CW]的错误