VS2005连接问题: LNK2005错误 :error LNK2005: _free 已经在 libcmtd.lib(dbgheap.obj) 中定义

之前常常遇到这个警告信息,由于运行并无什么问题,因此也没深究。可是耿耿于怀那个“ 0 个错误,0 个警告”的成功提示,在网上搜了一下。原来问题出在默认库的引用选择上。

VS2008,项目——属性——配置属性——C/C++——代码生成:他有/MT,/MTd,/Md,/MDd四个选项,你必须让全部使用的库都使 用相同的配置,不然就会有相应的提示,甚至可能会出现没法解析的函数。有时咱们使用的库不是本身能够控制的,那么就只能把工程属性设置成河你使用的库相同 的选项。

错误 1 error LNK2005: _free 已经在 libcmtd.lib(dbgheap.obj) 中定义         MSVCRT.lib

错误 2 error LNK2005: _malloc 已经在 libcmtd.lib(dbgheap.obj) 中定义     MSVCRT.lib

.....

若是有一堆的重定义错误发生在同一个lib中,并且跟它冲突的也是同一个lib,那么这个两个lib的功能应该是同样的,能够2选一,只要在“忽略特定的库”内填入须要忽略的库。

项目属性-配置属性-连接器-输入-忽略特定的库:libcmtd.lib

项目属性-配置属性-常规-MFC的使用:在共享 DLL 中使用 MFC

MSVCRT.lib 和libcmt.lib的冲突仍是比较常见的。

从错误信息能够看出是msvcrt.lib和libcmt.lib库中重复定义了__isctype等符号。为何会出现这样的问题呢?这就要从这两个库的做用提及了。

msvcrt.lib是VC中的Multithreaded DLL 版本的C运行时库,而libcmt.lib是Multithreaded的运行时库。在同一个项目中,全部的源文件必须连接相同的C运行时库。若是某一文 件用了Multithreaded DLL版本,而其余文件用了Single-Threaded或者Multithreaded版本的库,也就是说用了不一样的库,就会致使这个警告的出现。

告警信息的意思咱们明白以后,就要找形成这个问题的缘由了。在项目设置中咱们能够看到当前项目使用的是Multithreaded非DLL版本的运 行时库,这说明项目中还有其余文件用到了不是这个版本的运行时库。很显然,就是openssl的静态库。查看openssl中ms下的nt.mak,咱们 能够发现静态库版本中openssl使用编译开关/MD进行编译的,也就是说openssl静态库是默认用的Multithreaded DLL 版本的C运行时库。

缘由找到了。那么解决方法,很明显有两个。总之就是将两个项目的运行时库统一。

简单的方式就是将项目的动态库修改成使用Multithreaded DLL 版本的C运行时库便可。

某些状况下你的项目可能不能改变当前的运行时库,你能够将openssl的nt.mak中的/MD开关修改成/MT而后从新编译openssl静态库就能够了。

默认库“library”与其余库的使用冲突;请使用 /NODEFAULTLIB:library LNK4098 的解决办法

您试图与不兼容的库连接。

注意

运行时库如今包含可防止混合不一样类型的指令。若是试图在同一个程序中使用不一样类型的运行时库或使用调试和非调试版本的运行时库,则将收到此警告。例如,如 果编译一个文件以使用一种运行时库,而编译另外一个文件以使用另外一种运行时库(例如单线程运行时库对多线程运行时库),并试图连接它们,则将获得此警告。应 将全部源文件编译为使用同一个运行时库。有关更多信息,请参见使用运行时库(/MD、/MT 和 /LD)编译器选项。

可使用连接器的 /VERBOSE:LIB 开关来肯定连接器搜索的库。若是收到 LNK4098,并想建立使用如单线程、非调试运行时库的可执行文件,请使用 /VERBOSE:LIB 选项肯定连接器搜索的库。连接器做为搜索的库输出的应是 LIBC.lib,而非 LIBCMT.lib、MSVCRT.lib、LIBCD.lib、LIBCMTD.lib 和 MSVCRTD.lib。对每一个要忽略的库可使用 /NODEFAULTLIB,以通知连接器忽略错误的运行时库。

下表显示根据要使用的运行时库应忽略的库。

若要使用第一行运行时库    请忽略第2行的这些库

单线程 (libc.lib)

libcmt.lib、msvcrt.lib、libcd.lib、libcmtd.lib、msvcrtd.lib

多线程 (libcmt.lib)

