动态库(非MFC动态库)

一、C语言动态库

  1、创建C语言动态库,并封装函数:

    1)创建新工程:Win32 Dynamic-Link Library

      

    2)添加SourceFiles文件:Cdll.c

      

      Cdll.c中的内容:      

//创建C的动态库

//_declspec(dllexport)声明导出
_declspec(dllexport)int Cdll_add(int add1,int add2){
    return add1+add2;
}

_declspec(dllexport)int Cdll_sub(int sub1,int sub2){
    return sub1-sub2;
}

    3)编译、链接

      注意:调用动态库中的函数时,须执行函数导出,库函数的导出有两种方法:

      ①方法一:用_declspec(dllexport)声明导出

      ②方法二:模块定义文件.def

        如:LIBRARY 库文件名(无后缀)

          EXPORTS

            函数名1 @1

            函数名2 @2

      Build后在当前工程的Debug文件夹下生成Cdll.dll和Cdll.lib文件

      这里生成的Cdll.dll文件中保存了函数的实际偏移地址和对应编号,Cdll.lib文件中并非函数的源代码而是存放的动态库dll中的函数偏移地址编号

      将Cdll.dll文件置于工作区下的bin文件夹中,将Cdll.lib文件置于工作区下的lib文件夹中

  2、调用C语言动态库中的函数:

    ①静态调用(隐式链接)

      注意:静态调用(隐式链接)调用动态库中的函数时,在声明函数时须在函数原型前加_declspec(dllimport)声明导入

      但我们这里是用C程序来调用C语言动态库中的函数,无须函数声明

      1)创建工作工程:Win32 Console Application

        

      2)添加SourceFiles文件:UseCdll.c

        

        UseCdll.c中的内容:        

//静态调用C的动态库

//告诉链接器去哪儿抓偏移地址编号
#pragma comment(lib,"../lib/Cdll.lib")

int main(){
    int sum,sub;
    sum=Cdll_add(5,3);
    sub=Cdll_sub(5,3);
    printf("sum=%d,sub=%d
",sum,sub);
    return 0;
}

      3)编译、链接

        注意:调用动态库的情况下,须将生成的dll文件(即Cdll.dll)与执行文件(即UseCdll.exe)放在同一目录下,程序才可运行

        这里因为之前已将Cdll.dll文件统一置于工作区下的bin文件夹中,因此在本次调用时须修改VC6的菜单栏->工程->设置->连接->输出文件名:../bin/UseCdll.exe

    ②动态调用(显式链接)

二、C++动态库

  1、创建C++动态库,并封装函数:

    注意:调用动态库中的函数时需要执行函数导出,库函数的导出有两种方法:

      ①方法一:用_declspec(dllexport)声明导出

      ②方法二:模块定义文件.def

        如:

          LIBRARY 库文件名(无后缀)

                            EXPORTS

            函数名1 @1

            函数名2 @2

        注:.def 文件中的注释格式为“;注释”,且为单行注释。

    ①“_declspec(dllexport)声明导出”方式创建C++动态库

      1)创建新工程:Win32 Dynamic-Link Library

        

      2)添加SourceFiles文件:CPPdll.cpp

        

        CPPdll.cpp中的内容:       

//用"声明导出_declspec(dllexport)"方式创建C++动态库

//函数导出
_declspec(dllexport)int CPPdll_add(int add1,int add2){
    return add1+add2;
}

_declspec(dllexport)int CPPdll_sub(int sub1,int sub2){
    return sub1-sub2;
}

      3)编译、链接

        Build后,在当前工程的Debug文件夹下生成CPPdll.dll和CPPdll.lib文件

        CPPdll.dll文件保存了函数的实际偏移地址和对应编号,CPPdll.lib文件并非函数源代码而是存放的动态库dll中的函数名和偏移地址编号

        将CPPdll.dll文件置于工作区下的bin文件夹中,将CPPdll.lib文件置于工作区下的lib文件夹中

    ②“模块定义文件.def”方式创建C++动态库

      1)创建新工程:Win32 Dynamic-Link Library

        

      2)添加SourceFiles文件:CPPdll2.def

        

        CPPdll2.def中的内容:        

LIBRARY CPPdll2
EXPORTS
    CPPdll_add @1
    CPPdll_sub @2

      3)添加SourceFiles文件:CPPdll2.cpp

        

        CPPdll2.cpp中的内容:        

//用"模块定义文件导出.def"方式创建C++动态库

//函数导出
int CPPdll_add(int add1,int add2){
    return add1+add2;
}

int CPPdll_sub(int sub1,int sub2){
    return sub1-sub2;
}

      4)编译、链接

        Build后,在当前工程的Debug文件夹下生成CPPdll2.dll和CPPdll2.lib文件

        CPPdll2.dll文件保存了函数的实际偏移地址和对应编号,CPPdll2.lib文件并非函数源代码而是存放的动态库dll中的函数名和偏移地址编号

        将CPPdll2.dll文件置于工作区下的bin文件夹中,将CPPdll2.lib文件置于工作区下的lib文件夹中

  2、调用C++动态库中的函数:

    ①静态调用(隐式链接)

      1)创建工作工程:Win32 Console Application

        

      2)添加SourceFiles文件:UseCPPdll.cpp

        

        UseCPPdll.cpp中的内容:        

