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文件。对于win98、win2000系统,把exe文件和VC的 dll连接库放到一起就成了。对于winxp、win2003系统就要麻烦的多了,VC的连接库默认是被放到了winsxs目录下,结果造成在这些系统上,直接拷贝exe文件,往往是不能运行(找不到msvcr80.dll、mfc80.dll文件等),或者在事件日志中报错。

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

方式二、直接拷贝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)找到一绝招:

方式三、
  1、首先编译myapp.exe的时候,在配置中,选择生成单独的manifest文件,如:myapp.exe.manifest。
  2、将myapp.exe、myapp.exe.manifest拷贝到一个目录下
  3、将Microsoft.VC80.MFC、Microsoft.VC80.CRT、Microsoft.VC80.MFCLOC几个目录下的文件,都拷贝到myapp.exe所在的目录下。
  4、将Microsoft.VC80.MFCLOC.manifest文件中的version="8.0.50727.42",修改为version="8.0.50608.0"。
  5、编辑myapp.exe目录下的所有 .manifest文件,将文件中的publicKey键值删除,一般是publicKeyToken="1fc8b3b9a1e18e3b"
  6、然后运行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,生成的文件与你的程序一起发布就行了;
 

 

 

参考资料

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

2、VS2005安装文件 "由于应用程序配置不正确,应用程序未能启动"

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

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

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

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

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

7、VC9 SP1 generates manifests with the wrong version number

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

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

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

9、部署 (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

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可以做到,不过关于这个工具的资料十分的少)

2、将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版本)

 

说明:

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

2、采用共享并行程序集部署方式发布的程序,会自动根据所谓的“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这个程序; ② 再尝试着看看链接7、8、9,这些链接的文章内容十分的晦涩,有的还是英文的,需要有点耐心看; ③ 最后仔细的看看链接5、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库,而没有vc2005、vc2008所需 要的dll库以及相应的manifest清单文件,而在vista系统或者即将到来的windows 7系统上则包含有vc2005、vc2008的dll库和manifest清单文件)

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

 

 

 

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

 

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

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

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

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

默认选项为“是”(表示将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

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

2、将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)

3、将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以供发布了。  

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


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

1、编译选项的位置

以VS2005为例,这样子打开:

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

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

3)         点击Code Generation节

4)         右侧第六行Runtime Library项目

2、各个设置选项代表的含义

编译选项

包含

静态链接的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,表示运行时库由程序集成。

 

二、/MD、/MT的选择

      1、为什么选择/MD,不选/MT?

         (1)程序就不需要静态链接运行时库,可以减小软件的大小;

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

      2、为什么选择/MT,不选择/MD?

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

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

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

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

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

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

 

四、选择/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(会导致膨胀?)。

四、参考资料

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

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

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

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

3、运行时库有哪些版本

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

4、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

5、不同模块不同的堆

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

 

6、因为运行时库版本问题导致加载失败的分享

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