libc.lib、msvcrt.lib、libcd.lib、libcmtd.lib、msvcrtd.lib

使用 DLL 的多线程 (msvcrt.lib)

libc.lib、libcmt.lib、libcd.lib、libcmtd.lib、msvcrtd.lib

调试单线程 (libcd.lib)

libc.lib、libcmt.lib、msvcrt.lib、libcmtd.lib、msvcrtd.lib

调试多线程 (libcmtd.lib)

libc.lib、libcmt.lib、msvcrt.lib、libcd.lib、msvcrtd.lib

使用 DLL 的调试多线程 (msvcrtd.lib)

libc.lib、libcmt.lib、msvcrt.lib、libcd.lib、libcmtd.lib

例如,若是收到此警告,并但愿建立使用非调试、单线程版本的运行时库的可执行文件,能够将下列选项与连接器一块儿使用:

/NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib

 写进vs205\vs2008中 用分号隔开便可

 ======================================================================================================================

 

 

C2005编译出来的程序文件,采用了manifest方式来指定dll文件。对于win9八、win2000系统,把exe文件和VC的 dll链接库放到一块儿就成了。对于winxp、win2003系统就要麻烦的多了,VC的链接库默认是被放到了winsxs目录下,结果形成在这些系统上,直接拷贝exe文件,每每是不能运行(找不到msvcr80.dll、mfc80.dll文件等),或者在事件日志中报错。

解决方式:
方式1、在目标系统上安装2005版vcredist_x86.exe。

方式2、直接拷贝VS8目录下的VC \ redist \ x86 \  目录下的 Microsoft.VC80.MFC、Microsoft.VC80.CRT、Microsoft.VC80.MFCLOC几个文件夹,到exe所在的目录下,目录结构以下:
.\myapp.exe
.\myapp.dll
.\Microsoft.VC80.CRT\
.\Microsoft.VC80.MFC\
.\Microsoft.VC80.MFC\Microsoft.VC80.MFCLOC\
而后修改Microsoft.VC80.MFCLOC目录下的Microsoft.VC80.MFCLOC.manifest文件,将其中的version="8.0.50727.42",修改成version="8.0.50608.0"。


方式二的目录结构,在xp和2003下是没有问题的,可是在win98/win2000中,由于exe和dll不在同一目录下,就会出现找不到dll的问题。