//用"隐式链接"方式调用C++动态库
//对应的在创建C++动态库时使用的是"声明导出_declspec(dllexport)"方式

#include <stdio.h>
int CPPdll_add(int add1,int add2);
int CPPdll_sub(int sub1,int sub2);

//告诉编译器到哪去抓函数的导出偏移地址编号
#pragma comment(lib,"../lib/CPPdll.lib")

/*****************************************/
//C++编译器调用C语言动态库中的函数
extern "C"int Cdll_add(int add1,int add2);
extern "C"int Cdll_sub(int sub1,int sub2);
#pragma comment(lib,"../lib/Cdll.lib")
/*****************************************/

int main(){
    int sum=CPPdll_add(5,6);
    int sub=CPPdll_sub(5,6);
    printf("sum=%d,sub=%d
",sum,sub);

    /*********************************/
    sum=Cdll_add(5,8);
    sub=Cdll_sub(5,8);
    printf("sum=%d,sub=%d
",sum,sub);
    /********************************/

    return 0;
}

      3)编译、链接

        注意:调用动态库的情况下,须将生成的dll文件(即CPPdll.dll)与执行文件(即UseCPPdll.exe)放在同一目录下,程序才可运行

        这里因为之前已将CPPdll.dll文件统一置于工作区下的bin文件夹中,因此在本次调试时,须修改VC6的菜单栏->工程->设置->连接->输出文件名:../bin/UseCPPdll.exe

      4)C++编译器在调用动态库中的函数时,须进行函数声明:

        #include <stdio.h>

        int CPPdll_add(int add1,int add2);

        int CPPdll_sub(int sub1,int sub2);

      5)C++编译器在调用C语言动态库中的函数时,会对函数名进行换名,须使用extern “C”来抑制C++编译器的换名

        extern "C"int Cdll_add(int add1,int add2);

        extern "C"int Cdll_sub(int sub1,int sub2);

        #pragma comment(lib,"../lib/Cdll.lib")

      6)这里C++编译器在调用动态库中的函数时,未见将函数导入,是因为将函数声明放在了调用程序内部,如果单独放在头文件中,则须将函数导入:

        //UseCPPdll.h

        _declspec(dllimport)int CPPdll_add(int add1,int add2);

        _declspec(dllimport)int CPPdll_sub(int sub1,int sub2);

        若调用的是C语言动态库(.c生成的.dll、.lib)中的函数,还须在int前加extern "C"

    ②动态调用(显式链接)

      注意:C++程序在动态调用(显式链接)C++动态库中的函数时,会对函数进行换名,故推荐用“模块定义文件.def”的方式导出函数

      1)创建工作工程:Win32 Console Application

        

      2)添加SourceFiles文件:UseCPPdll2.cpp

        

        UseCPPdll2.cpp中的内容:        

//用"显示连接"方式调用C++动态库
//用此方法调用时,C++编译器会对库中函数的调用进行换名
//因此在创建C++动态库时,推荐使用"模块定义导出.def"方式将函数导出

#include <windows.h>
#include <stdio.h>

typedef int(*DLL_ADD)(int m,int n);
typedef int(*DLL_SUB)(int m,int n);

