2012-01-24 18 views
4

我的代碼被編譯爲Windows DLL與Visual C++。我想在調用terminate()時記錄罕見情況,因此我在庫初始化函數中設置了terminate()處理程序,後者在使用我的庫之前由用戶代碼調用。我的處理程序寫入日誌並調用abort()來模擬默認的terminate()行爲。如何檢測是否安裝了自定義的terminate()處理程序?

問題是用戶代碼也可能用C++編寫,並使用相同的C++運行時版本,因此與我的庫共享terminate()處理程序。該代碼可能還希望更改terminate()處理程序以進行日誌記錄。所以他們會打電話set_terminate(),然後加載並初始化我的庫,我的庫也會調用set_terminate()並覆蓋它們的terminate()處理程序,這對他們來說很難檢測到,因爲terminate()處理程序是他們將要測試的最後一個東西。

所以我想要以下內容。在庫初始化函數中,我將retrieve the current terminate() handler,找到它是否是一個標準的,然後如果它碰巧是一個非標準的函數,我將存儲它的地址,稍後(如果需要)我的terminate()處理程序將寫入日誌,然後將呼叫轉移到該自定義terminate()處理程序。

是否有可能找到當前安裝的terminate()處理程序是默認還是自定義程序?

+3

如果你打算反正調用'abort',爲什麼不總是鏈接到前一個終止處理程序而不是調用'abort'? –

+0

FWIW,我不認爲鏈接以前的終止處理程序是一個好主意。如果程序死於代碼中,請調用您的處理程序,如果在主機代碼中,則調用主機處理程序。請參閱下面我的RAII答案以瞭解如何實現此目的。 – Ben

回答

0

MSDN隱約說

如果沒有以前的功能設置,(的set_terminate)的返回值可以用來恢復默認的行爲;這個值可能是NULL;

_get_terminate相同。我發現它並不真正有用,因爲如果返回的值不是NULL,仍然不能保證它是有效的terminate處理程序。一種可能的解決方案是使用GetModuleHandleExGET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS來確定set_terminate返回的地址是否是任何模塊中的有效地址。

+0

我認爲我們可以假設,如果_get_terminate爲null,則不存在預先存在的終止處理程序。 – Ben

+0

當然,但我們可以假設相反的,即指向有效處理程序的非空結果嗎?我不太善於用英語來說明,但他們說沒有處理程序的情況下返回的值可能是「NULL」。這是否意味着它「可能」有一些特殊的非NULL值? –

+0

它表示您可以通過調用'set_terminate'來恢復以前的行爲,並返回之前調用'set_terminate'的返回值。這並不排除可能是特殊情況的「特殊」返回值(例如0xffffffff)。但只要你把它當作不透明就沒有問題。不知道他爲什麼想連鎖前一個,但我不認爲這是一個好主意。 – Ben

2

通過RAII做這樣的:

class terminate_scope 
{ 
public: 
    terminate_function _prev; 
    terminate_scope(terminate_function f = NULL){ 
    _prev = set_terminate(f); 
    } 
    ~terminate_scope(){ 
    set_terminate(_prev); 
    } 
}; 

要使用:

void MyFunctionWantsOwnTerminateHandler(){ 
    terminate_scope termhandler(&OwnTerminateHandler); 
    // terminate handler now set 
    // All my code will use that terminate handler 
    // On end of scope, previous terminate handler will be restored automatically 
} 

你可以有終止處理程序鏈上一個,如果你有絕對的把握,你需要。

+0

+1優秀,乾淨的解決方案。 –

相關問題