1 錯誤提示
error LNK2019: 無法解析的外部符號 __imp_FreeImage_Allocate,該符號在函數 "public: bool __cdecl colmap::Bitmap::Allocate(int,int,bool)" (?Allocate@Bitmap@colmap@@QEAA_NHH_N@Z) 中被引用。
2 錯誤分析與解決
該錯誤是在“C++項目屬性 ---- 鏈接器 ----- 輸入 ---- 附加依賴項”中,沒有輸入第三方靜態鏈接庫的時候,報錯的。需要指明的是,你有時候忘記輸入某些lib,工程編譯的時候不會報錯,而會在鏈接的時候出錯。
此時,只需要輸入對應debug/release版本的lib庫即可。
3 補充
各個動態鏈接庫的編譯方式必須統一才行,要不然很容易對庫函數的引用產生沖突。簡單來說就是,如果使用的第三方函數庫編譯方式采用/MD,那么主工程也應該使用/MD。我使用了libevent,而主工程默認采用/MT,所以需要忽略一大堆的函數庫。
MSDN中對於在不同的配置下Link的LIB作了說明:
編譯器會自動根據編譯選項,選擇對應的LIB文件。一般情況下這不會出現問題。然而,在部分情況下,一旦你的程序的各個部分(LIB, OBJ…)並非由相同的編譯選項編譯出,而Link在一起的話,會出現各種各樣的看似很難解決的問題,這類問題主要以重復定義的錯誤形式存在,通常的解決方法也很簡單,就是選擇同樣的編譯選項進行編譯之后再Link。
3.1 鏈接方式
1、為什么選擇/MD,不選/MT?
(1)程序就不需要靜態鏈接運行時庫,可以減小軟件的大小;
(2)所有的模塊都采用/MD,使用的是同一個堆,不存在A堆申請,B堆釋放的問題;
(3)用戶機器可能缺少我們編譯時使用的動態運行時庫。(補充:如果我們軟件有多個DLL,采用/MT體積增加太多,則可以考慮/MD + 自帶系統運行時庫)
選擇/MD要注意“多個模塊選擇不同版本運行庫”的問題:
-
多個dll被一個exe LoadLibrary加載,如果這些dll使用的運行時庫是不同的,那么可能出現加載失敗,原因可能是舊版本的運行時庫已經在了,而某個dll它需要的是新版本的運行時庫,舊版本不符合要求。
-
如果工程里所有的模塊都是自己寫的或者可以完全控制的,那么這個問題不難解決,只需要在工程設置里都設置/MD,然后在相同的環境下編譯一次就行。但是假如這個模塊是外界提供的呢?
-
可能存在這種情況:A動態庫使用了B靜態庫,B靜態庫使用了C動態庫,B靜態庫是外界提供的,我們要使用它,但無法修改它,我們也無法接觸到C動態庫。如果C動態庫使用的運行時庫版本跟編譯A動態庫的本地使用的不一致,那么A動態庫里的嵌入信息就會記錄兩個不同版本的運行時庫,它被加載的時候,可能會選擇版本新的。假設A動態庫被一個exe LoadLibrary加載,而這個exe本身的運行時庫是舊的,這樣就會導致A動態庫加載失敗,即便把新的運行時庫拷貝到目錄下也不行,因為exe這個進程已經加載了那個舊的運行時庫。這時候必須使用manifest文件指定嵌入到A動態庫里的運行時庫為某個版本,忽略掉C動態庫使用的運行時庫版本。
-
這個問題挺復雜的,我心思沒去驗證windows的PE文件加載會對運行時庫做什么樣的優先選擇、運行時庫在靜態庫里的記錄…。只要記住,給外界使用的組件版本盡量避免使用/MD(這樣會導致膨脹嗎?據說,安裝包可以做字節流式壓縮)。
-
附上另一個問題:靜態庫的依賴關系:exe–>libA–>libB,現在不想讓exe接觸到libB,於是把libA的librarian選項–>General選項–>Link Library Dependencies設置為Yes,這樣即可,libA會包含libB,exe只需要接觸libA。另外需要特別注意,libA對libB的依賴只需要且只能在Solution的Project Dependencies里設置,如果在libA的代碼里寫了”#pragma comment(lib, “libB.lib”)”,會導致exe在link libA的時候提示找不到libA。如果exe還出現link錯誤,那一定是VS抽筋了:)2、為什么選擇/MT,不選擇/MD?(1)有些系統可能沒有程序所需要版本的運行時庫,程序必須把運行時庫靜態鏈接上。
2、為什么選擇/MT,不選擇/MD?
(1)有些系統可能沒有程序所需要版本的運行時庫,程序必須把運行時庫靜態鏈接上。
(2)減少模塊對外界的依賴。
選擇/MT 時存在一個“堆空間釋放”的問題:
不同的模塊各自有一份C運行時庫代碼、或者根本沒有C運行時庫,導致了各個模塊會有各自的堆。如果在A堆中申請空間,到B堆中釋放就會有崩潰,在模塊A申請的空間,必須在模塊A中釋放。
多個模塊,必須選擇相同的運行時庫。
Summary
知道各個不同的LIB代表的版本信息非常重要,可以幫助快速定位問題
在編程的時候,一定要把所有的項目的編譯選項(是靜態鏈接Runtime庫還是動態鏈接Runtime庫,Debug/Release)配置成一樣的。如果部分LIB/OBJ是由第三方提供(OBJ情況很少見),一般情況下只能調整自己的編譯選項,除非你可以要求第三方提供其他版本的編譯好的LIB
在發布可重用的靜態LIB庫供其他人調用的時候,最好對應不同的編譯選項,乃至VC版本,提供不同版本的LIB。VC自己的Runtime就是最好的例子。
參考
C++工程編譯之“error LNK2001: 無法解析的外部符號”