• C++动态库的制作和调用


    原文链接:https://blog.csdn.net/w_x_myself/article/details/82252646

    1、dll的有点
    代码复用是提高软件开发效率的重要途径。一般而言,只要某部分代码具有通用性,就可将它构造成相对独立的功能模块并在之后的项目中重复使用。比较常见的例子是各种应用程序框架,ATL、MFC等,它们都以源代码的形式发布。由于这种复用是“源码级别”的,源代码完全暴露给了程序员,因而称之为“白盒复用”。“白盒复用”的缺点比较多,总结起来有4点。 
    暴露了源代码;多份拷贝,造成存储浪费; 
    容易与程序员的“普通”代码发生命名冲突; 
    更新功能模块比较困难,不利于问题的模块化实现; 
    实际上,以上4点概括起来就是“暴露的源代码”造成“代码严重耦合”。为了弥补这些不足,就提出了“二进制级别”的代码复用。使用二进制级别的代码复用一定程度上隐藏了源代码,对于缓解代码耦合现象起到了一定的作用。这样的复用被称为“黑盒复用”。 

    说明:实现“黑盒复用”的途径不只dll一种,静态链接库甚至更高级的COM组件都是。

    2、ddl的创建
    2.1、创建及注意事项
    文件------>新建------>项目------>Win32控制台应用程序/Win32项目------>单击下一步------>应用程序类型选择DLL(图1)------>单击完成。

     创建出来原始项目结构:

     在附加选项中,选择空项目,生成的项目结构

     注意:解决方案配置问题,win32平台生成的dll文件,只能被win32平台运行的项目调用:x64平台生成的dll文件,只能被x64平台运行的项目调用。

    2.2、动态库制作方法
    extern "C" _declspec(dllexport)与project2.h中的#ifdef.......endif是将C++函数导出,才会生成lib文件

    2.2.1、方法一
    通过定义C的接口函数对类方法进行封装,及定义全局变量,源码如下(此方法定义的类,还可以进行多项目联合编程):

    Project1.h

    #include "stdafx.h"
    #include<iostream>
    #include<string>
    using namespace std;
    class project1
    {
    public:
        project1();
        ~project1();
        void project1_name();
        void project1_budget(int money);
        bool project1_run();
        int project1_numPeople();
        string project_name;
    };

    Project1.cpp

    #include "stdafx.h"
    #include"Project1.h"
    project1::project1(){}
    project1::~project1(){}
    project1 theApp;//定义一个全局变量,方便被封装函数调用类的方法
    void project1::project1_name()
    {
        cout << "项目名称为:"<<endl;
        cout << project_name << endl;
    }
    void project1::project1_budget(int money)
    {
        cout << money << endl;
    }
    bool project1::project1_run()
    {
        return true;
    }
    int project1::project1_numPeople()
    {
        return 10;
    }
    extern "C" _declspec(dllexport) void name()
    {
        theApp.project1_name();
    }
    extern "C" _declspec(dllexport) void budget(int money)
    {
        theApp.project1_budget(money);
    }
    extern "C" _declspec(dllexport) bool run()
    {
        return theApp.project1_run();
    }
    extern "C" _declspec(dllexport) int numPeople()//对numPeople进行封装,需要使用关键字extern "C" _declspec(dllexport),运用关键字后,才会生产lib文件
    {
        return theApp.project1_numPeople();
    }

    2.2.2、方法二

    将类的成员函数直接封装成C接口,源码如下

    project2.h

    #ifdef TESTDLL_EXPORTS  
    #define TESTDLL_API __declspec(dllexport)   
    #else  
    #define TESTDLL_API __declspec(dllimport)   
    #endif 
    #include<iostream>
    #include<string>
     
    using namespace std;
    class project2
    {
    public:
     
        project2();
        ~project2();
        TESTDLL_API void project2_name();
        TESTDLL_API void project2_budget(int money);//库制作不会报异常,但是传入参数会无效
    //应修改成 static TESTDLL_API void project2_budget(int money);
        TESTDLL_API bool project2_run();
        TESTDLL_API int project2_numPeople();
        string project_name;
    };

    project2.cpp

    project2::project2(){}
    project2::~project2(){}
    void project2::project2_name()
    {
        project_name=“项目2”;//1
        cout << "项目名称为:" << endl;
        cout << project_name << endl;//2
        //调用时,应该把1和2注销,原因未理解
    }
    void project2::project2_budget(int money)
    {
        cout << money << endl;
    }
    bool project2::project2_run()
    {
        return true;
    }
    int project2::project2_numPeople()
    {
        return 20;
    }

    2.3、查看动态库生成的接口
    运用的工具:单击Windows图标------>所有程序------>找到相应的Visual Studio文件夹------->选择Visual Studio tool(会打开文件夹)-------->寻找本机工具命令提示。切换到dll文件目录下,运行命令:dumpbin /EXPORTS 库名(例:dumpbin /EXPORTS Project2.dll)

    方法一生成的动态库结构图:

     方法二生成的动态库结构图:

    3、动态库的链接

    3.1、显示链接

    获取dll库的路径,无需配置环境,代码如下:

    #include<iostream>
    #include<Windows.h>
    using namespace std;
    void Display_Call_Project1_DLL()
    {
        typedef void(*name)();
        typedef void(*budget)(int money);
        typedef bool(*run)();
        typedef int(*numPeople)();
        HMODULE hDLL = LoadLibrary("..\..\Make_Dll\x64\Debug\Project1.dll");//dll的文件路径
        if (hDLL == NULL)
        {
            cout << "动态库未找到" << endl;
            return;
        }
        name n = name(GetProcAddress(hDLL, "name"));//运用函数名
        n();
        budget b = budget(GetProcAddress(hDLL, MAKEINTRESOURCE(1)));//运用序号调用,调用的为budget函数
        b(2000);
        
        run r = run(GetProcAddress(hDLL, "run"));
        cout << r() << endl;
     
        numPeople np = numPeople(GetProcAddress(hDLL, "numPeople"));
        cout << np() << endl;
        FreeLibrary(hDLL);
    }
    //调用方法二生成的动态库
    void Display_Call_Project2_DLL()
    {
        typedef void(*name)();
        typedef void(*budget)(int money);
        typedef bool(*run)();
        typedef int(*numPeople)();
        HMODULE hDLL = LoadLibrary("..\..\Make_Dll2\x64\Debug\Project2.dll");//dll的文件路径
        if (hDLL == NULL)
        {
            cout << "动态库未找到" << endl;
            return;
        }
        name n = name(GetProcAddress(hDLL, "?project2_name@project2@@QEAAXXZ"));//运用函数名
        n();
        budget b = budget(GetProcAddress(hDLL, MAKEINTRESOURCE(1)));//运用序号调用,调用的为budget函数
        b(1000);
     
        run r = run(GetProcAddress(hDLL, MAKEINTRESOURCE(4)));
        cout << r() << endl;
     
        numPeople np = numPeople(GetProcAddress(hDLL, MAKEINTRESOURCE(3)));
        cout << np() << endl;
        FreeLibrary(hDLL);
    }
    int main()
    {
        Display_Call_Project2_DLL();
        system("pause");
        return 0;
    }

    注意:运用方法二,生成的动态库,成员函数必须设置静态成员函数,并且不能调用类的成员。否则入传参无效,并且调用类成员会报错。

    3.2、隐式链接
    必须配置环境:

    项目->属性->配置属性->VC++ 目录-> 在“包含目录”里添加头文件project2.h所在的目录

    项目->属性->配置属性->VC++ 目录-> 在“库目录”里添加头文件project2.lib所在的目录

    项目->属性->配置属性->链接器->输入-> 在“附加依赖项”里添加“project2.lib”(若有多个 lib 则以空格隔开)

    方法一生成的动态库,无法进行隐式链接。

    隐式链接动态库的制作方法,必须在类函数中加上宏定义,源码如下:

    project2.h

    #ifdef TESTDLL_EXPORTS
     
    #define TESTDLL_API __declspec(dllexport)   
    #else  
    #define TESTDLL_API __declspec(dllimport)   
    #endif 
    #include<iostream>
    #include<string>
     
    using namespace std;
    class project2
    {
    public:
     
        TESTDLL_API project2();
        TESTDLL_API ~project2();
        TESTDLL_API void project2_name();
        TESTDLL_API void project2_budget(int money);
        TESTDLL_API bool project2_run();
        TESTDLL_API int project2_numPeople();
        string project_name;
    };

    project2.cpp

    #define TESTDLL_EXPORTS//不进行宏定义,或提示链接不一致,导致隐式调用失败
    #include"Project2.h"
    project2::project2(){}
    project2::~project2(){}
    void project2::project2_name()
    {
        //project_name = "项目2";
        cout << "项目名称为:";
        //cout << project_name << endl;
    }
    void project2::project2_budget(int money)
    {
        cout << money << endl;
    }
    bool project2::project2_run()
    {
        return true;
    }
    int project2::project2_numPeople()
    {
        return 20;
    }

     调用函数:

    main.cpp

    #include<iostream>
    #include<Windows.h>
    #include"Project1.h"
    #include"project2.h"
    using namespace std;
    void Call_Project2_DLL()
    {
        project2 p;
        p.project2_run();
        p.project2_budget(1000);
        
    }
    int main()
    {
     
        Call_Project2_DLL();
        system("pause");
        return 0;
    }

    注:提示找不到dll库时,将dll库放在main.cpp同级目录下

  • 相关阅读:
    1133catalan数二维变种
    HDU2254 奥运 矩阵应用
    hdu1134大数+catalan数
    C#编写一个控制台应用程序,输入三角形或者长方形边长,计算其周长和面积并输出。
    简单工厂
    每日总结
    C#编写一个控制台应用程序,可根据输入的月份判断所在季节
    使用Java api对HBase 2.4.5进行增删改查
    C#编写程序,用 while 循环语句实现下列功能:有一篮鸡蛋,不止一个,有人两个两 个数,多余一个,三个三个数,多余一个,再四个四个地数,也多余一个,请问这篮鸡蛋至 少有多少个
    每日总结
  • 原文地址:https://www.cnblogs.com/symcheal/p/14640230.html
Copyright © 2020-2023  润新知