问题:
背景:
工作中开发的CookieTool工具是一个MFC的程序,如果选择以共享dll中使用MFC的方式,那么在没有该版本(MFC的DLL或者运行时库DLL)运行库操作系统上是不能运行的。这里体现出程序的兼容性问题
想到解决办法:
将所有的依赖DLL,都静态链接到exe当中,这样就能在没有环境的系统上跑了。
研究目的:
VS IDE环境各种编译选项,比较复杂,乱用或者用错的情况时有发生,尤其在多项目联合编译时经常编译不过,非常影响工作效率,很有必要深入研究一番IDE的一些关键的选项配置。
测试研究
下面以VS2008为例探索研究,其他高版类似(或许看MS的使用说明手册来的快)。下面就来看看一些跟MFC和 /MT /MDd 相关的一些设置,资深人员可以绕过了
目的:exe能在没有运行时库环境中运行,顺便将/MD /MT 和MFC选项关系彻底搞明白
先了解什么是MT MD
/MT 的意思就是使用微软的多线程静态链接库
/MD 的意思就是使用微软的多线程动态链接库
在VS中的工程选项里面C/C++可以设置/MT /MD和调试版本/MTd /MDd
MT MD选项对应的是CRT运行时库
参考博文:
vs 运行时库 - lyq240919525的专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/lyq240919525/article/details/40402909
VC中使用静态链接库 之 /MD /MT 用法 - Vonger的专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/vonger/article/details/6368799
配置的MFC选项 代表有啥意思
exe配置MFC使用选项有三个选项:
一个是不使用MFC的库,简单来说exe当中不会引用到mfc框架
一个是静态链接MFC的库,简单来说mfc框架会以静态库的方式连接到exe当中
还有一个是动态链接MFC的库,简单来说mfc框架会以动态库的方式连接到exe当中
MFC选项 对应的是MFC DLL是静态链接还是动态链接
参考博文:VS项目属性中MFC的使用选项_pengdexin_新浪博客
http://blog.sina.com.cn/s/blog_68fea3080101939l.html
实验前的环境:
基于Dialog 的MFC 的exe 项目和 外加两个开源的算法静态lib项目
实验一:
前提条件: exe 的MFC的使用配置成 在共享中使用MFC 且 exe 配置成/MDd模式,将引用的静态库lib_json.lib配置为 /MTd模式
编译32位Debug版本链接时报错:
1>msvcprtd.lib(MSVCP90D.dll) : error LNK2005: "public: __thiscall std::basic_string,class std::allocator>::~basic_string,class std::allocator>(void)" (??1?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@XZ) 已经在lib_json.lib(json_value.obj) 中定义1>msvcprtd.lib(MSVCP90D.dll) : error LNK2005: "public: bool __thiscall std::basic_string,class std::allocator>::empty(void)const " (?empty@?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QBE_NXZ) 已经在lib_json.lib(json_reader.obj) 中定义1>msvcprtd.lib(MSVCP90D.dll) : error LNK2005: "public: class std::basic_string,class std::allocator> & __thiscall std::basic_string,class std::allocator>::operator+=(char)" (??Y?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAEAAV01@D@Z) 已经在lib_json.lib(json_reader.obj) 中定义1>msvcprtd.lib(MSVCP90D.dll) : error LNK2005: "public: __thiscall std::basic_string,class std::allocator>::basic_string,class std::allocator>(char const *)" (??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@PBD@Z) 已经在lib_json.lib(json_value.obj) 中定义1>msvcprtd.lib(MSVCP90D.dll) : error LNK2005: "public: class std::basic_string,class std::allocator> & __thiscall 1>msvcprtd.lib(MSVCP90D.dll) : error LNK2005: "public: char const & __thiscall std::basic_string,class std::allocator>::operator[](unsigned int)const " (??A?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QBEABDI@Z) 已经在lib_json.lib(json_writer.obj) 中定义1>msvcprtd.lib(MSVCP90D.dll) : error LNK2005: "protected: char * __thiscall std::basic_string,class std::allocator>::_Myptr(void)" (?_Myptr@?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@IAEPADXZ) 已经在lib_json.lib(json_value.obj) 中定义1>msvcprtd.lib(MSVCP90D.dll) : error LNK2005: "public: __thiscall std::_Container_base_secure::~_Container_base_secure(void)" (??1_Container_base_secure@std@@QAE@XZ) 已经在lib_json.lib(json_value.obj) 中定义1>msvcprtd.lib(MSVCP90D.dll) : error LNK2005: "public: __thiscall std::basic_string,class std::allocator>::basic_string,class std::allocator>(class std::basic_string,class std::allocator> const &)" (??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@ABV01@@Z) 已经在lib_json.lib(json_value.obj) 中定义
1>msvcprtd.lib(MSVCP90D.dll) : error LNK2005: "public: class std::locale::facet * __thiscall std::locale::facet::_Decref(void)" (?_Decref@facet@locale@std@@QAEPAV123@XZ) 已经在lib_json.lib(json_reader.obj) 中定义
1>LIBCMTD.lib(sprintf.obj) : error LNK2005: _sprintf 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(sprintf.obj) : error LNK2005: _sprintf_s 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(tidtable.obj) : error LNK2005: __encode_pointer 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(tidtable.obj) : error LNK2005: __decode_pointer 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(dbgheap.obj) : error LNK2005: __recalloc 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(dbgheap.obj) : error LNK2005: __CrtSetCheckCount 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(setlocal.obj) : error LNK2005: __configthreadlocale 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(crt0dat.obj) : error LNK2005: _exit 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(crt0dat.obj) : error LNK2005: __exit 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(crt0dat.obj) : error LNK2005: __cexit 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(crt0dat.obj) : error LNK2005: __amsg_exit 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(crt0dat.obj) : error LNK2005: __initterm_e 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(mlock.obj) : error LNK2005: __lock 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(mlock.obj) : error LNK2005: __unlock 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(winxfltr.obj) : error LNK2005: __XcptFilter 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(dbghook.obj) : error LNK2005: __crt_debugger_hook 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(dbgrptw.obj) : error LNK2005: __CrtDbgReportW 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(lconv.obj) : error LNK2005: _localeconv 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(crt0init.obj) : error LNK2005: ___xi_a 已经在msvcrtd.lib(cinitexe.obj) 中定义
1>LIBCMTD.lib(crt0init.obj) : error LNK2005: ___xi_z 已经在msvcrtd.lib(cinitexe.obj) 中定义
1>LIBCMTD.lib(crt0init.obj) : error LNK2005: ___xc_a 已经在msvcrtd.lib(cinitexe.obj) 中定义
1>LIBCMTD.lib(crt0init.obj) : error LNK2005: ___xc_z 已经在msvcrtd.lib(cinitexe.obj) 中定义
1>LIBCMTD.lib(hooks.obj) : error LNK2005: "void __cdecl terminate(void)" (?terminate@@YAXXZ) 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(invarg.obj) : error LNK2005: __invalid_parameter 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(invarg.obj) : error LNK2005: __invoke_watson 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(atox.obj) : error LNK2005: _atoi 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(errmode.obj) : error LNK2005: ___set_app_type 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>LIBCMTD.lib(_ctype.obj) : error LNK2005: _isalnum 已经在msvcrtd.lib(MSVCR90D.dll) 中定义
1>libcpmtd.lib(locale0.obj) : error LNK2005: "void __cdecl _AtModuleExit(void (__cdecl*)(void))" (?_AtModuleExit@@YAXP6AXXZ@Z) 已经在msvcprtd.lib(locale0_implib.obj) 中定义
。。。。。。。。
问题原因分析:
exe配置的是MDd运行时库,也就是说最后生成的exe运行时依赖MSVCR90D.dll,而exe引用的两个静态库,编译选项是/MTD,这样生成的两个静态库自己,又会静态链接CRT的lib(比如这里libcpmtd.lib)到静态库中,导致链接生成的exe有库冲突 。lib_json /MTD 使用的libcpmtd.lib,exe /MDd使用的是MSVCR90D.dll,而MSVCR90D.dll和libcpmtd.lib其实代码是一样的,链接器在查找外部引用时,会找到两份实现代码,不知道选择哪一份,所以编译报错了。(多半跟exe的 导入表有关)
解决方案:
将需要链接的静态库lib_json.lib,运行库由MTd改成 MDd,就是两份代码都用MSVCR90D.dll
实验二:
前提条件: exe 的MFC的使用配置成 在共享中使用MFC 且 exe 配置成/MDd模式,将引用的静态库lib_json.lib配置为 /MDd模式
再次编译:没有报错了,仍然有些警告,不过已经编译过去了
>LINK : warning LNK4098: 默认库“MSVCRT”与其他库的使用冲突;请使用/NODEFAULTLIB:library
3>正在嵌入清单
3>Microsoft (R) Windows (R) Resource Compiler Version 6.1.6723.1
3>Copyright (C) Microsoft Corporation. All rights reserved.
3>LINK : warning LNK4098: 默认库“MSVCRT”与其他库的使用冲突;请使用/NODEFAULTLIB:library
测试结果:
直接拿到一个64位系统下运行,报以下错误:
上图 这个就是其他机器上缺少mfc的DLL或者是CRT的DLL(MSVCR90D.dll)导致的
实际测试,编译出来的 exe 运行库选择MD /MTD,都不能在没有MFC 库的系统上跑起来。
实验三:
前提条件:按照图1 设置(在共享DLL中使用MFC) 且exe 修改成MT模式 静态库lib_json.lib配置为 /MTd模式
链接时报如下错:
3>c:\program files (x86)\microsoft visual studio 9.0\vc\atlmfc\include\afxver_.h(81) : fatal error C1189: #error : Please use the /MD switch for _AFXDLL builds
强制deubg版本的exe运行时库选择 MT(静态链接MFC库)编译直接报错? 编译器不让编译?为什么
debug版本的exe强制是使用动态加载MFC的DLL方式,不允许静态链接CRT 库?不是的 前面分析过见图一,exe 引用库是DLL,所以只能指定动态的CRT库,而不能指定MT的CRT静态库
实验四:
前提条件: 将exe修改成 在静态库中使用MFC 且 exe 修改成MT模式 ,链接的静态库lib_json.lib配置为 /MTd模式
再次编译:
程序直接放到测试环境中可以运行---------------说明这就是静态库链接方式,OK问题基本解决了
实验五:
前提条件: exe修改成 在静态库中使用MFC 且强行将exe的/MT 改成/MD模式 ,链接的静态库lib_json.lib配置为/MTd模式
链接时报错:
1>c:\program files (x86)\microsoft visual studio 9.0\vc\atlmfc\include\afx.h(24) : fatal error C1189: #error : Building MFC application with /MD[d] (CRT dll version) requires MFC shared dll version. Please #define _AFXDLL or do not use /MD[d]
这里跟实验二报的是同样一个错误。说明exe静态使用MFC,所对应的CRT只能是/MT模式,不能设置成/MD模式
实验六:
前提条件: exe修改成 在静态库中使用MFC 且exe为/MT模式 + 将链接的静态库lib_json.lib配置为/MD模式
exe在链接时又报库冲突:
3>MSVCRT.lib(MSVCR90.dll) : error LNK2005: __gmtime64 已经在libcmtd.lib(gmtime64.obj) 中定义
3>MSVCRT.lib(MSVCR90.dll) : error LNK2005: __time64 已经在libcmtd.lib(time64.obj) 中定义
3>MSVCRT.lib(MSVCR90.dll) : error LNK2005: _malloc 已经在libcmtd.lib(dbgmalloc.obj) 中定义
3>MSVCRT.lib(MSVCR90.dll) : error LNK2005: _free 已经在libcmtd.lib(dbgfree.obj) 中定义
3>MSVCRT.lib(MSVCR90.dll) : error LNK2005: "public: virtual __thiscall std::exception::~exception(void)" (??1exception@std@@UAE@XZ) 已经在libcmtd.lib(stdexcpt.obj) 中定义
3>MSVCRT.lib(MSVCR90.dll) : error LNK2005: "public: __thiscall std::exception::exception(void)" (??0exception@std@@QAE@XZ) 已经在libcmtd.lib(stdexcpt.obj) 中定义
3>MSVCRT.lib(MSVCR90.dll) : error LNK2005: "public: __thiscall std::exception::exception(class std::exception const &)" (??0exception@std@@QAE@ABV01@@Z) 已经在libcmtd.lib(stdexcpt.obj) 中定义
3>MSVCRT.lib(MSVCR90.dll) : error LNK2005: _strchr 已经在libcmtd.lib(strchr.obj) 中定义
3>MSVCRT.lib(MSVCR90.dll) : error LNK2005: __invalid_parameter 已经在libcmtd.lib(invarg.obj) 中定义
3>MSVCRT.lib(MSVCR90.dll) : error LNK2005: "public: __thiscall std::exception::exception(char const * const &)" (??0exception@std@@QAE@ABQBD@Z) 已经在libcmtd.lib(stdexcpt.obj) 中定义
3>MSVCRT.lib(MSVCR90.dll) : error LNK2005: _memmove_s 已经在libcmtd.lib(memmove_s.obj) 中定义
3>MSVCRT.lib(MSVCR90.dll) : error LNK2005: _sprintf 已经在libcmtd.lib(sprintf.obj) 中定义
3>MSVCRT.lib(MSVCR90.dll) : error LNK2005: _sprintf_s 已经在libcmtd.lib(sprintf.obj) 中定义
3>MSVCRT.lib(MSVCR90.dll) : error LNK2005: _strpbrk 已经在libcmtd.lib(strpbrk.obj) 中定义
这里实验发现 静态库链接的 MSVCRT.dll和exe链接的MSVCRT.lib冲突,前面分析过,这两个库都是同一份代码。
最后 实验总结:
1、使用的MFC框架的exe,在共享DLL中使用MFC : exe 运行时库必须设置/MD 或者/MDd,所依赖的静态库运行时库必须是/MD 或者/MDd
2、使用的MFC框架的exe,在静态库中使用MFC : exe 运行时库必须设置/MT 或者/MTd,所依赖的静态库运行时库必须是/MT 或者/MTd
3、在共享中DLL使用MFC,运行时需要运行环境包含有MFC的动态库和C++的CRT运行时动态库,否则无法运行
4、在静态库中使用MFC,运行时不需要运行环境包含有MFC的动态库和C++的CRT运行时动态库,因为程序中已经静态链接了相关运行时库
选择2方案编译,发布的exe就能在缺少库的环境下运行了
为了发布一个静态链接的exe,我按照上面实验总结操作碰到的一个错误:
链接时仍然出现下面错:
3>LINK : error LNK2001: 无法解析的外部符号wWinMainCRTStartup
3>uafxcw.lib(tooltip.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(dockcont.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(olestrm.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>libcpmt.lib(xmbtowc.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(bardock.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(oletyplb.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(occlock.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(olefact.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(oledisp1.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(oledisp2.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(barcore.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(winctrl2.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(wingdix.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(olereg.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(winfrm.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(appinit.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(occsite.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(dlgtempl.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(docmgr.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(bartool.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(winutil.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(filecore.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
3>uafxcw.lib(filest.obj) : error LNK2001: 无法解析的外部符号__security_check_cookie
exe的链接选项里,发现忽略了特定的库:libcmt,这个库恰好是/MT所对应的一个静态库,去掉就能编译过了。要说原因估计大家已经能猜测到了,就不多说了。