动态链接库相比与静态链接库,所占用的内存空间小,较少交换时间,生成的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文件,这样既能确保函数名称不变,而且还能保证动态链接库的入口点函数名字不变。