关于动态链接库编译顺序导致 " _DllMain@12 已经在 LIBCMTD.lib(dllmain.obj) 中定义 " 问题的解决方法

时间:2021-04-17 15:51:14
1. 我用 VC 2003 编译一个关于网络发包的动态链接库, 其中包含文件如下:

   - 源文件
     - base64.cpp
     - CWSTAPI.cpp
     - IniFile.cpp
     - md5.cpp
   - 头文件
     - base64.h
     - CWSTAPI.h
     - IniFile.h
     - md5.h

   其中 CWSTAPI.h 和 CWSTAPI.cpp 文件中包含动态链接库导出函数的声明和定义.

2. 直接生成解决方案会产生如下错误 : 
   LINK : LNK6004: 没有找到 ..\..\bin/CWSTAPI.dll 或上一个增量链接没有生成它;正在执行完全链接
   nafxcwd.lib(dllmodul.obj) : error LNK2005: _DllMain@12 已经在 LIBCMTD.lib(dllmain.obj) 中定义
   nafxcwd.lib(afxmem.obj) : error LNK2005: "void * __cdecl operator new(unsigned int)" (??2@YAPAXI@Z) 已经在 libcpmtd.lib(newop.obj) 中定义
   nafxcwd.lib(afxmem.obj) : error LNK2005: "void __cdecl operator delete(void *)" (??3@YAXPAX@Z) 已经在 LIBCMTD.lib(dbgdel.obj) 中定义
   nafxcwd.lib(afxmem.obj) : error LNK2005: "void * __cdecl operator new[](unsigned int)" (??_U@YAPAXI@Z) 已经在 libcpmtd.lib(newaop.obj) 中定义
   nafxcwd.lib(afxmem.obj) : error LNK2005: "void __cdecl operator delete[](void *)" (??_V@YAXPAX@Z) 已经在 LIBCMTD.lib(delete2.obj) 中定义
   nafxcwd.lib(dllmodul.obj) : warning LNK4006: _DllMain@12 已在 LIBCMTD.lib(dllmain.obj) 中定义;已忽略第二个定义
   nafxcwd.lib(afxmem.obj) : warning LNK4006: "void * __cdecl operator new(unsigned int)" (??2@YAPAXI@Z) 已在 libcpmtd.lib(newop.obj) 中定义;已忽略第二个定义
   nafxcwd.lib(afxmem.obj) : warning LNK4006: "void __cdecl operator delete(void *)" (??3@YAXPAX@Z) 已在 LIBCMTD.lib(dbgdel.obj) 中定义;已忽略第二个定义
   nafxcwd.lib(afxmem.obj) : warning LNK4006: "void * __cdecl operator new[](unsigned int)" (??_U@YAPAXI@Z) 已在 libcpmtd.lib(newaop.obj) 中定义;已忽略第二个定义
   nafxcwd.lib(afxmem.obj) : warning LNK4006: "void __cdecl operator delete[](void *)" (??_V@YAXPAX@Z) 已在 LIBCMTD.lib(delete2.obj) 中定义;已忽略第二个定义
     正在创建库 ..\..\bin/CWSTAPI.lib 和对象 ..\..\bin/CWSTAPI.exp
   ..\..\bin/CWSTAPI.dll : fatal error LNK1169: 找到一个或多个多重定义的符号

   后来发现 .cpp 文件的编译顺序如下:

   正在编译...
   md5.cpp
   IniFile.cpp
   CWSTAPI.cpp
   base64.cpp

3. 以前我没有添加 base64.h 和 base64.cpp 文件, 可以正常编译, 难道是和这个有关, 于是我想办法让 base64.cpp 在 CWSTAPI.cpp 之前编译, 我共想了三种方法 : 
(1) 先将 base64.cpp 文件从项目中移除, 接着又加载进来, 那么 "解决方案资源管理器" 中文件的顺序为 : 

    - 源文件
      - CWSTAPI.cpp
      - IniFile.cpp
      - md5.cpp
      - base64.cpp

    这样我点击菜单上 生成 -> 重新生成解决方案, 果然编译顺序为 : 

    正在编译...
    base64.cpp
    md5.cpp
    IniFile.cpp
    CWSTAPI.cpp

    注意不能使用生成解决方案, 那样可能 .cpp 文件不会重新编译.

    但是这样会发生问题, 因为你关掉这个解决方案, 重新打开这个解决方案时, 文件又按字母顺序在 "解决方案资源管理器" 中显示, 这就以为着生成解决方案编译 .cpp 文件时是按照 .cpp 文件的字母顺序来编译的.

