10

引言——冗长而乏味的部分

(问题在最后)

我对不断更改 FPU 控制字的第三方 COM 组件感到非常头疼。

我的开发环境是Windows和Visual C++ 2008。正常的FPU控制字指定在各种情况下不应该抛出异常。我已经通过查看中_CW_DEFAULT找到的宏float.h以及在启动时查看调试器中的控制字来验证这一点。

每次我调用 COM 对象时,控制字都会在返回时被修改。这很容易防御。我只是重置了控制字,一切都很好。问题是当 COM 组件开始调用我的事件接收器时。我可以通过在收到事件调用后立即重置控制字来保护我的代码,但是一旦我从事件调用返回,我就无法做任何事情。

我没有这个COM组件的源代码,但我正在与作者联系。我从他那里得到的回应是“嗯?”。我认为他根本不知道我在说什么,所以我担心我必须自己做点什么。我相信他的运行时(我认为它是 Delphi 或 Borland C++,因为 DLL 充满了符号名称,都以大写 T 开头),或者他正在使用的其他第三方代码,这导致了问题。我不认为他的代码明确修改了 FPU 控制字。

那么,我能做些什么呢?从业务的角度来看,必须使用这个第三方组件。从技术角度来看,我可以放弃它,自己实现通信协议。但是,这将非常昂贵,因为该协议涉及处理信用卡交易。我们不想承担责任。

我迫切需要一个 hack-around,或者一些关于 Borland 产品中 FPU 设置的有用信息,我可以将它们传递给组件的作者。

问题

有什么可以做的吗?我不认为组件作者有能力修复它(从他相当无能的回答来看)。

我一直在玩弄安装我自己的异常处理程序的想法,我只是在处理程序中重置控制字,并告诉 Windows 继续执行。我尝试使用 安装处理程序SetUnhandledExceptionFilter(),但由于某种原因,没有捕获到异常。

  1. 为什么我没有捕捉到异常?
  2. 如果我成功捕获 FPU 异常,重置 FPU 控制字,然后让执行继续,因为什么都没发生——那么所有的赌注都没有了吗?

更新

I would like to thank everyone for their suggestions. I have sent the author instructions on what he can do to make life easier for not just me, but many other clients of his code. I suggested to him that he should sample the FPU control word at DllMain(DLL_PROCESS_ATTACH), and save the control word for later, so that he can reset FPU CW before calling my event handlers, and returning from my calls.

For now, I have a hack-around if anyone is interested. The hack-around is potentially a bad one, because I don't know what it'll do to his code. I have received confirmation earlier that he does not use any floating point numbers in his code, so this should be safe, barring some third party code he uses, that relies on FPU exceptions.

The two modifications I have made to my app:

  1. Wrap my message pump
  2. 安装窗口挂钩 ( WH_CALLWNDPROC) 以捕获绕过消息泵的角落案例

在这两种情况下,我都会检查 FPU CW 是否发生了变化。如果有,我将其重置为_CW_DEFAULT.

4

2 回答 2

6

我认为您对组件是在 Embarcadero 产品中编写的诊断很可能是正确的。Delphi 的运行时库确实启用了浮点异常,对于 C++ Builder 也是如此。

Embarcaderos 工具的优点之一是浮点错误被转换为语言异常,这使得数字编码更容易。这对你来说几乎没有什么安慰!

这整个区域是一个巨大的 PITA。关于 FP 控制字没有任何规则。这是一个完全免费的。

我不相信捕获未处理的异常不会完成工作,因为 MS C++ 运行时可能已经捕获了这些异常,但我不是该领域的专家,我可能错了。

我相信您唯一现实的解决方案是在代码执行到达时将 FPU 设置为您想要的,并在执行离开代码时恢复它。我对 COM 事件接收器知之甚少,无法理解为什么它们会成为这样做的障碍。

我的产品包括一个在 Delphi 中实现的 DLL,我遇到了相反的问题。大多数调用的客户端都有一个禁用异常的 FPU 控制字。我们采用的策略是在进入时记住8087CW,在执行代码之前将其设置为标准的Delphi CW,然后在退出点恢复。我们也通过在进行回调之前恢复调用者的 8087CW 来处理回调。这是一个普通的 DLL 而不是 COM 对象,因此它可能更简单一些。

如果您决定尝试让 COM 供应商修改他们的代码,那么他们需要调用该Set8087CW()函数。

但是,由于游戏没有规则,我相信 COM 对象供应商拒绝更改他们的代码并将责任推给您是有道理的。

抱歉,如果这不是 100% 确凿的答案,但我无法将所有这些想法都纳入评论!

于 2011-08-03T21:59:34.957 回答
6

尽管 FP 控制字是每个线程的,但在创建新线程时会调用 dllmain 函数,但我认为您无法避免这种短时间进入新进程。

我建议您分拆一个新进程来运行 COM,并使用您最喜欢的进程间通信方法(例如 windows 消息、进程外 COM、命名管道、套接字等)与该进程聊天。通过这种方式,COM 服务器可以自由地进行各种破坏(包括自身崩溃),而不会使您的主机进程停机。

另一个想法是编写一个 DLL,其唯一目的是在其 DllMain 中重置 FPU,并在有问题的 DLL 之后立即加载它。Windows 可能在创建新线程时使用加载顺序调用 DllMain,包括 COM 服务器创建的线程。请注意,这取决于 Windows 的内部行为。此外,COM 服务器实际上可能依赖于 fp 异常,因为它启用了它们。禁用 FP 异常可能会导致 COM 服务器出现意外行为。

于 2011-08-03T23:20:26.597 回答