动态调用动态链接库(.dll),包括函数和类的调用

在阅读本文前,我假定您具备如下能力:

  1. C++基础
  2. 基本的VS操作能力

阅读完本文后:

C++动态链接库的编写
动态调用C++动态链接库(包含类和函数)


首先,了解四个概念:

  1. 静态链接库

    .lib文件,库中的代码最后需要连接到你的可执行文件中

  2. 动态链接库

    **.dll文件
    动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 文件中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个 DLL 副本的内容。

  3. 动态链接库的静态调用

    需要.h .dll文件, 有时候甚至会用到.lib文件,在工程编译时就将库中的方法和类等引入

  4. 动态链接库的动态调用

    在已知库文件内容的前提下,只使用.dll文件,动态的将其调用,本文就在描述此方法的实现。(库文件可使用dumpin等工具打开dll已查看)。
    动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 文件中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个 DLL 副本的内容。

其它一些概念:
动态链接库与静态链接库的区别:
lib是编译时用到的,dll是运行时用到的。如果要完成源代码的编译,只需要lib;如果要使动态链接的程序运行起来,只需要dll。

为什么用动态链接库:
私以为:在上层调用与底层开发确定接口之后,使用动态链接库能更方便的进行模块化编程,将底层驱动开发工作与应用层开发工作完全区分开来。


然后,动态链接库的生成(VS2015):

新建项目->win32控制台程序->DLL,空白程序
动态调用动态链接库(.dll),包括函数和类的调用_第1张图片

创建头文件,声明你想要的类,函数等(将来要导出的类,函数最好都放在一个头文件中,将来会用到)。以及类的接口函数和释放函数。

这里要注意的是:
**输出的函数需要用关键字 __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,以及之前工程的头文件(用于在新工程说明变量的数据结构)

  1. 新建一个工程,选择你需要用(win32,mfc)的即可
  2. 将生成的dll放到你新建工程的运行目录中
  3. 在新工程中新建一个目录,这里我命名为include,将之前工程的头文件放进去。
  4. 在工程属性里,将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的返回值赋给一个指向雷德实例的指针即可。


以上,如果有什么需要说明的地方,请留言。

你可能感兴趣的:(C++)