有什么更好的解决呢?呵呵,国外的一个大牛(http://blog.kalmbachnet.de)找到一绝招:

方式3、
  一、首先编译myapp.exe的时候,在配置中,选择生成单独的manifest文件,如:myapp.exe.manifest。
  二、将myapp.exe、myapp.exe.manifest拷贝到一个目录下
  三、将Microsoft.VC80.MFC、Microsoft.VC80.CRT、Microsoft.VC80.MFCLOC几个目录下的文件,都拷贝到myapp.exe所在的目录下。
  四、将Microsoft.VC80.MFCLOC.manifest文件中的version="8.0.50727.42",修改成version="8.0.50608.0"。
  五、编辑myapp.exe目录下的全部 .manifest文件,将文件中的publicKey键值删除,通常是publicKeyToken="1fc8b3b9a1e18e3b"
  六、而后运行myapp.exe看看,嗯。

 

另,其余解决方法:

最近用vc2005写了一个程序,拷贝到其它机器上运行时,提示“因为应用程序配置不正确,应用程序未能启动。从新安装应用程序可能会纠正这个问题。”。

以为很奇怪,依赖的dll都有在,怎么会提示错误呢。立刻上网用这个错误查了一下,大多数人说是编译选项的问题,如下是摘自http://bbs.mscommunity.com/forums/ShowThread.aspx?PostID=52760的解答:

在项目属性-〉配置属性-〉c/c++ -〉代码生成里,有一个运行时库, 
.net 05 的默认选项是 多线程DLL,把他改为多线程便可。

后来我花了一些时间测试,发现也能够不用修改编译选项,只要将Program Files\Microsoft Visual Studio 8\VC\redist下相应平台的Microsoft.VC80.MFC.manifest和Microsoft.VC80.CRT.manifest拷贝到应用程序目录便可。若是你还用到了atl库,则还要Microsoft.VC80.ATL.manifest。

总结:解决这个问题目前我所知道的有两种方法,

1,修改项目属性,静态连接mfc库(静态连接时,会自动修改上面提到的多线程DLL为多线程)。

2,带上Microsoft.VC80.MFC.manifest和Microsoft.VC80.CRT.manifest。

 

其余人回复:

解决VC2005程序的一个运行错误“因为应用程序的配置不正确,应用程序未能启动,从新安装应用程序可能会纠正这个问题” 
是否是报这个错误啊?若是是2000系统,则会提示错误:"没有找到XXXXXX.dll,所以这个应用程序未能启动。从新安装应用程序可能会修复此问题" 
若是你没使用到C#在里面的话,有种方法能够解决,我之前也遇到过这个老是,因此写在个人博客里,文章地址为:http://www.busfly.cn/post/5.html 
在这里也简单的说下解决方法: 
工程-》属性-》配置属性-》常规-》MFC的使用,选择"在静态库中使用mfc" 
这样生成的exe文件应该就能够在其余机器上跑了。

 

=============================================================================================

 

 

首先感谢这位几仁兄的几篇博客:

  http://hi.baidu.com/fairysky/blog/item/130dda13db7b050a5aaf53be.html

  http://hi.baidu.com/fairysky/blog/item/e7a8366dbaa735f3431694c8.html

  http://www.cppblog.com/lf426/archive/2008/04/12/46885.aspx

 

 时间原因:

 有时间再来整理:有点凌乱,很差意思。

 

 感谢的XCyber回复:

  为何这么折腾呢?
  这样看来,微软发明manifest是错误的,由于你们都为运行库烦恼,真的还不如VC6,这可能吗?
其实很简单,打开你的vs,建立一个Setup and Development下的Setup Project项目,而后添加Merge Module,选择你须要的运行库,最后就是Build,生成的文件与你的程序一块儿发布就好了;
 

 

 

参考资料

一、VS2005解决"应用程序配置不正确,程序没法启动"问题

二、VS2005安装文件  "因为应用程序配置不正确,应用程序未能启动"

三、Microsoft Visual C++ 2008发布程序的部署问题

4VC编写的程序不能在其余机器上运行的解决方案

新增(先看看上面的4个连接以后,遇到问题以后再看下面的几个连接)

5关于vs2008 sp1 C++生成的 manifest中运行库版本号的问题 (推荐1)

六、在VC++2008的项目中,如何显示地指定要使用的C++库的版本? (推荐2)

七、VC9 SP1 generates manifests with the wrong version number

ps:有人认为这是一个bug,并汇报到ms网站上,但“推荐1”认为这不是一个bug

八、VC Runtime Binding...(ms的官方blog对这个问题的解释)

关于VC运行时绑定(上面连接的中文翻译)

九、部署  (C++)推荐,比较难看懂

关于连接9下几个比较有用的连接:

程序集搜索顺序英文,主要讲的是CRT、MFC等的DLL和manifest文件的部署方式

选择部署方法

使用 Program Files\Microsoft Visual Studio 8\VC\Redist目录中提供的文件将特定 Visual C++程序集做为应用程序的私有程序集安装。容许没有管理员权限的用户安装应用程序或能够经过共享运行应用程序时,建议使用这种方法。有关示例,请参见如何:使用  XCopy进行部署。(摘自:选择部署方法

 

总结以下:

使用vs2008/vs2008开发的程序有2种部署方法:共享并行程序集和私有程序集部署方法

所谓的共享并行程序集部署方法是指程序依赖的CRTMFCATLDLL和manifest文件位于目标机器上的c:\windows\winsxs目录中,发布程序的时候只须要将程序拷贝到目标机器上就能够了私有程序集部署方法指的是发布程序程序的时候,将所依赖的crt、mfc、atl的dll放在程序的当前目录下

 

对于release版程序

比较的简单的方法采用共享程序集的方式来部署,安装vcredist.exe (Microsoft Visual C++ 2008 SP1 Redistributable Package (x86)

也能够采用下面debug程序的私有程序集的部署方法

 

对于debug版本程序

◆ 若目标机器安装了VS开发环境(vs2005 sp1/vs2008 sp1),则在机器上同时也安装了共享并行程序集,包含各个版本的dll(8.0、9.0版本,位于C:\Windows\Winsxs目录下),则不需作任何的部署,直接将须要发布的程序拷贝到目标机器上就能够了,这和release版程序的发布方式是同样的

◆ 在没有安装VS开发环境(安装了vs2005 sp1/vs2008 sp1)的机器上,只能采用私有程序集的方式来部署(由于vcredist.exe只安装了release版的CRT、MFC、ATL的DLL和manifest文件,没有对应的debug版本)

已知的2种方法:(针对vs2008 sp1,安装了sp1以后,在系统上会存在两个版本的CRT、MFC、ATL的DLL:9.0.21022.89.0.30729.1

一、使当前程序的manifest文件中依赖项的版本与vc安装目录下的redist目录下的dll的版本一致,均为9.0.30729.1

方法:

a、在编译项目时定义一个符号_BIND_TO_CURRENT_VCLIBS_VERSION,该符号定义于C:\Program Files\Microsoft Visual Studio 9.0\VC\include\crtassem.h 文件中(假设VC安装在c盘),这样使得编译后的程序的manifest依赖于CRT 9.0.30729.1版本(一样的,对于MFC也应该定义一个相似的符号,你们能够本身在VC的include目录下搜索“9.0.30729.1”或“9.0.21022.8”,就能够找到对应的定义该符号的头文件)

b、经过外部工具修改生成的exe或dll中manifest文件(好像windows sdk中的mt.exe能够作到,不过关于这个工具的资料十分的少)

二、将VC安装目录下的redist目录下(C:\Program Files\Microsoft Visual Studio 9.0\VC\redist)的 Microsoft.VC90.CRT拷贝到要发布的程序的当前目录下,修改Microsoft.VC90.CRT目录中的 Microsoft.VC90.CRT.manifest文件中的版本号,改为9.0.21022.8,这样使得程序误觉得该目录下的vc的dll版本是  9.0.21022.8(实质上仍然是9.0.30729.1版本)

 

说明:

一、连接4 的说法是错误的,根据我本身的实验,若是采用私有程序集的部署方法,必须保证manifest文件中的版本号都是相等的,不存在要发布的程序的manifest文件中的版本号大于等于依赖项(CRT、MFC、ATL的dll)的版本号的说法

二、采用共享并行程序集部署方式发布的程序,会自动根据所谓的“policy”(位于C:\WINDOWS\WinSxS\Policies目录下)进行跳转(由低版本号向高版本号跳转);例如程序中的manifest的版本号为9.0.21022.8而实际上程序是用vc2008 sp1编译的(版本号为9.0.30729.1,在程序实际执行的时候,会根据

x86_policy.9.0.Microsoft.VC90.DebugCRT_1fc8b3b9a1e18e3b_x-ww_037be232目录下的9.0.30729.1.policy文件能够用记事本打开该文件中的内容选择9.0.30729.1版本的debugCRT

 

 

我我的推荐的阅读顺序:① 先看前面的4个连接,大体有点印象,知道什么是manifest、如何查看manifest文件的内容(能力强的话,也能够本身编写manifest文件)、在vc中如何查看编译过程当中生成的manifest文件内容、知道C:\WINDOWS\WinSxS\目录是干什么的、知道vcredist.exe这个程序; ②  再尝试着看看连接七、八、9,这些连接的文章内容十分的晦涩,有的仍是英文的,须要有点耐心看; ③ 最后仔细的看看链接五、6,并多多试验(特别推荐连接5,这个连接中的内容十分的详细)

 

 

 

 

vc2005/vc2008采用了新的程序部署技术(manifest清单文件),manifest清单文件实际上相似于咱们经常使用的makefile文件,它定义了程序运行的依赖关系(程序运行所须要的dll库的名称、版本等)。

程序运行,首先根据manifest清单文件(这个文件能够嵌入到exe或dll中,或者单独生成外部文件,能够经过vc2005/vc2008的编译选项控制:工程“属性”->“配置属性”->“清单工具”->“输入输出”->“嵌入清单文件”,选择“是”或“否”来控制)来查找程序运行须要的dll库的名称、版本等,若是所在的系统中没有程序运行所须要的dll库和相应的manifest清单文件,则弹出“应用程序配置不正确,程序没法启动”对话框。

 

另 外要注意,因为vc2005/vc2008与.net集成,致使出现一个新的概念:在.net中,将exe、dll都当作“程序集 (assemble)”,每一个程序集(assemble)都附带有一个manifest清单文件,所以使得vc2005/vc2008的CRT(C 运行时库)、MFC、ATL等dll库都附带有一个manifest清单文件。

归 根结底是因为老版本的系统没有咱们开发的程序运行所须要的基本运行时库(2k、xp系统只有vc6的一些dll库,而没有vc200五、vc2008所需 要的dll库以及相应的manifest清单文件,而在vista系统或者即将到来的windows 7系统上则包含有vc200五、vc2008的dll库和manifest清单文件)

ps:上面的那段话说的有点幼稚和简单了,这里涉及到不少的问题:程序的升级更新、vs的补丁、库的版本问题等等,不是简单的拷贝、粘贴就能解决的。。。

 

 

 

 

举个例子:(在XP SP3系统下)

 

使用vc2008 express sp1版(没有mfc、atl),新建一个“HelloWorld”的“win32控制台应用程序”工程,在release下编译,此时默认的编译选项:(在这里咱们只关注与咱们的问题相关的几个选项)

一、工程“属性”->“配置属性”->“c/c++”->“代码生成”->“运行库”

默认选项为/MD(release)、/MDd(debug),对这几个编译选项不清楚的能够参见: VC运行库版本不一样致使连接.LIB静态库时发生重复定义问题的一个案例分析和总结

二、工程“属性”->“配置属性”->“清单工具”->“输入输出”->“嵌入清单文件”

默认选项为“是”(表示将manifest清单文件嵌入到程序中);固然,咱们也能够选择“否”,从而单独生成一个manifest清单文件,不过这会增长没必要要的依赖项,所以不建议选择“否”。

编译->连接以后在“ HelloWorld ”工程的release或debug目录下,咱们可以看到一个HelloWorld.exe.intermediate.manifest清单文件(根据编译选项,见上,vc2008将manifest清单文件嵌入到了exe程序中,HelloWorld.exe.intermediate.manifest清单文件是一个临时文件,但它的内容与嵌入到exe程序的manifest文件是同样的),用文本编辑器打开该文件(用“记事本”也行,不过格式太乱,看不清楚内容,推荐使用vim或其它的文本编辑器查看),大体内容以下:

ps:在网上看到另外的一个方法,用记事本打开exe或dll程序,查看嵌入到exe或dll中的manifest清单文件,方法:“打开记事本,而后将exe或dll拖入到记事本中,固然了,确定会出现大段的乱码,不要紧,直接日后看,就能发现相似于下面的内容的部分

XML语言HelloWorld.exe.intermediate.manifest

01 <?xml version='1.0' encoding='UTF-8' standalone='yes'?>
02 <assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
03 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
04      <security>
05        <requestedPrivileges>
06          <requestedExecutionLevel level='asInvoker' uiAccess='false' />
07        </requestedPrivileges>
08      </security>
09 </trustInfo>
10 <dependency>
11      <dependentAssembly>
12        <assemblyIdentity type='win32' name='Microsoft.VC90.CRT' version='9.0.21022.8'processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
13      </dependentAssembly>
14 </dependency>
15 </assembly>

咱们重点查看红色部分,这说明编译后的exe程序依赖于vc90(也即vc2008)的CRT(C运行时库),版本9.0.210022.8(这是因为使用/MD选项,程序动态的依赖于CRT,若是使用/MT选项,则会将CRT静态连接到程序中,固然,这会使程序的尺寸急剧的增加,大概有10倍的大小差距)

当exe程序执行时,它会根据嵌入的manifest文件查找相应的依赖项,在这个HelloWorld.exe程序中,它依赖于vc90 CRT,所以它会在“C:\WINDOWS\WinSxS”和“当前目录”下查找相应的dll库以及manifest文件,(这里指的是xp系统,不考虑vista系统,具体的参见:程序集搜索顺序)

在个人机器上有2个版本的vc90 CRT(因为安装了vc2008 express sp1)

vc90 CRT的dll库位于9.0.21022.8版本)“C:\WINDOWS\WinSxS\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375”

相应的manifest文件则位于“C:\WINDOWS\WinSxS\Manifests\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_d08d0375.manifest”

vc90 CRT的dll库位于9.0.30729版本)“C:\WINDOWS\WinSxS\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.30729.1_x-ww_6f74963e”

相应的manifest文件则位于“C:\WINDOWS\WinSxS\Manifests\x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.30729.1_x-ww_6f74963e.manifest”

在这里咱们就有一个疑问了,咱们的开发环境是vc2008 express sp1,那么咱们的程序连接的CRT版本应该是9.0.30729版本的啊?这个不是我瞎说的,你们能够用dependency walker来查看程序实际连接的DLL版本),为何在manifest文件中依赖的CRT倒是9.0.21022.8版本的?  这里就涉及到一个新的名词“policy ",操做系统会根据C:\WINDOWS\WinSxS\Policies \x86_policy.9.0.Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_x-ww_b7353f75 \9.0.30729.1.policy文件的内容,进行dll版本的跳转(重点看深蓝斜体字部分)从而选择了9.0.30729版本vc90 CRT (这个所谓的“policy跳转”是道听途说来的,具体的英文资料藏在microsoft的什么地方我就不得而知了。里面夹带了一些我本身的主观猜想,否则的话,没有办法解释manifest版本号9.0.21022.8是,而实际连接的dll的版本号倒是9.0.30729

 

XML语言9.0.30729.1.policy
01  <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
02  <!-- Copyright (c) Microsoft Corporation. All rights reserved. -->
03  <assembly  xmlns= "urn:schemas-microsoft-com:asm.v1"  manifestVersion= "1.0" >
04      <assemblyIdentity  type= "win32-policy"  name= "policy.9.0.Microsoft.VC90.CRT" version= "9.0.30729.1"  processorArchitecture= "x86"  publicKeyToken= "1fc8b3b9a1e18e3b" />
05      <dependency>
06          <dependentAssembly>
07              <assemblyIdentity  type= "win32"  name= "Microsoft.VC90.CRT"  processorArchitecture= "x86" publicKeyToken= "1fc8b3b9a1e18e3b" />
08             <bindingRedirect oldVersion="9.0.20718.0-9.0.21022.8"newVersion="9.0.30729.1"/>
09             <bindingRedirect oldVersion="9.0.30201.0-9.0.30729.1"newVersion="9.0.30729.1"/>

10          </dependentAssembly>
11      </dependency>
12  </assembly>

 

 

 

若是咱们将这个HelloWorld.exe拷贝到其它的机器上(没有安装vc2008 sp1Microsoft Visual C++ 2008 SP1Redistributable Package (x86)),则程序由于没能找到vc90 CRT,而不能运行,弹出“应用程序配置不正确,程序没法启动”对话框。

根据参考资料的文章中的内容,对于release版程序,有一个简单的办法就是安装vcredist_x86.exe”,文件大小4M左右,自动安装在“C:\WINDOWS\WinSxS目录下,包含了CRT、MFC、ATL等库的dll和manifest清单文件;整个安装时间不到1分钟。

若是机器上安装了vc2005/vc2008,则会自动的安装vcredist_x86.exe程序,安装后在“控制面板”->“添加删除程序”中有一项“Microsoft Visual c++ 2008 Redistributable - x86 9.0.3.729”(我安装的是Microsoft Visual C++ 2008 SP1 Redistributable Package (x86) 版本)

注意:要根据编译器版本以及vc2005/vc2008是否安装了sp1补丁进行选择对应的vcredist.exe版本

 

 

上述的解决办法我称之为共享程序集部署方法,一样的咱们也能够采用私有程序集的部署方式来发布程序

Helloworld例子的私有程序集的部署方法:(针对release版本,仍然是采用上面的例子

,采用参考资料中提到的第2中私有程序集部署方法:将Microsoft.VC90.CRT目录下的manifest文件的版本号修改成9.0.21022.8

一、将编译后的程序拷贝到一个目录下,假定为d:\helloworld

二、将vc安装目录下的redist\x86目录下的Microsoft.VC90.CRT目录拷贝到d:\helloworld(假定vs安装在C:\Program Files\Microsoft Visual Studio 9.0,则vc安装目录为C:\Program Files\Microsoft Visual Studio 9.0\VC)

三、将Microsoft.VC90.CRT目录下的manifest文件的版本号修改成9.0.21022.8用记事本打开修改

最终发布程序的目录结构

D:\helloworld

      |--helloworld.exe

      |--Microsoft.VC90.CRT

                     |--Microsoft.VC90.CRT.manifest

                    |--msvcm90.dll

                     |--msvcp90.dll

                    |--msvcr90.dll

这个时候,程序的manifest文件(已经内嵌到exe中了)依赖的vc90 CRT的版本号和Microsoft.VC90.CRT.manifest文件的版本号对应一致,都是9.0.21022.8可是要注意的是,咱们的helloworld程序实际上依赖的vc90 CRT版本是9.0.30729版本这里只是采用了一种欺骗的方法,由于咱们编译时连接的CRT的版本是9.0.30729版本)

 

例子是针对release版,对于debug版本必需要采用私有程序的部署方法,这是拷贝的目录就不是release版的Microsoft.VC90.CRT了,而是Microsoft.VC90.DebugCRT,方法同release版的是同样的 

 

 

 Microsoft Visual C++ 2008 发布程序的部署问题 生成Microsoft.VC90.CRT文件目录结构:

VC2005和VC2008编译出来的程序放到别人的电脑上为何有可能没法运行呢?
        这个问题无数人在问,可是很遗憾,没有人给出完整的解释和完美的解决方案。其实我也只有一台电脑,并且装了VC了,这个问题必需要台没有装这类软件的电脑 才容易去分析。感谢那些为了测试我小程序的朋友,是大家一次次在如此恶劣的网络速度下收取我一次次修改的dll包和部署文件,才让这个问题的完美解决方案 浮出水面。这里就把个人经验给你们分享吧。

1:Microsoft Visual C++ 2008 Express Edition能够发布软件吗?

        能!
        不少人说,由于是Express版,不是Studio,因此只是用来练习语言的,不能发布软件——错!
        除了没有MFC和ATL,基本上跟 .net 版本是同样的。发布出来的,是完整的可执行文件。

2:VC 2008 (2005) 发布出来的程序必须附带上他们特定的dll文件吗?

        不必定。
        若是目标系统是个常常升级的系统,微软已经为其打上了所须要的dll文件补丁了,不须要在软件包里面附加特定的dll文件。特别在Vista系统中,你更是不须要VC8和VC9的dll文件。可是在一些老版本的系统中,这些文件就是必须的。

3:VC2008和VC2005特定的dll文件是哪些?

VC8: msvcm80.dll, msvcp80.dll, msvcr80.dll
VC9: msvcm90.dll, msvcp90.dll, msvcr90.dll

4:如何部署文件?

首先,请选择release版本;在生成可执行文件(exe文件)的时候,会获得相应的部署文件(manifest文件)。
好比,获得a.exe文件,就会同时生成a.exe.intermediate.manifest文件。请将这2个文件放在同一文件夹下。
而后,你须要VC8和VC9的部署文件:Microsoft.VC80.CRT.manifest和Microsoft.VC90.CRT.manifest。
请到你的VC安装目录下寻找,好比:
C:\Program Files\Microsoft Visual Studio 9.0\VC\redist\x86\Microsoft.VC90.CRT
我这里也把2个部署文件直接贴出来,没装的直接用就是了:
Microsoft.VC80.CRT.manifest
 

<? xml version="1.0" encoding="UTF-8" standalone="yes" ?>
< assembly  xmlns ="urn:schemas-microsoft-com:asm.v1"  manifestVersion ="1.0" >
< noInheritable ></ noInheritable >
< assemblyIdentity  type ="win32"  name ="Microsoft.VC80.CRT"  version ="8.0.50727.762"  processorArchitecture ="x86"  publicKeyToken ="1fc8b3b9a1e18e3b" ></ assemblyIdentity >
< file  name ="msvcr80.dll"  />  < file  name ="msvcp80.dll"  />  < file  name ="msvcm80.dll"  />
</ assembly >
Microsoft.VC90.CRT.manifest
<? xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!--  Copyright (c) Microsoft Corporation.  All rights reserved.  -->
< assembly  xmlns ="urn:schemas-microsoft-com:asm.v1"  manifestVersion ="1.0" >
    
< noInheritable />
    
< assemblyIdentity
        
type ="win32"
        name
="Microsoft.VC90.CRT"
        version
="9.0.21022.8"
        processorArchitecture
="x86"
        publicKeyToken
="1fc8b3b9a1e18e3b"
    
/>
    
< file  name ="msvcr90.dll"  />  < file  name ="msvcp90.dll"  />  < file  name ="msvcm90.dll"  />
</ assembly >
而后将VC8的3个dll以及这个manifest装到一个文件夹里,并将文件夹命名为Microsoft.VC80.CRT。
一样将VC9的3个dll以及这个manifest装到一个文件夹里,并将文件夹命名为Microsoft.VC90.CRT。
将这2个文件夹放到与exe文件(及其部署文件)所在目录下就OK了。
至于为何VC9编译的程序要用VC8的dll,你们能够看看我例程部署文件:
<? xml version='1.0' encoding='UTF-8' standalone='yes' ?>
< assembly  xmlns ='urn:schemas-microsoft-com:asm.v1'  manifestVersion ='1.0'>
  
<trustInfo xmlns ="urn:schemas-microsoft-com:asm.v3" >
    
< security >
      
< requestedPrivileges >
        
< requestedExecutionLevel  level ='asInvoker'  uiAccess ='false'  />
      
</ requestedPrivileges >
    
</ security >
  
</ trustInfo >
  
< dependency >
    
< dependentAssembly >
      
< assemblyIdentity  type ='win32'  name ='Microsoft.VC90.CRT'  version ='9.0.21022.8'  processorArchitecture ='x86'  publicKeyToken ='1fc8b3b9a1e18e3b'  />
    
</ dependentAssembly >
  
</ dependency >
  
< dependency >
    
< dependentAssembly >
      
< assemblyIdentity  type ='win32'  name ='Microsoft.VC80.CRT'  version ='8.0.50727.762'  processorArchitecture ='x86'  publicKeyToken ='1fc8b3b9a1e18e3b'  />
    
</ dependentAssembly >
  
</ dependency >
</ assembly >
VC 2008生成出来就须要VC90和VC80的CRT,咱们能有什么脾气呢……
也就是说,还别管你exe文件多大,要保证正常运行,咱们须要首先拷贝这8个文件……
MinGW(gcc)编译的就没这些麻烦。因此,我如今都是用两个编译器编译两个exe以供发布了。  

=====================================================================================================

 

 

1、MD(d)、MT(d)编译选项的区别

一、编译选项的位置

以VS2005为例,这样子打开:

1)         打开项目的Property Pages对话框

2)         点击左侧C/C++节

