动态链接库相比与静态链接库,所占用的内存空间小,较少交换时间,生成的dll文件也小,而且相应速度也快,静态链接库是将DLL程序为每一个应用程序装载一个副本程序,而动态链接库在多个文件同步使用一个DLL的共享内存中副本。
动态链接库分为Win32 DLL 和 MFC DLL,而后者又分为静态链接的常规DLL ,动态链接的DLL 和 扩展DLL。
一: Win32DLL
方法一:使用__declspec(dllexport) 导出
1)创建 Win32 dynamic library 取工程名称CreateWin32DLL, 选择
a dll that exports some symbols(该方法提供一些APIENTRY DllMain信息,不用在输入了)
2)为了很好的归类,新添加一个类,全部导出函数在这个类中。
类视图中 add new class 名称为CExportDll (一般类名开头为C)
3)新添加类 代码如下:
头文件中:
// ExportDll.h: interface for the CExportDll class.
//
//
#if !defined(AFX_EXPORTDLL_H__F99856A6_8485_4044_A526_1482DADE0ADE__INCLUDED_)
#define AFX_EXPORTDLL_H__F99856A6_8485_4044_A526_1482DADE0ADE__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CExportDll
{
public:
CExportDll();
virtual ~CExportDll();
};//此段可以删除
extern “C” __declspec(dllexport) int Add(int a, int b);
extern “C” _declspec(dllexport) int Subtract(int a, int b);
#endif // !defined(AFX_EXPORTDLL_H__F99856A6_8485_4044_A526_1482DADE0ADE__INCLUDED)
cpp中
// ExportDll.cpp: implementation of the CExportDll class.
//
//
#include “stdafx.h”
#include “ExportDll.h”
//
// Construction/Destruction
//
CExportDll::CExportDll()//可删除
{
}
CExportDll::~CExportDll()//可删除
{
}
extern “C” __declspec(dllexport) int Add(int a, int b)
{
return a + b;
}
extern “C” __declspec(dllexport) int Subtract(int a, int b)
{
return a - b;
}
【注意事项:】
- DLL中尽量不要导出 数据变量
2)有 visual assist中 关键字是 蓝色的,在编写win32DLL时 出现了
error C2065: ‘dllexport’ : undeclared identifier
【解决方法】最后发现是关键字declspec写错了,导致出错
extern “C” __delcspec(dllexport) int Subtract(int a, int b);
应该为 extern “C” __declspec(dllexport) int Subtract(int a, int b);
extern “C” 是c++编译器在编译函数时告诉要用c编译器的方式处理函数名。
3)在测试win32DLL时,要.h,lib dll缺一不可,后两者名称与工程名称相同,.h是含 __declspec的自定义的头文件。添加完毕后出现问题:
fatal error LNK1104: cannot open file “CreateWin32DLL.obj”
【解决方法】1.忘记引用 dll的头文件了 ;2.设置项目属性时,Link中 Lib名称没有带.lib,写成了CreateWin32DLL。这两种情况下都会出现这个问题。
只要修改了DLL,最好将dll lib .h都重新拷贝下。。。
4)extern “C” 返回类型 __declspec(dllexport) 函数名称(形参)
或者:extern “C” __declspec(dllexport) 返回类型 函数名称(形参) 都可以,但尽量用第二种,这样可以很方便的把extern “C” __declspec(dllexport) 定义成一个宏定义。
但需要注意的是,extern "C"好像不能出现中Class CExportDll中 ,如果想在此类中定义,则只能声明为 __declspec(dllexport) int Add(int a, int b);
在函数定义时,
__declspec(dllexport) int CExportDll::Add(int a, int b){return a + b;}
【问题解决】
extern “C” 不能出现在class CExportDLL 类内是因为:
动态链接库中的类中的非静态成员函数不允许用extern "C"阻止名字改编。 类的成员函数是属于类的一部分,不能脱离开类单独存在,而用 extern说明是一个外部成员函数,所以在类中不能用 extern “C” 。
如果想导出一个类 可以用:宏定义 AFX_EXT_CLASS来代替 __declspec(dllexport) 可以用来修饰 类
#define AFX_CLASS_EXPORT __declspec(dllexport)
#define AFX_EXT_CLASS AFX_CLASS_EXPORT
例如: class AFX_EXT_CLASS DLLExport{};
或者 class extern “C” __declspec(dllexport) DLLExport{};
DLLExport为类 前面是导出修饰。
5)导出函数名称:使用 vc6.0中 depends工具查找 dll所在文件,
① __declspec(dllexport) int Add(int a, int b)的导出函数为
?Add@@YAHHH@Z
② __declspec(dllexport) int __stdcall Add(int a, int b)的导出函数为
?Add@@YGHHH@Z
③ __declspec(dllexport) int __cdecl Add(int a, int b);的导出函数为
?Add@@YAHHH@Z
④ extern “C” __declspec(dllexport) int Add(int a, int b)导出函数
Add
⑤ extern “C” __declspec(dllexport) int __stdcall Add(int a, int b)导出函数
_Add@8
⑥ extern “C” __declspec(dllexport) int __cdecl Add(int a, int b)的导出函数为
Add
所以,extern “C” 防止C++名字改编,而 __stdcall 用于Win32API函数约定,使输出函数的前面加一个下划线,函数名称后面加@和参数的字节数,_函数名称@字节数;C/MFC语言默认是__cdecl,导出函数与前者差不多,只是形参列表开始标记由YG编程YA了;
__stdcall 函数调用约定: _函数名@字节数
__cdecl函数调用约定: _函数名@字节数
__fastcall :函数名前后加@,最后加字节数: @函数名@字节数
为了建立自己的标准动态库,建议使用WinAPI 也就是__stdcall约定,这样即使VB调用是也是没问题的。
【结论】最后发现用extern “C” 加调用约定用__cdecl输出的函数名称不会改变;extern “C” 加调不用约定时输出的函数名称不会改变;在不使用 extern "C"时,只使用函数调用约定时,输出函数名字也会改编。最后两者要结合使用!!!!
因为__cdecl为C语言默认的,在加入 extern "C"时,有没有此约定都是可以的。
加入__stdcall约定的导出函数可以被其他语言使用,注意,应该是静态调用,不是动态调用(LoadLibrary)。但,我测试的时候用的隐式调用,
extern “C” __declspec(dllexport) int __stdcall Add(int a, int b)时
__stdcall约定会报错:无法定位程序输入点Add位于动态连接上。!!!,但他们都建议使用此约定呢???
【问题解决】
无法定位程序输入点Add位于动态连接上是 因为DLL创建使用了Release模式下,而调用时使用的Debug模式下,所以问出现问题,更改后依然会报错error LNK2001: unresolved external symbol _Add
因为DLL创建时使用__stdcall约定,而C++默认为__cdecl,所以会报错。
修改约定:属性-》C/C++中 -》category》code generation》_calling convention 中__cdecl 修改为__stdcal就可以啦
所以,此问题是可以。
【还有一种error情况】当修改带__stdcal的头文件时,DLL的工程必须是release模式下编译,否则在调用时会报 unresolved external symbol _Add@8。(是不是VC6.0的bug?)
【结论】动态调用时只有下式成功了,隐式调用时这个也可以,就按照下式写就可以!
extern “C” __declspec(dllexport) int Add(int a, int b)
方法二:添加def文件
1)Win32项目中没有.def文件,先在工程目录下创建一个记事本,更改名称为工程名字,且后缀修改为.def
2)DLL工程中,添加 新文件,就可以啦。VC6.0中直接添加def文件就可以,不用修改属性什么的。
实例如下:
; CreateWin32DLL.def : Declares the module parameters for the DLL.
LIBRARY “CreateWin32DLL”
DESCRIPTION ‘CreateWin32DLL Windows Dynamic Link Library’
EXPORTS
; Explicit exports can go here
Add @1
Subtract @2
结论:
为了解决输出函数名称被修改问题,有两种方法:
(一) 添加 extern “C” 不添加其他 函数调用约定:
extern “C” __declspec(dllexport) int Add(int a, int b);
(二)添加.def文件,这样既能确保函数名称不变,而且还能保证动态链接库的入口点函数名字不变。