zoukankan      html  css  js  c++  java
  • 动态库(非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

  • 相关阅读:
    重打技术征集系统第七稿开发第1、2天
    关于centos7右上角网络图标消失的解决办法
    记录一下hbase踩的坑
    在jsp中,单选按钮的点击事件(点击隐藏或显示输入框)
    input中禁止输入特殊字符
    《软件方法》阅读笔记——1
    随手快递app开发第十七天
    Codeforces Round #284 (Div. 1) B. Name That Tune(最大流)
    hihocoder 1310岛屿(dfs,一个搜索技巧)
    Codeforces Round #294 (Div. 2) E. A and B and Lecture Rooms(lca+思维,树上寻找与给定两个点距离相等的点的个数)
  • 原文地址:https://www.cnblogs.com/zhouwanqiu/p/6921351.html
Copyright © 2011-2022 走看看