3)         点击Code Generation节

4)         右侧第六行Runtime Library项目

二、各个设置选项表明的含义

编译选项

包含

静态连接的lib

说明

/MD

_MT、_DLL

MSVCRT.lib

多线程、Release、DLL版本的运行时库

/MDd

_DEBUG、_MT、_DLL

MSVCRTD.lib

多线程、Debug、DLL版本的运行时库

/MT

_MT

LIBCMT.lib

多线程、Release版本的运行时库

/MTd

_DEBUG、_MT

LIBCMTD.lib

多线程、Debug版本的运行时库

 

简单的说:

(1)/MD,表示运行时库由操做系统提供一个DLL,程序里不集成。

(2)/MT,表示运行时库由程序集成。

 

2、/MD、/MT的选择

      一、为何选择/MD,不选/MT?

         (1)程序就不须要静态连接运行时库,能够减少软件的大小;

         (2)全部的模块都采用/MD,使用的是同一个堆,不存在A堆申请,B堆释放的问题。

      二、为何选择/MT,不选择/MD?

         (1)有些系统可能没有程序所须要版本的运行时库,程序必须把运行时库静态连接上。

(2)减小模块对外界的依赖。

      三、多个模块,必须选择相同的运行时库。

3、选择/MT须要解决的堆空间释放问题

         不一样的模块各自有一份C运行时库代码、或者根本没有C运行时库,致使了各个模块会有各自的堆。若是在A堆中申请空间,到B堆中释放就会有崩溃,在模块A申请的空间,必须在模块A中释放。

         附录的DLL以及DLLUser代码,以STL的string为例,经过修改编译选项验证了这个问题。string在赋值的时候须要释放掉原来的空间,而后再申请新的空间存储新的内容。

 