(2) 重命名 .cpp 文件的名称
    既然 .cpp 文件是根据字母顺序排序的, 编译时也遵循这个顺序的反序, 那么只要保证 CWSTAPI.cpp 文件最后编译就可以了, 而 base64.cpp 文件排在 CWSTAPI.cpp 文件的前面, 那么将 base64.cpp 文件重命名为 ZBase64.cpp, 并相应地将 base64.h 重命名为 ZBase64.h, 这样就改变了编译的顺序. 这样做有个不好的地方就是为了达到调整编译顺序的目的而去改变 .cpp 文件的名称, 这样的命名往往是违心的. 

(3) 将 .cpp 移到其他文件夹中
    VC 默认将 .cpp 文件放在 源文件中, 将 .h 文件放在 头文件中.
    建立一个文件夹 Include, 将除了 CWSTAPI.h, CWSTAPI.cpp 文件的所有 .h 和 .cpp 文件都放在这个文件夹中, 即

    - 源文件
      - CWSTAPI.cpp
    - 头文件
      - CWSTAPI.h
    - 资源文件
    - Include
      - base64.cpp
      - base64.h
      - IniFile.cpp
      - IniFile.h
      - md5.cpp
      - md5.h

    这个顺序需要重新打开解决方案才能看到, 这样编译顺序为 : 

    正在编译...
    md5.cpp
    IniFile.cpp
    base64.cpp
    CWSTAPI.cpp

4. 这个问题足足困扰了我大半天, 最后终于找到这几种解决方法, 并且分享给大家, 最终我采用了最后一种方法. 这些方法肯定不是最好的, 所以我想征集一下大家的方法. 

16 个解决方案

#1


顶楼主

#2


我基本上也是换文件名、位置,希望找到更好的方法。
UP.

#3


up

#4


up

#5


#6


怎么加分啊?

#7


重命名



顶下先 



有点累了  


先 沙发一会~~~~~~~~~~~~~~·

#8


先告诉你怎么加分,再看题,第二天可以给帖子加分,并且限制只能加100分

#9


解决了问题就行,如果是我,我首先想到的肯定是第2种解决办法,不过我倾向于最后一种解决办法

#10


赞!

#11


写得太多,没看仔细.我刚遇到过类似问题.链接时出错,再链接一遍.就可以.原因是在".h"中定义全局变量引起的.将这个全局变量放到类中.好像用extern也可以.

#12


第一次发帖, 给大家都送分啊.

#13


mark

#14


还有一个,如果在ocx工程中使用CString也会触发2005错误,只能使用其他类型

#15


狂顶楼主。太有才了。这个问题困扰我了很久,调单线程为多线程,也不能解决。一试楼主的方法,立竿见影。很好。希望大家也都多留意其他解决办法,都分享一下。

#16


直接修改.vcproj,把base64.cpp和base64.h放最后面,就好了,我的老天。

#1


顶楼主

#2


我基本上也是换文件名、位置,希望找到更好的方法。
UP.

#3


up

#4


up

#5


#6


怎么加分啊?

#7


重命名



顶下先 



有点累了  


先 沙发一会~~~~~~~~~~~~~~·

#8


先告诉你怎么加分,再看题,第二天可以给帖子加分,并且限制只能加100分

#9


解决了问题就行,如果是我,我首先想到的肯定是第2种解决办法,不过我倾向于最后一种解决办法

#10


赞!

#11


写得太多,没看仔细.我刚遇到过类似问题.链接时出错,再链接一遍.就可以.原因是在".h"中定义全局变量引起的.将这个全局变量放到类中.好像用extern也可以.

#12


第一次发帖, 给大家都送分啊.

#13


mark

#14


还有一个,如果在ocx工程中使用CString也会触发2005错误,只能使用其他类型

#15


狂顶楼主。太有才了。这个问题困扰我了很久,调单线程为多线程,也不能解决。一试楼主的方法,立竿见影。很好。希望大家也都多留意其他解决办法,都分享一下。

#16


直接修改.vcproj,把base64.cpp和base64.h放最后面,就好了,我的老天。