在阅读本文前,我假定您具备如下能力:
C++基础
基本的VS操作能力
阅读完本文后:
C++动态链接库的编写
动态调用C++动态链接库(包含类和函数)
首先,了解四个概念:
静态链接库
.lib文件,库中的代码最后需要连接到你的可执行文件中
动态链接库
**.dll文件
动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 文件中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个 DLL 副本的内容。
动态链接库的静态调用
需要.h .dll文件, 有时候甚至会用到.lib文件,在工程编译时就将库中的方法和类等引入
动态链接库的动态调用
在已知库文件内容的前提下,只使用.dll文件,动态的将其调用,本文就在描述此方法的实现。(库文件可使用dumpin等工具打开dll已查看)。
动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 文件中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个 DLL 副本的内容。
其它一些概念:
动态链接库与静态链接库的区别:
lib是编译时用到的,dll是运行时用到的。如果要完成源代码的编译,只需要lib;如果要使动态链接的程序运行起来,只需要dll。
为什么用动态链接库:
私以为:在上层调用与底层开发确定接口之后,使用动态链接库能更方便的进行模块化编程,将底层驱动开发工作与应用层开发工作完全区分开来。
然后,动态链接库的生成(VS2015):
新建项目->win32控制台程序->DLL,空白程序
创建头文件,声明你想要的类,函数等(将来要导出的类,函数最好都放在一个头文件中,将来会用到)。以及类的接口函数和释放函数。
这里要注意的是:
**输出的函数需要用关键字 __declspec(dllexport) 修饰
基类里的函数均为纯虚函数,仅用作接口
createAPI返回的是子类的实例句柄**
#ConsoleApplication2.h
#ifdef CONSOLEAPPLICATION2_EXPORTS
#define CONSOLEAPPLICATION2_API __declspec(dllexport)
#else
#define CONSOLEAPPLICATION2_API __declspec(dllimport)
#endif
// 此类是从 ConsoleApplication2.dll 导出的
class CConsoleApplication2 {
public:
CConsoleApplication2(void){};
virtual ~CConsoleApplication2(void){};
public:
virtual int returnint1(void) = 0;
virtual int returnint2(void) = 0;
virtual void printhelloworld(void) = 0;
};
#ifdef __cplusplus
extern "C" {
#endif
//新建类的实例
CONSOLEAPPLICATION2_API CConsoleApplication2* createAPI(void);
//销毁类的实例
CONSOLEAPPLICATION2_API void DestoryAPI(CConsoleApplication2* hdl);
#ifdef __cplusplus
}
#endif
class childcls :public CConsoleApplication2
{
public :
~childcls(void){};
childcls(void){};
public:
int returnint1(void);
int returnint2(void);
void printhelloworld(void);
};
并在cpp中实现必要的功能。
// ConsoleApplication2.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#include "ConsoleApplication2.h"
#include "stdio.h"
// 有关类定义的信息,请参阅 ConsoleApplication2.h
//注意,返回值为子类的实例
CConsoleApplication2* createAPI(void)
{
CConsoleApplication2* hdl = new childcls;
if (hdl != NULL)
{
return hdl;
}
else
return NULL;
}
void DestoryAPI(CConsoleApplication2* hdl)
{
if (hdl != NULL)
delete hdl;
}
int childcls::returnint1(void)
{
return 1;
}
int childcls::returnint2(void)
{
return 2;
}
void childcls::printhelloworld(void)
{
printf("hello world dll");
}
进行编译生成,生成相应库文件dll。
动态调用动态链接库
需要用到你之前生成的dll,以及之前工程的头文件(用于在新工程说明变量的数据结构)
新建一个工程,选择你需要用(win32,mfc)的即可
将生成的dll放到你新建工程的运行目录中
在新工程中新建一个目录,这里我命名为include,将之前工程的头文件放进去。
在工程属性里,将include路径添加到 “附加包含目录”。
现在就到了调用dll阶段
简言之,就是使用loadlibarty导入dll,再使用createAPI创建一个子类的实例,之后就可以通过地址来使用相应的函数了。
先上代码
#include "windows.h"
#include "stdio.h"
#include "include\ConsoleApplication2.h"
static LPWSTR path = L"ConsoleApplication2.dll";
typedef CConsoleApplication2* (*CREATEAPI)();
typedef void (*DESTORYAPI)(CConsoleApplication2 *);
typedef struct dllAPI
{
CREATEAPI create;
DESTORYAPI destory;
}dll;
dll *mdll = NULL;
CConsoleApplication2 *dllhdl = NULL;
void *pNDllapi = NULL;
bool loaddll()
{
pNDllapi = LoadLibrary(path);
if (pNDllapi == NULL)
{
printf("dll load failed.");
return false;
}
else
{
if (mdll == NULL)
{
mdll = new dll;
}
memset(mdll,0,sizeof(mdll));
mdll->create = (CREATEAPI)GetProcAddress((HMODULE)pNDllapi, "createAPI");
mdll->destory = (DESTORYAPI)GetProcAddress((HMODULE)pNDllapi, "DestoryAPI");
}
return true;
}
bool init()
{
loaddll();
if (mdll->create == NULL)
{
printf("init create failed");
return false;
}
dllhdl = mdll->create();
printf("proc going ok 0");
return true;
}
bool destory()
{
mdll->destory((CConsoleApplication2*)dllhdl);
FreeLibrary((HMODULE)path);
return true;
}
int main()
{
init();
int a = dllhdl->returnint1();
printf("proc going ok 1,%d",a);
destory();
int err = 0;
Sleep(3000);
return 0;
}
说明:
windows.h是必不可少的,加载dll的方法在这里。
最终我们用到的实例是dllhdl ,所以代码不需要和我的结构相同,只需要将createAPI的返回值赋给一个指向雷德实例的指针即可。
引用:https://blog.csdn.net/harrypap/article/details/82114379