在一個原有MFC Dll項目中添加了兩個CPP文件,由於新添加的CPP文件沒有用到MFC,在工程中設置這兩個CPP文件不使用預編譯頭文件。
今天早晨clean後重新編譯,出現”LNK2005:_DllMain@12 already defined"錯誤,之前也經常遇到類似問題,總是百度一下解決問題就不再深究,
今天詳細查閱了msdn的資料,基本上搞清楚了問題的緣由及解決方案。
具體原因:CRT 庫對 new、delete 和 DllMain 函數使用弱外部鏈接。MFC 庫也包含 new、delete 和 DllMain 函數。這些函數要求先鏈接 MFC 庫,然後再鏈接 CRT 庫。
應對方案:可以參考微軟給出的兩個方案,基本思路都是干預鏈接時引用lib的順序,先mfc後crt。我比較傾向於第二個方案,可以乾淨徹底的解決問題。
第一個方案的思路是忽略衝突的lib庫,然後再按正確的順序添加lib庫。
第二個方案的思路是通過添加/verbose:lib鏈接選項,打印出鏈接的順序,找到問題根源並解決。
具體到我自己的dll項目,我首先懷疑到新加的兩個CPP文件,在其首部添加 #include "stdafx.h" ,然後設置其使用stdafx.h作爲預編譯頭文件,
重新build,link成功。這個微軟給出的文檔中有相關的參考:
以下摘自msdn:http://support.microsoft.com/kb/148652/zh-cn
當 C 運行時 (CRT) 庫和 Microsoft 基礎類 (MFC) 庫的鏈接順序有誤時,可能會出現以下 LNK2005 錯誤之一:
nafxcwd.lib(afxmem.obj) :error LNK2005:
"void * __cdecl operator new(unsigned int)"(??2@YAPAXI@Z) already
defined in LIBCMTD.lib(new.obj)
nafxcwd.lib(afxmem.obj) :error LNK2005:
"void __cdecl operator delete(void *)"(??3@YAXPAX@Z) already defined
in LIBCMTD.lib(dbgnew.obj)
nafxcwd.lib(afxmem.obj) :error LNK2005:
"void * __cdecl operator new(unsigned int,int,char const *,int)"
(??2@YAPAXIHPBDH@Z) already defined in LIBCMTD.lib(dbgnew.obj)
mfcs40d.lib(dllmodul.obj):error LNK2005:_DllMain@12 already defined in
MSVCRTD.LIB (dllmain.obj)
mfcs42d.lib(dllmodul.obj):error LNK2005:_DllMain@12 already defined in
msvcrtd.lib(dllmain.obj)
注意:下列步驟基於 Visual C++ 6.0 進行。
解決方案一:強制鏈接器按照正確的順序鏈接庫
- 在“項目”菜單上,單擊“設置”。
- 在“項目設置”對話框的“以下項目的設置”視圖中,單擊以選中出現鏈接錯誤的項目配置。
- 在“鏈接”選項卡上,單擊以選中“類別”組合框中的“輸入”。
- 在“忽略庫”框中,插入庫名(例如,Nafxcwd.lib;Libcmtd.lib)。
注意:等效的鏈接器命令行是:/NOD:<library name>。 - 在“對象/庫模塊”框中,插入庫名。必須確保這些庫按順序列出,而且是行中的前兩個庫(例如,Nafxcwd.lib 和 Libcmtd.lib)。
解決方案二:找到並糾正出現問題的模塊
要查看當前的庫鏈接順序,請按照下列步驟操作:- 在“項目”菜單上,單擊“設置”。
- 在“項目設置”對話框的“以下項目的設置”視圖中,單擊以選中出現鏈接錯誤的項目配置。
- 在“鏈接”選項卡上的“項目選項”框中鍵入 /verbose:lib。
- 重新生成項目。在鏈接過程中,這些庫將在輸出窗口中列出。
如果源文件的擴展名爲 .c,或者該文件的擴展名爲 .cpp 但不使用 MFC,則可以創建一個較小的頭文件 (Forcelib.h) 並將其放在模塊的頂端。這個新的頭文件可確保按照正確的順序搜索庫。
Visual C++ 不包含該頭文件。要創建此文件,請按照下列步驟操作:
- 打開 Msdev\Mfc\Include\Afx.h。
- 選定 #ifndef _AFX_NOFORCE_LIBS 和 #endif //!_AFX_NOFORCE_LIBS 之間的行。
- 將選定部分複製到 Windows 剪貼板。
- 創建一個新文本文件。
- 將剪貼板的內容粘貼到這個新文件中。
- 將該文件另存爲 Msdev\Mfc\Include\Forcelib.h。
在 Visual C++ .NET 中重現問題的步驟
- 啓動 Microsoft Visual Studio .NET。
- 在“文件”菜單上,指向“新建”,然後單擊“項目”。
- 單擊“項目類型”下的“Visual C++ 項目”,然後單擊“模板”下的“MFC 應用程序”。
- 在“名稱”文本框中,鍵入 Q148652。
- 在“位置”文本框中,鍵入 C:\Test,然後單擊“確定”。
- 在“MFC 應用程序嚮導”對話框中,單擊“應用程序類型”。
- 單擊“應用程序類型”下的“基於對話框”,然後單擊“MFC 的使用”下的“在靜態庫中使用 MFC”。
- 單擊“完成”。
- 在“解決方案資源管理器”中,選擇“源文件”下的全部三個 .cpp 文件。
- 右鍵單擊三個選定的文件,然後單擊“刪除”。
- 右鍵單擊“源文件”,指向“添加”,然後單擊“添加新項”。
- 單擊“模板”下的“C++ 文件”。在“名稱”文本框中,鍵入 Aa。單擊“打開”。
- 將以下代碼粘貼到 Aa.cpp 中:
int test(){new int; return 1;}
- 右鍵單擊“源文件”,指向“添加”,然後單擊“添加現有項”。
- 選擇以下文件:
- Q148652.cpp
- Q148652Dlg.cpp
- stdafx.cpp
- 單擊“打開”。
- 您在第 15 步中選擇的文件將出現在“源文件”下。
- 選擇“源文件”下的全部四個 .cpp 文件。
- 右鍵單擊選定的四個 .cpp 文件,然後單擊“屬性”。
- 展開“配置屬性”,然後展開“C/C++”。
- 單擊“預編譯頭”。
- 將“創建/使用預編譯頭”屬性設置爲“不使用預編譯頭”。單擊“確定”。
- 在“生成”菜單上,單擊“重新生成解決方案”。