8

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

Q1:执行此操作的最佳方法是什么:

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

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

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

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

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

更新

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

SetExceptionMask(GetExceptionMask() << exZeroDivide);

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

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

4

1 回答 1

9

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

最好的方法是这样:

SetExceptionMask(exAllArithmeticExceptions);

这将在 32 位目标上设置 8087 控制字,在 64 位目标上设置 MXCSR。你会发现SetExceptionMaskMath单位。

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

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

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

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

于 2013-10-04T18:14:44.567 回答