同时生成 Release版和Debug版DLL的方法
warning LNK4070的解决办法
重命名了一个MFC常规DLL的工程文件(VS C++ 2005编译环境),结果在编译时出现这样的警告:1>B.exp : warning LNK4070: .EXP 中的 /OUT:A.dll 指令与输出文件名"../outdir/Debug/B.dll"不同;忽略指令(这里假设原来的工程文件名叫A.vcproj,改名后叫B.vcproj)。
后来我发现虽然输出为B.dll,但是对应的静态库B.lib被其它工程以隐式链接的方式调用时,使用的还是A.dll(这个可以使用Dependcies工具来查看),这样导致往往其它动态库不能加载成功。这下我不能把它仅仅当做warning而弃之不管了,于是上网查资料解决这个warning。查完资料,再结合自己的思考,大致明白了造成warning的原因。
原来是虽然我修改了工程名,但是没有修改这个工程的def文件中LIBRARY字段的值,造成工程的输出文件和def文件的LIBRARY字段的值不一样。比如我把A.vcproj修改为B.vcproj,但在def文件还是LIBRARY "A"。这时只需将def文件中的LIBRARY字段修改为:LIBRARY "B"。这样就能完全消除这个警告。而被别的库以隐式链接调用也是以B.dll面目出现的。
VC6.0程序移到VS2010出现警告之warning LNK4017:
从VC 6.0的程序中看到.def文件为:
编译时总是出现警告:
\EvaDataGr.def(5): warning LNK4017: DESCRIPTION 语句不支持目标平台;已忽略
找了很久也没找到相应的解决办法,查到一篇博客中的.def文件定义后,暂用删除DESCRIPTION 'DataTypeWm Windows Dynamic Link Library'这句话的办法编译。
.def文件定义
DLL中导出函数的声明有两种方式:一种为在函数声明中加上__declspec(dllexport),这里不再举例说明;另外一种方式是采用模块定义(.def) 文件声明,.def文件为链接器提供了有关被链接程序的导出、属性及其他方面的信息。
首先创建一个DLL程序,.cpp中
[cpp] view plain copy
int __stdcall Add(int numa, int numb)
{
return (numa + numb);
}
int __stdcall Sub(int numa, int numb)
{
return (numa - numb);
}
然后创建一个.def的文件,在里面加上:
;DllTestDef.lib : 导出DLL函数
;作者:----
LIBRARY DllTestDef
EXPORTS
Add @ 1
Sub @ 2
最后创建一个测试程序:.cpp文件如下:
[cpp] view plain copy
#include <iostream>
#include <windows.h>
using namespace std;
typedef int (__stdcall *FUN)(int, int);
HINSTANCE hInstance;
FUN fun;
int main()
{
hInstance = LoadLibrary("DLLTestDef.dll");
if(!hInstance)
cout << "Not Find this Dll" << endl;
fun = (FUN)GetProcAddress(hInstance, MAKEINTRESOURCE(1));
if (!fun)
{
cout << "not find this fun" << endl;
}
cout << fun(1, 2) << endl;
FreeLibrary(hInstance);
return 0;
}
说明:
.def文件的规则为:
- LIBRARY语句说明.def文件相应的DLL;
- EXPORTS语句后列出要导出函数的名称。可以在.def文件中的导出函数名后加@n,表示要导出函数的序号为n(在进行函数调用时,这个序号将发挥其作用);
- .def 文件中的注释由每个注释行开始处的分号(;) 指定,且注释不能与语句共享一行。
如果导出 C++ 文件中的函数,必须将修饰名放到.def 文件中,或者通过使用外部“C”定义具有标准 C 链接的导出函数。如果需要将修饰名放到.def 文件中,则可以通过使用 DUMPBIN 工具或 /MAP 链接器选项来获取修饰名。请注意,编译器产生的修饰名是编译器特定的。如果将 Visual C++ 编译器产生的修饰名放到 .def 文件中,则链接到 DLL 的应用程序必须也是用相同版本的 Visual C++ 生成的,这样调用应用程序中的修饰名才能与 DLL 的 .def 文件中的导出名相匹配。
如果生成扩展 DLL 并使用 .def 文件导出,则将下列代码放在包含导出类的头文件的开头和结尾:
#undef AFX_DATA#define AFX_DATA AFX_EXT_DATA// <body of your header file>#undef AFX_DATA#define AFX_DATA
这些代码行确保内部使用的 MFC 变量或添加到类的变量是从扩展 DLL 导出(或导入)的。例如,当使用 DECLARE_DYNAMIC 派生类时,该宏扩展以将 CRuntimeClass 成员变量添加到类。省去这四行代码可能会导致不能正确编译或链接 DLL,或在客户端应用程序链接到 DLL 时导致错误。
当生成 DLL 时,链接器使用 .def 文件创建导出 (.exp) 文件和导入库 (.lib) 文件。然后,链接器使用导出文件生成 DLL 文件。隐式链接到 DLL 的可执行文件在生成时链接到导入库。
请注意,MFC 本身使用 .def 文件从 MFCx0.dll 导出函数和类。
同时生成 Release版和Debug版DLL的方法
warning LNK4070: /OUT:dll.dll directive in .EXP ..
我在DLL项目中遇到了,因为要对Debug版和Release版分别指定不同的输出文件名。对Debug版,我指定输出文件为dllD.dll,对Release版指定为dllR.dll。
解决方法有两个:
(1)删掉自动生成的dll.DEF文件,在代码中使用_declspec(dllexport)导出函数;
(2)删掉dll.DEF文件中LIBRARY字段后面双引号及其内部的库名即可,也可以将其改掉。如改为:
; dll.def : Declares the module parameters for the DLL.
LIBRARY "dllD"
DESCRIPTION 'dll Windows Dynamic Link Library'
EXPORTS
; Explicit exports can go here
******************************************************************************************
还有更好方法:
- 生成两份DEF文件:xx.def //Release版本;xxD.def //Debug版本
- 把这两个文件都添加到项目中;
- 只要在Debug项目的Setting中选中xx.def,在右边的General中在Exclude file from built前打勾;同样在Release项目的Setting中选中xxD.def,在右边的General中在Exclude file from built前打勾;
- 接下来就Betch Build。