2011-06-06 123 views
5

我正在一個有時會引發未處理的異常的DLL中工作。我正在使用madExcept來檢測和調試錯誤的代碼,但是當我最終部署我的DLL時,我想在DLL中包含我自己的全局異常處理程序來記錄異常。如何在DLL中創建全局異常處理程序?

所以,問題是我怎麼能在我的Delphi DLL設置一個全球性的異常處理程序?

回答

10

「一個全球性的異常處理程序」的概念是不存在的DLL中它存在於VCL的方式。要理解爲什麼,請記住,異常通過展開堆棧直到它們可以找到處理程序而傳播。該VCL可以因爲在VCL應用程序安裝全局異常處理程序,所發生的一切(不包括啓動和關閉)將在調用堆棧有TApplication.Run的地方,而這也正是它把異常處理程序。由於你的DLL沒有像這樣的單一中心點,你不能這樣做。

你可以做的是建立一個「中央異常處理程序」,在您的DLL某處。它應該以Exception對象作爲參數。然後做這樣的事情你全部出口套路:

procedure MyExportedRoutine(param: integer); 
begin 
    try 
    //do normal stuff 
    except 
    on E: Exception do 
     CentralExeptionHandler(E); 
    end; 
end; 

這真的是你能做的最好的,除非你使用COM。如果你正在編寫一個COM DLL,使用safecall調用約定標記你的接口方法,編譯器會默默地爲你生成代碼來處理異常傳播。

+4

+1我認爲您必須爲您的DLL中的所有入口點執行此操作,因爲異常應該不跨模塊邊界傳播。擁有入口點例程的通用模板也有其他好處。例如,您可以將調用添加到例程,這些例程設置並恢復浮點控制字,當您的主機應用程序不是Delphi應用程序時,這經常會成爲問題。 – 2011-06-06 06:51:55

10

究竟你的「全球異常處理」是什麼意思?

視窗結構化異常處理(SEH),在32位,通過走在其中發生異常的線程的異常處理程序鏈查找處理程序。異常處理鏈是一個鏈接的記錄列表,其頭部位於FS:[0];記錄通常在堆棧上分配,每當try時被推送,並在退出受保護塊時彈出。每個異常記錄都有一個回調例程; Windows在搜索階段會調用此例程以查看異常的詳細信息,以確定鏈的此「級別」是否將「處理」異常。然後,Windows再次通過異常鏈展開調用堆棧,使用不同的值調用每個回調,讓它知道展開正在進行,直到它到達選擇處理異常的處理程序。如果沒有找到處理程序,則該進程將被終止,並且不會通知。通常情況下,這不會發生;操作系統會在堆棧底部(鏈中的最後一個元素)安裝自己的最後機會處理程序,這通常會彈出熟悉的Windows「此程序遇到問題」對話框。但是如果事情變得非常糟糕,或者異常處理程序鏈被搞亂了,那麼這個過程就會很難實現。因此,從這個對Windows異常處理的簡要概述中,應該清楚的是沒有單個「全局」處理程序,只有一個處理程序列表,每個線程一個列表(FS寄存器是線程上下文的一部分);因此,而「最後機會」處理程序是最早安裝在堆棧中的處理程序。捕獲DLL中發生的異常的最簡單方法是立即在每個入口點安裝異常處理程序。有關如何做到這一點的細節,請參閱梅森的答案(這與try/except);但請注意,如果您的DLL調用回其他地方(例如,通過回調例程),那麼您可能會捕獲對您而言並非「意味着」且不是由您的代碼引起的異常。 (在DLL級別期望異常通過第三方代碼傳播的風格很糟糕,但可能發生。)

+0

大巴里!這讓我更清楚爲什麼我有時看到「這個程序遇到了問題」,有時我卻沒有。我知道在這些情況下它很脆弱,不知道爲什麼。 – 2011-06-07 01:39:56

+3

@Warren Borland實際上擁有這項異常處理技術方面的專利,我認爲EMBT現在擁有它。 MS爲此付出了代價,但x64的處理方式有所不同,部分原因可能在於它獲得了專利;另外,就我所知,GNU/LLVM /等等都沒有優秀的SEH支持,部分原因是由於專利問題。不幸的是,真的。 – 2011-06-07 02:55:24