2017-02-10 62 views
0

我正在用C++寫一個Windows DLL。這個庫只有C接口,並且只使用標準的Windows庫,這些庫也有C接口。因此,將所有C++庫靜態鏈接到庫中似乎是安全的,因爲我僅在使用C接口時不依賴於C++ ABI版本。在Windows DLL上靜態鏈接libgcc

不幸的是,當我用-static-stdc++ -static-libgcc編譯我的庫時,我的庫停止處理異常,並且當引發一些異常時,DLL調用其靜態鏈接的_Unwind_RaiseException函數,該函數中止整個應用程序。

我認爲這可能是一個損壞的libgtcc.a所以我試圖更新我的編譯器。但MinGW 4.8和MinGW 6.3的結果是一樣的。

請有誰能夠解釋我在這裏發生了什麼?

Klasyc

+2

使用TDM-GCC。請參閱[簡介](http://tdm-gcc.tdragon.net/quirks) – Ripi2

+0

或MinGW,它支持SEH。 – rustyx

+0

@RustyX OP已經嘗試了MinGW,正如你所說的那樣支持SEH,但是如果庫是靜態鏈接的並且異常是通過DLL邊界引發的,那麼不會。這是TDM-GCC解決的問題之一。如果運行時庫是動態鏈接的(即在進程中只有一個版本),MinGW很好。 –

回答

0

你是否正在處理DLL中的所有異常?如果任何異常使用C調用約定在函數外部「泄漏」,則會使應用程序崩潰。

我有C++異常下MinGW的x86_64-5.3.0-Win32的SEH-rt_v4-REV0和沒有問題的mingw32 4.8.1 DWARF2具有靜態的libstdC++/libgcc中。

DLL源(dll.cpp):

#include <windows.h> 
#include <exception> 
#include <iostream> 

extern "C" { 
__declspec(dllexport) int __stdcall test() { 
    try { 
     new int[-1]; 
     return 123; 
    } catch (const std::exception& ex) { 
     std::cerr << "Exception:" << ex.what() << std::endl; 
     return 456; 
    } 
} 
} 

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD  fdwReason, LPVOID lpvReserved) 
{ 
    return TRUE; 
} 

應用源(app.c):

#include <stdio.h> 

#ifdef __cplusplus 
extern "C" 
#endif 
__declspec(dllimport) int __stdcall test(); 

int main() { 
    printf("result: %d\n", test()); 
    return 0; 
} 

編譯:

g++ -O2 -static-libgcc -static-libstdc++ -shared -Wl,--out-implib=dtest.lib -s -o dtest.dll dll.cpp

g++ -O2 -static-libgcc -static-libstdc++ -L. -s -o app app.c -ldtest

輸出:

Exception:std::bad_alloc 
result: 456 

爲了回答您的評論「爲什麼我需要SEH支持的編譯器?我認爲SEH允許我捕捉Windows例外「 - 在Windows上,SEH是的所有例外的事實標準,包括C++異常(當使用Visual C++編譯時)MinGW對於C++異常有不同的實現(SEH,SJLJ和DWARF),但是SEH是唯一沒有開銷的機制,所以如果它可用,你應該更喜歡它通過SJLJ和DWARF。

+0

謝謝你的例子。當我嘗試像你一樣編譯它時,它確實有效。但是當我嘗試用g ++編譯'app.c'(我必須將'extern「C''添加到'test()'函數頭)時,應用程序會崩潰。因此,似乎使用靜態libgcc的兩個副本會造成問題。請注意我正在使用DWARF-2例外,因爲我必須生成32位二進制文​​件。 – klasyc

+0

我沒有這個問題。看到我更新的答案。 – rustyx

+0

對不起,延遲迴答。請嘗試在沒有此開關的情況下使用'-static-libgcc'和應用程序編譯DLL。靜態DLL和動態應用程序的結合似乎打破了事情。 – klasyc

0

感謝RastyX在他的答案中的那個簡單的例子經過一些研究,我發現應用程序崩潰時,我編譯DLL部分與-static-libgcc和應用程序部分沒有此開關(兩部分編譯相同的g ++)

也感謝Ripi2的TDM-GCC sugg estion。我嘗試了SJLJ和DWARF-2異常處理,並且都可以工作。

總而言之,似乎MinGW的libgcc不喜歡每個進程實例化多次。在我的情況下,我使用DLL中的一個副本(靜態鏈接)和一個副本在應用程序中,這可能會打破異常處理。另一方面,TDM-GCC側重於基本庫的靜態鏈接,因爲它的網頁說,因此即使沒有-static-stdc++ -static-libgcc命令行開關(這是我想要的),也只產生依賴於Windows庫的二進制文件。此外,異常處理在這裏工作,所以更改工具鏈對我來說是正確的。