2011-08-03 28 views
10

引进 - 漫长而枯燥的部分

(现在的问题是在结束)第三方代码被修改FPU控制字

我越来越严重的头痛过,保持第三方COM组件改变FPU控制字。

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

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

我没有这个COM组件的来源,但我正在与作者联系。我从他那里得到的答复是“呃?”。我不认为他有丝毫的线索我在说什么,所以我担心我必须自己做一些事情。我相信他的运行时(我认为它是Delphi或Borland C++,因为DLL中充满了符号名称,全部以大写字母T开头),或者他正在使用的其他第三方代码,这就是问题所在。我不认为他的代码明确地修改了FPU控制字。

那么,我该怎么办?从业务角度来看,使用这个第三方组件势在必行。从技术角度来看,我可以抛弃它,并自己实施通信协议。但是,这将非常昂贵,因为此协议涉及处理信用卡交易。我们不想承担责任。

我非常需要一个关于Borland产品中的FPU设置的入门知识或一些有用的信息,我可以将这些信息传递给组件的作者。

的问题

有什么可以做什么?我不认为组件作者有什么需要解决它(通过从他相当无知的反应来判断)。

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

  1. 为什么我不能捕捉到例外?
  2. 如果我成功捕捉FPU异常,重置FPU控制字,并让执行继续,因为什么也没有发生 - 都是下注吗?

更新

我要感谢大家对他们的建议。我已经发送了作者的指示,说明他可以做些什么来让生活更加轻松,不仅仅是我,还有许多其他代码的客户。我向他建议,他应该在DllMain(DLL_PROCESS_ATTACH)处采样FPU控制字,并在稍后保存控制字,以便在调用我的事件处理程序之前重置FPU CW,并从我的调用中返回。

现在,如果有人感兴趣,我有一个黑客入侵。破解可能是一个糟糕的问题,因为我不知道它会如何处理他的代码。我之前已经收到确认,他没有在他的代码中使用任何浮点数,所以这应该是安全的,除非使用某些依赖于FPU异常的第三方代码。

我对我的应用程序所做的两处修改:

  1. 缠上了我的消息泵
  2. 安装窗钩(WH_CALLWNDPROC)赶角落情况下,消息泵被旁路

在这两种情况下,我检查FPU CW是否已更改。如果有,我将它重置为_CW_DEFAULT

+2

http://www.virtualdub.org/blog/pivot/entry.php?id=53;长话短说,他们也在任何“危险的”代码路径之后添加了代码来恢复FPU控制世界,但帖子提到“可以禁用Borland运行时库的这种行为并避免此问题” –

+0

@matteo , 谢谢你的链接。一个有趣的阅读!我会将其转发给作者。 –

+0

顺便说一下,我认为禁用该帖子中提到的行为的方法实际上是使用Set8087CW函数,正如@David在他的回答中所解释的。 –

回答

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%决定性的答案,但我不能把所有这些想法变成一个评论!

+0

感谢您的信息!我希望另一个人愿意帮助我,因为我为他创造了收入。我猜如果他实现了一个新的接口,比如说'IFPUControlWordPolicy',我可以通过这个接口注册我的首选CW,那么他可以确保CW被设置为我想要的,并将它恢复到他的首选CW来电等。如果这很简单。可能有运行时代码在“他的场景”后面运行,修改了FPU CW。他声称他不会对FPU CW做任何事情,并且不知何故FPU CW被修改了...... –

+0

......看起来CW在我从事件接收器返回时被修改。我怀疑他是异步生成事件的(即,它们不是由于我的呼叫进入他的组件而产生的),并且CW被修改。我无法为自己辩护,因为在这种情况下,我的代码并不在此时的调用堆栈中。 :/ –

+0

emba runtime最明确地设置CW。他不会明确地做。尽管他的代码可能依赖于FP异常。 –

6

尽管FP控制字是每个线程,当创建新线程时会调用dllmain函数,但我不认为您可以避免进入新进程。

我建议你分拆一个新程序来运行COM,并用你最喜欢的进程间通信方法(例如,windows消息,out-of-proc COM,命名管道,套接字等)。通过这种方式,COM服务器可以自由地进行各种损坏(包括崩溃本身),而不会导致主机进程停机。

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

+0

CW需要重复重置,因为每次delphi代码运行delphi库都会修改它 –