关于DLL
DLL(Dynamic Linkable Library),动态链接库,可以向程序提供一些函数、变量或类。这些可以直接拿来使用。
静态链接库与动态链接库的区别:
(1)静态链接库与动态链接库都是共享代码的方式。静态链接库把最后的指令都包含在最终生成的EXE文件中了;动态链接库不必被包含在最终EXE文件中,EXE文件执行时可以“动态”地引用和卸载这个与EXE独立的DLL文件。
(2)静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。
动态链接库的分类:Visual C++支持三种DLL,它们分别是:
(1)Non-MFC DLL(非MFC动态库):
非MFC动态库不采用MFC类库结构,其导出函数为标准的C接口,能被非MFC或MFC编写的应用程序所调用;
(2)MFC Regular DLL(MFC规则DLL):
MFC规则DLL 包含一个继承自CWinApp的类,但其无消息循环;
(3)MFC Extension DLL(MFC扩展DLL):
MFC扩展DLL采用MFC的动态链接版本创建,它只能被用MFC类库所编写的应用程序所调用。
借用一下别人的简介: 原文链接:https://blog.youkuaiyun.com/u014066037/article/details/82491481
导出DLL
生成DLL文件,导出函数,一般有两种方法,一种是在函数声明前面加上__declspec(dllexport),另一种是用def文件来动态导出函数。接下来我将介绍三种使用__declspec(dllexport)导出DLL的方法。
导出函数和全局变量
新建一个普通的控制台空项目,新建头文件声明函数,源文件定义函数,导出函数时常在头文件 使用__declspec(dllexport) 声明要导出的函数
// An highlighted block
//
extern "C" __declspec(dllexport) int Add(int a, int b);
函数定义并没有什么特别,但使用DLL时 ,需要使用__declspec(dllimport) 声明导入的函数
// An highlighted block
#include <iostream>
using namespace std;
//lib文件,相当于导出函数的目录
#pragma comment(lib,"DynamicLib")
//声明导入的函数
extern "C" _declspec(dllimport) int Add(int a, int b);
int main()
{
Add(1,2);
return 0;
}
为了方便使用,可以将导出导入声明都写入头文件。导出函数的步骤如下:
1.新建头文件 func.h
// An highlighted block
#pragma once
#ifdef _DLL_EXPORTS
#define DLL_API _declspec(dllexport)
#else
#define DLL_API _declspec(dllimport)
#endif
//将_DLL_EXPORTS定义在源文件
extern "C" DLL_API int Add(int a, int b);
//导出函数,extern表示变量定义在别的地方
DLL_API extern int intArray[512];
2.定义源文件 func.cpp
// An highlighted block
// func.cpp: 定义 DLL 应用程序的导出函数。
#define _DLL_EXPORTS
#include "func.h"
//定义变量
int intArray[512];
int Add(int a,int b)
{
return a+b;
}
3.在解决方案下项目上右键属性–》配置属性–》常规–》配置类型,将类型由应用程序(exe)改为动态库(.dll)
4.重新生成项目,生成成功。我用的是 release配置方案,所以在 release文件夹下找到dll文件与lib文件
5.使用DLL。简单试一下DLL文件。将 头文件 func.h,dll文件,lib文件,放入,需要使用项目文件下。我是放在与main.cpp文件相同文件夹下。
// 测试导出的函数
#include <iostream>
#include "func.h"
using namespace std;
//导入生成的lib文件
#pragma comment(lib,"DLL.lib")
int main()
{
cout<<Add(1,2)<<endl;
return 0;
}
导出类
1.同样声明头文件Stu.h,只需要在导出类名前关键字class后加上_declspec(dllexport),就可以实现导出类
// An highlighted block
#pragma once
#ifdef _DLL_StuEXPORTS
#define DLL_StuAPI _declspec(dllexport)
#else
#define DLL_StuAPI _declspec(dllimport)
#endif
//只需要在导出类名前关键字class后加上_declspec(dllexport),就可以实现导出类
class DLL_StuAPI Stu
{
public:
Stu(int a);
void print();
private:
int _a;
};
2.定义.cpp文件:
// An highlighted block
// Stu.cpp: 定义 DLL 应用程序的导出函数。
#pragma once
#ifdef _DLL_StuEXPORTS
#define DLL_StuAPI _declspec(dllexport)
#else
#define DLL_StuAPI _declspec(dllimport)
#endif
//只需要在导出类名前关键字class后加上_declspec(dllexport),就可以实现导出类
class DLL_StuAPI Stu
{
public:
Stu(int a);
void print();
private:
int _a;
};
3,4,5步骤与导出函数相同,将调用函数改为调用类对象就行了
这种简单导出类的方式,除了导出的东西太多、使用者对类的实现依赖太多以外,还有其他问题:必须保证使用同一种编译器。导出类的本质是导出类里的函数,因为语法上直接导出了类,没有对函数的调用方式、重命名进行设置,导致了产生的dll并不通用。
导出指向类对象的指针
定义一个抽象类(都是纯虚函数),调用者跟dll共用一个抽象类的头文件,dll中实现此抽象类的派生类,dll最少只需要提供一个用于获取抽象类对象指针的接口。由于是对象一般是通过new分配的,需要我们使用后delete对象,防止内存泄漏。
1.func.h文件:
#pragma once
#ifdef _DLL_AnimalEXPORTS
#define DLL_AnimalAPI _declspec(dllexport)
#else
#define DLL_AnimalAPI _declspec(dllimport)
#endif
class IAnimal
{
public:
virtual void eat() = 0;
virtual void delObj() = 0;
};
extern "C" DLL_AnimalAPI IAnimal *GetCat();
2…cpp文件:
#define _DLL_AnimalEXPORTS
#include "Animal.h"
#include <iostream>
using namespace std;
class Cat :public IAnimal
{
public:
Cat()
{
cout << "Cat is created" << endl;
}
~Cat()
{
cout << "Cat is deleted" << endl;
}
virtual void eat()
{
cout << "Cats eat fish" << endl;
}
virtual void delObj()
{
delete this;
}
};
IAnimal *GetCat()
{
return new Cat;
}
3,4,5步骤与导出函数相同
调用者代码:
//测试导出的类指针
#include <iostream>
#include "Animal.h"
using namespace std;
//导入生成的lib文件
#pragma comment(lib,"DLL.lib")
int main()
{
IAnimal *cat=GetCat();
cat->eat();
return 0;
}
//上面的导出类的例子,引用了:https://blog.youkuaiyun.com/qq_33757398/article/details/82229325j
生成 MFC Extension DLL(MFC扩展DLL)
新建MFC DLL项目,应用设置选择MFC拓展DLL
2.和普通MFC程序一样,添加窗口资源,窗口类,
3.修改窗口类头文件,窗口类cpp文件
在导出类名前关键字class后加上AFX_EXT_CLASS,找不到对话框添加,#include “Resource.h”
#pragma once
//找不到对话框查看是否添加,#include "Resource.h"
#include "Resource.h"
// MydiaLog 对话框
class AFX_EXT_CLASS MydiaLog : public CDialog
{
DECLARE_DYNAMIC(MydiaLog)
public:
MydiaLog(CWnd* pParent = NULL); // 标准构造函数
virtual ~MydiaLog();
virtual void OnFinalRelease();
// 对话框数据
enum { IDD = IDD_DLL_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
DECLARE_MESSAGE_MAP()
DECLARE_DISPATCH_MAP()
DECLARE_INTERFACE_MAP()
public:
afx_msg void OnBnClickedButton1();
};
4.如果导出静态变量的错误,IMPLEMENT_DYNAMIC(MydiaLog, CDialog)
对话框类头文件修改
//注释掉
// 对话框数据
// enum { IDD = IDD_DLL_DIALOG };
对话框类,构造函数
MydiaLog::MydiaLog(CWnd* pParent /*=NULL*/)
: CDialog(MYdialog::IDD, pParent)
{
EnableAutomation();
}
改为自己的对话框资源IDD
MydiaLog::MydiaLog(CWnd* pParent /*=NULL*/)
: CDialog(IDD_DLL_DIALOG , pParent)
{
EnableAutomation();
}
5.使用者函数,导入头文件,lib文件,dll文件
#include "MydiaLog.h"
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
#pragma comment(lib,"MFCDLL")
...
void CcvMFCDlg::OnBnClickedButton1()
{
MydiaLog ml;
ml.DoModal();
}