MFC程序运行时缺少库问题深入研究

问题:


发布的程序在系统中跑不起来

背景:

工作中开发的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模式                                                               

图一 exe MFC的使用方式 在共享DLL中使用


图2 C/C++引用的CRT类型  /MDd


图三 exe 需要依赖静态库 C/C++引用的CRT类型 /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和crt的运行库,报错


上图 这个就是其他机器上缺少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模式


图五 exe设置成MFC在静态库中使用

再次编译:

程序直接放到测试环境中可以运行---------------说明这就是静态库链接方式,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 在链接器中忽略了libmt库

exe的链接选项里,发现忽略了特定的库:libcmt,这个库恰好是/MT所对应的一个静态库,去掉就能编译过了。要说原因估计大家已经能猜测到了,就不多说了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容