• 第五篇--VS2017如何生成Dll文件


    参考资料:

    https://blog.csdn.net/qq_34097715/article/details/79540933

    https://www.cnblogs.com/RascallySnake/p/3182807.html

    生成Dll三步走

    第一步:先建一个Dll项目

    New --> Project --> Dynamic-Link Library(DLL) --> 取名,选路径 --> OK

    第二步:编写头文件,例子是一个四则运算

    selfTrainingDll.h

    #pragma once
    #ifdef DLL_TRAINING_API
    #else                                                                            
    #define DLL_TRAINING_API _declspec(dllimport) //当编译时,头文件不参加编译,所以.cpp文件中先定义,后头文件被包含进来,因此外部使用时,为dllexport,而在内部编译时,则为dllimport
    #endif  
    
    class DLL_TRAINING_API arithmetic_operation              //需要被外界调用的类(父类)
    {
    public:
        double Add(double a, double b);
        double Sub(double a, double b);
        double Multi(double a, double b);
        double Div(double a, double b);
    };
    
    int DLL_TRAINING_API export333();
    View Code

    第三步:编写CPP文件,实现方法

    selfTrainingDll.cpp

    // selfTrainingDll.cpp : Defines the exported functions for the DLL application.
    //
    
    #include "stdafx.h"
    
    #define DLL_TRAINING_API _declspec(dllexport)
    
    #include <iostream>
    #include "selfTrainingDll.h"
    using namespace std;
    
    double DLL_TRAINING_API arithmetic_operation::Add(double a, double b) {
        return a+b;
    }
    
    double DLL_TRAINING_API arithmetic_operation::Sub(double a, double b) {
        return a - b;
    }
    
    double DLL_TRAINING_API arithmetic_operation::Multi(double a, double b) {
        return a * b;
    }
    
    double DLL_TRAINING_API arithmetic_operation::Div(double a, double b) {
        return a / b;
    }
    
    int DLL_TRAINING_API export333() {
        return 333;
    }
    View Code

    第四步:生成Dll

    Build --> Build Solution

    至此,文件生成完毕

    静态方法调用Dll文件

    第一步:创建一个控制台程序

    省略

    第二步:编译运行,产生Debug文件夹

    第三步:将之前Dll项目生成的selfTrainingDll.h和selfTrainingDll.lib放入项目文件夹下,将selfTrainingDll.dll放入Debug文件夹下

    第四步:在项目中添加selfTrainingDll.h头文件

    第五步:在Cpp中调用Dll

    UseSelfDll.cpp

    // UseSelfDll.cpp : This file contains the 'main' function. Program execution begins and ends there.
    //
    
    #include "pch.h"
    #include <iostream>
    
    using namespace std;
    
    #include "selfTrainingDll.h"
    #pragma comment(lib,"selfTrainingDll.lib")
    
    int main()
    {
        arithmetic_operation ao;
        cout << ao.Add(1,2) << endl;
        cout << ao.Sub(2,1) << endl;
        cout << ao.Multi(2,1) << endl;
        cout << ao.Div(6,4) << endl;
        cout << export333() << endl;
        cout << "Hello World!
    "; 
    }
    View Code

    至此,调用成功

    动态方法调用Dll文件

    UseSelfDll.cpp

    // UseSelfDll.cpp : This file contains the 'main' function. Program execution begins and ends there.
    //
    
    #include "pch.h"
    #include <iostream>
    
    using namespace std;
    
    //#include "selfTrainingDll.h"
    //#pragma comment(lib,"selfTrainingDll.lib")
    
    #include <windows.h>
    
    int main()
    {
    
        typedef int(*_print)();
        cout << "1" << endl;
        HINSTANCE hDll = LoadLibrary(L"selfTrainingDll.dll");
        cout << "2" << endl;
        _print pAdd = (_print)GetProcAddress(hDll, (LPCSTR)MAKEINTRESOURCE(7));
        cout << "3" << endl;
        int a = pAdd();
        cout << a << endl;
        //arithmetic_operation ao;
        //cout << ao.Add(1,2) << endl;
        //cout << ao.Sub(2,1) << endl;
        //cout << ao.Multi(2,1) << endl;
        //cout << ao.Div(6,4) << endl;
        //cout << export333() << endl;
        cout << "Hello World!
    "; 
        FreeLibrary(hDll);
    }
    View Code

    由于C++导出Dll时会出现名字更改的问题,因此这里用序列号代表函数,至于函数的序列号可以用如下方法查看:

    用VS打开cmd窗口(Tools --> Visual Studio Command Prompt),运行dumpbin -exports xxx.dll     后面最好写DLL的绝对路径,否则可能会报错LNK1181: cannot open input file 'XXX.dll'。

    可在EXE所在的目录下使用dumpbin -imports xxx.EXE来查看某EXE文件使用过哪些dll库.

    64位EXE尽量去调用64位DLL,同理32位尽量调用32位。

    如果想要直接使用函数名,那么在生成DLL时要加extern "C"

    #pragma once
    #ifdef __cplusplus               // if used by C++ code
    extern "C" {                     // we need to export the C interface
    #endif
    
    #ifdef DLL_TRAINING_API
    #else                                                                            
    #define DLL_TRAINING_API _declspec(dllimport) //当编译时,头文件不参加编译,所以.cpp文件中先定义,后头文件被包含进来,因此外部使用时,为dllexport,而在内部编译时,则为dllimport
    #endif  
    
        class DLL_TRAINING_API arithmetic_operation              //需要被外界调用的类(父类)
        {
        public:
            double Add(double a, double b);
            double Sub(double a, double b);
            double Multi(double a, double b);
            double Div(double a, double b);
        };
    
        int DLL_TRAINING_API export333();
    
    #ifdef __cplusplus
    }
    #endif
    View Code
    // UseSelfDll.cpp : This file contains the 'main' function. Program execution begins and ends there.
    //
    
    #include "pch.h"
    #include <iostream>
    
    using namespace std;
    
    //#include "selfTrainingDll.h"
    //#pragma comment(lib,"selfTrainingDll.lib")
    
    #include <windows.h>
    
    int main()
    {
        typedef int (*_print)();
        HINSTANCE hDll = LoadLibrary(L"selfTrainingDll.dll");
        _print pAdd = (_print)GetProcAddress(hDll, "export333");
        int a = pAdd();
        cout << a << endl;
        //arithmetic_operation ao;
        //cout << ao.Add(1,2) << endl;
        //cout << ao.Sub(2,1) << endl;
        //cout << ao.Multi(2,1) << endl;
        //cout << ao.Div(6,4) << endl;
        //cout << export333() << endl;
        cout << "Hello World!
    "; 
        FreeLibrary(hDll);
    }
    View Code

    但是它仍然有局限性,只针对函数,不针对类。 

    另外对于带参数的函数,怎么动态调用呢。

    HINSTANCE hDLL;
    typedef DWORD(*GetPCICFG)(BYTE bBus, BYTE bDev, BYTE bFun, BYTE bIdx, PDWORD pdwPortVal, BYTE bSize);
    GetPCICFG getPCICFG;
    hDLL = LoadLibrary(L"PCI.dll");
    if (hDLL == NULL)
        printf("Error!!!
    "); 
    getPCICFG = (GetPCICFG)GetProcAddress(hDLL, "PCI_GetPCICFG");
    
    FreeLibrary(hDLL);
    View Code

    另外,可以将dll和exe工具放在一起调试,会比较方便,当然,exe里面的用动态调试比较好。静态的可以看前面的链接。

    首先,我们写了一个DLL,然后在Solution里面点击添加-->新建项目,这个就是我们新建的exe程序。新建了exe程序后,可以看到Solution里面有了两个项目,这时我们右键exe的项目将其设为启动项目,这时每次调试时都会执行exe的项目,如果要让exe随着DLL的修改而修改,可以同步反应,那么就右键exe项目,为其添加依赖项,选择DLL为他的依赖就好了。这样exe和DLL就可以同步调试了。

    静态调用DLL--同步调试

    参考链接:https://blog.csdn.net/cynophile/article/details/79749524

    一、新建一个DLL项目,编译。

    二、添加主cpp对应的头文件,然后添加一个“export.h”头文件。

    三、修改项目配置

    四、写主cpp的.h文件

    五、写主cpp里面的函数,然后再次编译

    六、右键单击左侧列表中“解决方案”,然后在弹出菜单中选择“添加 > 新建项目”,向解决方案中添加一个新的控制台项目,用于测试Bluetooth中导出的printHello()函数是否可以正常访问;

    七、将控制台项目设为启动项目

    八、将exe与dll链接起来,需要对项目进行配置

    九、在控制台(exe)属性页窗口中,将配置设置为“所有配置”,然后在左侧“配置属性”列表中,选择“链接器 > 常规”,接着在右侧属性列表中选择“附加库目录”属性右方的编辑框,在弹出的下拉列表中选择“编辑”;$(OutDir)

    十、配置完成,在exe中调用dll中的函数。

    十一、运行函数DebugStart Without Debugging

    十二、结果

    大功告成。

    另外,如果想要dll以c的方式导出,可以修改主cpp的.h文件

    #pragma once
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    
    #ifndef Bluetooth_H
    #define Bluetooth_H
    
    #include "export.h"
    
    EXPORT_BLUETOOTH void printHello();
    
    #endif //!Bluetooth_H
    
    #ifdef __cplusplus
    }
    #endif
    View Code

    动态调用DLL--同步调试

    参考链接:http://www.mamicode.com/info-detail-2949884.html

    依次执行静态调用DLL--同步测试的步骤:一、二、三、四、五、六、七、八 

    然后在Test_Bluetooth.cpp中调用printHello函数

    // Test_Bluetooth.cpp : This file contains the 'main' function. Program execution begins and ends there.
    //
    
    #include "pch.h"
    #include <iostream>
    #include <Windows.h>
    using namespace std;
    
    
    class WinDll
    {
    public:
        WinDll(const char* dll) : mLib(::LoadLibraryA(dll)) {}
        WinDll(const wchar_t* dll) : mLib(::LoadLibrary(dll)) {}
        ~WinDll() { FreeLibrary(mLib); }
    
        WinDll(const WinDll&) = delete;
        WinDll& operator=(const WinDll&) = delete;
    
        operator bool() { return !!mLib; }
    
        template <typename Ret, typename... Args>
        Ret Invoke(const char* name, const Args& ...args)
        {
            auto proc = GetProcAddress(mLib, name);
            typedef Ret(__stdcall* Func)(Args...);
            return (proc) ? reinterpret_cast<Func>(proc)(args...) : (Ret());
        }
    
    private:
        HMODULE mLib;
    };
    
    
    int main()
    {
        WinDll bluetooth("Bluetooth.dll路径");
        if (bluetooth) {
            printf("start
    ");
            bluetooth.Invoke<void>("printHello");
            bluetooth.~WinDll();
        }
    }    
    View Code

    如此可行。

    动态调用封装模板

    参考链接:https://www.cnblogs.com/wuyaSama/p/11510889.html

    动态调用dll,用到一个函数就要写那一串,写得很烦,就想有没有简单的方法封装一下,以便调用,之前找到一个封装类WinDll,用一些简单的还不错,用得还蛮开心的,不过当有函数的参数是char*的话就会出现问题,因此又找了一种其他的封装方式。

    template<typename _T> class Api;
    template<typename _Res, typename... _ArgTypes>
    class Api<_Res(_ArgTypes...)> {
    public:
        Api(const char* dllName, const char* funcName) {
            _M_module = LoadLibraryA(dllName);
            _M_func = reinterpret_cast<_Func>(GetProcAddress(_M_module, funcName));
        }
        ~Api() {
            if (_M_module) FreeLibrary(_M_module);
        }
        _Res operator()(_ArgTypes... __args) const {
            return _M_func(__args...);
        }
    private:
        typedef _Res(*_Func)(_ArgTypes...);
        _Func _M_func;
        HMODULE _M_module;
    };
    
    int main(int argc, char* argv[]){
        Api<int(char*, char*)> write_head("F:\aaa.dll", "print_hello");
        int result = write_head("123", "456");
        write_head.~Api();
    }
    View Code
  • 相关阅读:
    Oracle db 使用转换函数和条件表达式
    Oracle DB 嵌套函数
    Oracle DB NVL、NVL2、NULLIF、COALESCE函数
    OCP-1Z0-051-V9.02-92题
    OCP-1Z0-051-V9.02-90题
    OCP-1Z0-051-V9.02-89题
    OCP-1Z0-051-V9.02-88题
    OCP-1Z0-051-V9.02-87题
    OCP-1Z0-051-V9.02-86题
    OCP-1Z0-051-V9.02-85题
  • 原文地址:https://www.cnblogs.com/smart-zihan/p/10855079.html
Copyright © 2020-2023  润新知