第五篇--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!\n"; 
}
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!\n"; 
    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!\n"; 
    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!!!\n"); 
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中的函數。

十一、運行函數Debug\Start 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\n");
        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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM