2011-08-03 27 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庫都會修改它 –