4、选择/MD须要注意多个模块使用不一样版本运行时库的问题

     (2012-9-17补充)

     多个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(会致使膨胀?)。

4、参考资料

一、微软关于MT、MD等的详细介绍

http://msdn.microsoft.com/en-us/library/2kzt1wy3(v=VS.71).aspx

二、不要出现A模块申请,B模块释放的状况

http://www.cnblogs.com/minggoddess/archive/2010/12/15/1907179.html

三、运行时库有哪些版本

http://www.cppblog.com/MichaelLiu/articles/10607.html

四、CSDN上关于堆空间释放的讨论

http://topic.csdn.net/t/20010112/09/57983.html

http://topic.csdn.net/t/20031009/17/2338051.html

http://topic.csdn.net/u/20090502/00/bf1602e3-ddf5-49b0-af81-8a23383f9ddc.html

http://blog.csdn.net/blz_wowar/article/details/2176536

五、不一样模块不一样的堆

http://www.cnblogs.com/WengYB/archive/2011/08/18/2144727.html

 

六、由于运行时库版本问题致使加载失败的分享

http://blog.csdn.net/dev_yarin/article/details/6768373

http://blog.163.com/henan_lujun/blog/static/19538333200611485511640/

 

附录:

下载地址:http://files.cnblogs.com/cswuyg/Test_MD_and_MT.rar