int main(){
    //找到dll文件并使文件中的内容进入内存
    HINSTANCE hDll=LoadLibrary("CPPdll2.dll");
    printf("hDll:%d
",hDll);

    //获取函数的绝对地址并进行函数调用
    
    DLL_ADD myAdd=(DLL_ADD)GetProcAddress(hDll,"CPPdll_add");
    printf("myAdd:%p
",myAdd);
    int sum=myAdd(5,5);
    printf("sum=%d
",sum);

    DLL_SUB mySub=(DLL_SUB)GetProcAddress(hDll,"CPPdll_sub");
    printf("mySub:%p
",mySub);
    int sub=mySub(5,5);
    printf("sub=%d
",sub);

    FreeLibrary(hDll);
    return 0;
}

      3)编译、链接

        注意:调用动态库的情况下,须将生成的dll文件(即CPPdll2.dll)与执行文件(即UseCPPdll2.exe)放在同一目录下,程序才可运行

        这里因为之前已将CPPdll2.dll文件统一置于工作区下的bin文件夹中,因此在本次调试时,须修改VC6的菜单栏->工程->设置->连接->输出文件名:../bin/UseCPPdll2.exe

        动态加载:

          1.定义函数指针类型:typedef ...

          2.加载动态库

            HMODULE  LoadLibrary (

              LPCTSTR  lpFileName       // 动态库文件名(按路径规则搜索)或者用绝对/相对路径(按指定路径加载)

            );

            成功返回动态库实例句柄(HINSTANCE),失败返回NULL。

          3.获取函数地址

            FARPROC  GetProcAddress (

              HMODULE  hModule,        // 动态库实例句柄

              LPCSTR         lpProcName,     // 函数名(注意C++换名问题)

            );

            成功返回函数地址,失败返回NULL。

          4.卸载动态库

            BOOL  FreeLibrary (

              HMODULE  hModule    // 动态库实例句柄

            );

            成功返回TRUE,失败返回FALSE。

          5.可执行程序调用LoadLibrary时加载动态库,调用FreeLibrary时卸载动态库

  

  3、在C++动态库中封装类

    注意:动态库中类的导出只能用“_declspec(dllexport)”方式,不能使用“模块定义文件.def”方式

    1)创建新工程:Win32 Dynamic-Link Library

      

    2)添加HeaderFiles文件:dllClass.h

      

      dllClass.h中的内容:      

#ifndef DLLCLASS_H
#define DLLCLASS_H

//宏开关
//定义关于库中类的“导出”(我们编写库时为导出)或“导入”(用户使用库时需导入)的宏定义
#ifdef DLLCLASS_EXPORTS
#define EXT_CLASS _declspec(dllexport)
#else
#define EXT_CLASS _declspec(dllimport)
#endif

class EXT_CLASS CMath{
public:
    int Add(int add1,int add2);
    int Sub(int sub1,int sub2);
};

#endif

      类导出的宏开关:

        #ifdef DLLCLASS_EXPORTS

        #define EXT_CLASS _declspec(dllexport)

        #else

        #define EXT_CLASS _declspec(dllimport)

        #endif

        class EXT_CLASS CMath{

          ......

        }

    3)添加SourceFiles文件:dllClass.cpp

      

      dllClass.cpp中的内容:      

//在库中封装类
//在.cpp源文件中实现函数的功能,编译连接后生成的.dll文件用户才不可见具体的功能实现过程

//必须在头文件前打开宏开关,设为导出:_declspec(dllexport)
#define DLLCLASS_EXPORTS
#include "dllClass.h"
#include <windows.h>
#include <stdio.h>

//入口函数
BOOL WINAPI DllMain(HINSTANCE hDll,DWORD fdwReason,LPVOID pParam){
    switch(fdwReason){
        case DLL_PROCESS_ATTACH:  //动态库被别的进程加载
            //申请资源、初始化工作
            printf("Loading...
");
            break;
        case DLL_PROCESS_DETACH:  //动态库被别的进程卸载
            //善后处理
            printf("UnLoading...
");
            break;
    }
    return TRUE;
}

int CMath::Add(int add1,int add2){
    return add1+add2;
}

int CMath::Sub(int sub1,int sub2){
    return sub1-sub2;
}

    4)编译、链接

      Build后,在当前工程的Debug文件夹下生成dllClass.dll和dllClass.lib文件

      dllClass.dll文件保存了函数的实际偏移地址和对应编号,dllClass.lib文件并非函数源代码而是存放的动态库dll中的函数名和偏移地址编号

      将dllClass.dll文件置于工作区下的bin文件夹中,将dllClass.lib文件置于工作区下的lib文件夹中

  4、调用C++动态库中的类:

    1)创建工作工程:Win32 Console Application

      

    2)添加SourceFiles文件:UsedllClass.cpp

      

      UsedllClass.cpp中的内容:     

//调用dllClass.dll库

#include <stdio.h>
#include "../dllClass/dllClass.h"

#pragma comment(lib,"../lib/dllClass.lib")

int main(){
    CMath math;
    int sum=math.Add(5,2);
    int sub=math.Sub(5,2);
    printf("sum=%d,sub=%d
",sum,sub);
    return 0;
}

    3)编译、链接

      在UsedllClass.cpp未见有对宏开关的定义语句“#define DLLCLASS_EXPORTS”,故宏开关自动设为导入:_declspec(dllimport)

      注意:调用动态库的情况下,须将生成的dll文件(即dllClass.dll)与执行文件(即UsedllClass.exe)放在同一目录下,程序才可运行

      这里因为之前已将dllClass.dll文件统一置于工作区下的bin文件夹中,因此在本次调试时,须修改VC6的菜单栏->工程->设置->连接->输出文件名:../bin/UsedllClass.exe

三、动态库的入口函数:

  入口函数不是动态库所必须的,常用于动态库内部初始化或善后处理

  BOOL WINAPI DllMain (

    HANDLE      hDLL,                    // 动态库实例句柄

    DWORD      fdwReason,       // 被调用的原因

    LPVOID       lpvReserved     // 保留值

  );

  返回TRUE表示动态库加载成功,FALSE表示失败。

    fdwReason取值:

      DLL_PROCESS_ATTACH        - 进程加载,在主线程中调用LoadLibrary

      DLL_PROCESS_DETACH        - 进程卸载,在主线程中调用FreeLibrary

      DLL_THREAD_ATTACH        - 线程加载,在子线程中调用LoadLibrary

      DLL_THREAD_DETACH        - 线程卸载,在子线程中调用FreeLibrary

原文地址:https://www.cnblogs.com/zhouwanqiu/p/6921351.html