1. 程式人生 > >VS 2015 DLL的建立、靜態呼叫和動態呼叫

VS 2015 DLL的建立、靜態呼叫和動態呼叫

DLL的建立

建立步驟

檔案 -> 新建 -> 專案 -> “新建專案”對話方塊 -> “已安裝” -> 模板 -> 其他語言 -> Vistual C++ -> Win32 控制檯應用程式。

這裡寫圖片描述

在”Win32 應用程式嚮導”對話方塊中

  • “控制檯程式型別”選擇”DLL(D)”
  • “附加選項”勾選”匯出符號(X)”

這裡寫圖片描述

這裡寫圖片描述

匯入(匯出)標記的巨集定義

下列 ifdef 塊是建立使從 DLL 匯出更簡單的巨集的標準方法。此 DLL 中的所有檔案都是用命令列上定義的DLLDEMO_EXPORTS符號編譯的。在使用此 DLL 的任何其他專案上不應定義此符號。這樣,原始檔中包含此檔案的任何其他專案都會將DLLDEMO_API 函式視為是從 DLL 匯入的,而此 DLL 則將用此巨集定義的符號視為是被匯出的。

#ifdef DLLDEMO_EXPORTS
#define DLLDEMO_API __declspec(dllexport)
#else
#define DLLDEMO_API __declspec(dllimport)
#endif

匯出符號標識的巨集定義位於:解決方案資源管理器 -> 專案屬性 -> “專案屬性頁”對話方塊 -> “配置屬性” -> C/C++ -> 前處理器 -> 前處理器定義

這裡寫圖片描述

修改DLL專案

DllDemo.h

#ifdef DLLDEMO_EXPORTS
#define DLLDEMO_API __declspec(dllexport)
#else #define DLLDEMO_API __declspec(dllimport) #endif // 此類是從 DllDemo.dll 匯出的 class DLLDEMO_API CDllDemo { public: CDllDemo(void); // TODO: 在此新增您的方法。 }; extern DLLDEMO_API int nDllDemo; extern "C" extern DLLDEMO_API int nExternCDllDemo; DLLDEMO_API int fnDllDemo(void); extern "C" DLLDEMO_API int
fnExternCDllDemo(void); char DLLDEMO_API fnDefault(char, int, float); char DLLDEMO_API __stdcall fnstdcall(char, int, float); char DLLDEMO_API __cdecl fncdecl(char, int, float); char DLLDEMO_API __fastcall fnfastcall(char, int, float);

dllmain.cpp

// dllmain.cpp : 定義 DLL 應用程式的入口點。
#include "stdafx.h"
#include <stdio.h>
#include <tchar.h>

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    /*
    獲取模組檔名,如果是dll的話,獲取的是呼叫它的可執行檔案路徑
    WINBASEAPI 
    _Success_(return != 0)
    _Ret_range_(1, nSize) 
    DWORD WINAPI GetModuleFileNameW(
        _In_opt_ HMODULE hModule,
        _Out_writes_to_(nSize, ((return < nSize) ? (return + 1) : nSize)) LPWSTR lpFilename,
        _In_ DWORD nSize
    );
    */
    TCHAR lpFilename[MAX_PATH];
    DWORD ret = GetModuleFileName(NULL, lpFilename, MAX_PATH);
    if (ret) {
        _tprintf(_T("GetModuleFileName -> lpFilename=%s\n"), lpFilename);
    }
    else {
        printf("GetModuleFileName -> fail(%ld)", GetLastError());
    }

    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        printf("DLL_PROCESS_ATTACH\n");
        break;
    case DLL_THREAD_ATTACH:
        printf("DLL_THREAD_ATTACH\n");
        break;
    case DLL_THREAD_DETACH:
        printf("DLL_THREAD_DETACH\n");
        break;
    case DLL_PROCESS_DETACH:
        printf("DLL_PROCESS_DETACH\n");
        break;
    }
    return TRUE;
}

DllDemo.cpp

// DllDemo.cpp : 定義 DLL 應用程式的匯出函式。

#include "stdafx.h"
#include "DllDemo.h"

// 這是匯出變數的一個示例
DLLDEMO_API int nDllDemo = 1;
DLLDEMO_API int nExternCDllDemo = 2;

// 這是匯出函式的一個示例。
DLLDEMO_API int fnDllDemo(void)
{
    return 42;
}
DLLDEMO_API int fnExternCDllDemo(void)
{
    return 142;
}

char DLLDEMO_API fnDefault(char, int, float) 
{
    return 'a';
}
char DLLDEMO_API __stdcall fnstdcall(char, int, float)
{
    return 'b';
}
char DLLDEMO_API __cdecl fncdecl(char, int, float)
{
    return 'c';
}
char DLLDEMO_API __fastcall fnfastcall(char, int, float)
{
    return 'd';
}

// 這是已匯出類的建構函式。
// 有關類定義的資訊,請參閱 DllDemo.h
CDllDemo::CDllDemo()
{
    return;
}

生成DLL有以下幾種方案

  • 解決方案資源管理器 -> “DLL專案”右鍵 -> 生成(U)
  • 選單欄 -> 生產DLL(U)(Shift + F6)

然後會在專案的根目錄的Debug或Release資料夾下生成相應的檔案。

dumpbin檢視dll(或lib)的匯出符號

dumpbin.exe 位於 :C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin

這裡寫圖片描述

檢視編譯後的DLL函式名用到的只是”EXPORTS”引數而已。函式名為name列等號的左側。

可以看到extern “C”修飾的函式名與原函式名一致。原因可以參看《函式過載與Extern “C”》一文: http://blog.csdn.net/chy555chy/article/details/53015808。另外說明一點,使用extern “C”修飾的C++函式不能使用C++的過載特性,因為匯出的函式符號只是原函式名,編譯器沒有將引數型別資訊加到匯出的函式名中,因此無法區分過載函式。

這裡寫圖片描述

DLL的呼叫

匯入DLL

(1)當”DLL專案”和”可執行專案”屬於同一個解決方案時。在”DLL專案”右鍵”生成”可以在專案的Debug目錄下生成相應的DLL和LIB。然後在”可執行專案”右鍵”設為啟動專案”,然後點選”本地Windows偵錯程式”即可。

  • 靜態呼叫
    直接執行會報無法開啟LIB(不加字尾預設是LIB)或DLL
    這裡寫圖片描述
    這裡寫圖片描述
    解決方案:在”可執行專案”右鍵 -> 新增(D) -> 引用(R)…
    這裡寫圖片描述
    可以看到引用處多了相應的DLL檔案
    這裡寫圖片描述

  • 動態呼叫
    如果是動態呼叫,此時可正常執行
    這裡寫圖片描述

(2)如果是外部專案的dll

  • 靜態呼叫(有以下2種方法)

    • #param comment("lib", "path\\*lib")中指定其路徑。
    • 解決方案資源管理器 -> “可執行專案”右鍵”屬性” -> “專案屬性頁”對話方塊 -> 配置屬性 -> “連結器” -> 輸入 -> 附加依賴專案 -> 編輯 -> 輸入lib檔案的完整路徑(而不是lib的檔名)
      這裡寫圖片描述
  • 動態呼叫
    LoadLibrary(_T("path\\*.dll"));中指定其路徑。

DLL呼叫有兩種方式,一種是靜態呼叫,另外一種是動態呼叫

靜態呼叫(同時需要標頭檔案、LIB和DLL檔案,缺一不可)

靜態呼叫是一種顯式的呼叫方式,即在程式設計的時候便知道了被呼叫的DLL中的介面函式,在編譯連結的時候將DLL與工程生成的exe相關聯。

#include "stdafx.h"
#include "../DllDemo/DllDemo.h"

//lib字尾可以省略,但不可以改為dll
#pragma comment(lib, "DllDemo.lib")

int main()
{
    printf("%d\n", nDllDemo);
    printf("%d\n", fnDllDemo());
    printf("%d\n", fnExternCDllDemo());
    _tsystem(_T("pause"));
    return 0;
}

動態呼叫(僅需要DLL,不需要標頭檔案和LIB)

動態呼叫是一種隱式的呼叫方式,即程式執行過程中裝載DLL,然後獲取指定函式名稱的介面函式,然後再呼叫之。

#include "stdafx.h"
#include <Windows.h>

int main() 
{
    //參考 http://blog.csdn.net/g5dsk/article/details/6680698
    HMODULE hModule = LoadLibrary(_T("DllDemo.dll"));// 雖然 MSDN Library 說這裡如果指定了路徑,要用 backslashes (\),不要用 forward slashes (/),但其實用二者都可以。注:如果用 \,要用 \\。
    if (hModule == NULL || hModule == INVALID_HANDLE_VALUE) {
        return -1;
    }
    /*
    WINBASEAPI FARPROC WINAPI GetProcAddress(
        _In_ HMODULE hModule,
        _In_ LPCSTR  lpProcName //這個是dump /EXPORT *.dll “name”列等號前的值
    );
    返回的是函式或變數的地址,即函式指標或指向變數地址的指標
    */
    typedef int(*TYPE_fnDllDemo) ();
    typedef int(*TYPE_fnExternCDllDemo) ();
    int *nDllDemo = (int *)GetProcAddress(hModule, "?nDllDemo@@3HA");
    TYPE_fnDllDemo fnDllDemo = (TYPE_fnDllDemo)GetProcAddress(hModule, "?fnDllDemo@@YAHXZ");
    int *nExternCDllDemo = (int *)GetProcAddress(hModule, "nExternCDllDemo");
    TYPE_fnExternCDllDemo fnExternCDllDemo = (TYPE_fnExternCDllDemo)GetProcAddress(hModule, "fnExternCDllDemo");
    if(nDllDemo != NULL)
        printf("*nDllDemo = %d\n", *nDllDemo);
    if(fnDllDemo != NULL)
        printf("fnDllDemo() = %d\n", fnDllDemo());
    if (nExternCDllDemo != NULL)
        printf("*nExternCDllDemo = %d\n", *nExternCDllDemo);
    if(fnExternCDllDemo != NULL)
        printf("fnExternCDllDemo() = %d\n", fnExternCDllDemo());
    _tsystem(_T("pause"));
    return 0;
}

執行截圖

注意:DLL_PROCESS_DETACH是在關閉命令列的時候被呼叫,而不是不會呼叫。這裡被system(“pause”)暫停了,並不是說程式結束。另外即使點選關閉,基本也看不到,因為列印完視窗立馬就被關閉了,這些都是一瞬間完成的。

這裡寫圖片描述

相關推薦

VS 2015 DLL建立靜態呼叫動態呼叫

DLL的建立 建立步驟 檔案 -> 新建 -> 專案 -> “新建專案”對話方塊 -> “已安裝” -> 模板 -> 其他語言 -> Vistual C++ -> Win32 控制檯應用程式。 在”W

VC中使用動態連結庫DLL靜態呼叫動態呼叫

VC中生成DLL的辦法見:www.codeproject.com/KB/DLL/RegDLL.aspx VC中使用DLLhttp://www.cnblogs.com/c1230v/articles/1401448.html 呼叫DLL有兩種方法:靜態呼叫和動態呼叫. (一

8_物件建立static 關鍵字靜態變數成員變數的區別文件

一、物件建立   Student s = new Student(); 步驟: (1)載入 Student.class 檔案進記憶體; (2)在棧記憶體中為 s 開闢空間; (3)在堆記憶體為學生物件開闢空間; (4)對學生物件的成員變數進行預設初始化; (5)對學生物件的成員變數進行顯示初始化

Dll 模組間(dll, exe)使用匯出變數靜態變數外部變數的試驗與結論

DLL檔案(Dynamic Linkable Library 即動態連結庫檔案),是一種不能單獨執行的檔案,它允許程式共享執行特殊任務所必需的程式碼和其他資源 比較大的應用程式都由很多模組組成,這些模組分別完成相對獨立的功能,它們彼此協作來完成整個軟體系統的工作。可能存在一些模組的功能較為通用,在構造其它軟

問題集合 ---- linux 靜態動態建立檢查使用建議

=================================================================== linux靜態庫和動態庫分析 本文轉自 http://www.linuxeden.com/html/develop/20100326/94297.html

vs2015+opencv生成DLL並分別靜態動態呼叫

網路上關於vs生成dll的教程很多,解決方案繁雜,令人眼花繚亂,踩坑後推薦幾篇不錯的教程,親自測試可以通過的,我的除錯環境是vs2015 enterprise版+win10+opencv3.4.1。教程1:最簡單的DLL生成與呼叫教程:作者採用了宣告類進行打包的方法來呼叫。教

靜態編譯動態編譯(libdll

weibo docs p s 獎章 com 動態編譯 lan doc sin u2瓢剮JZP匪媳51http://www.docin.com/app/user/userinfo?userid=179185213 0宰9U拔7853E5噸渭3http://www.docin

Python中的實例方法類方法靜態方法普通方法

turn 屬性和方法 靜態方法 一個 def col == pre 類屬性  為了辨析它們的差別,寫了如下代碼:  1 class C: 2 3 def self_method(self, a): 4 return a 5 6

大型企業網絡構建之動態NAT靜態NAT華為NAT

動態NAT、靜態NAT和華為NAT動態NAT、靜態NAT和華為NAT一、NAT概述1、(network address translation )網絡地址轉換。2、NAT的工作過程:NAT設備收到內網的數據包以後,1、首先查看本地是否有去往數據包目地地址的路由;2、再次查看本地設備是否存在對應的nat轉換條目

PHP常見概念混淆(五)之PHP類常量靜態屬性屬性的區別

sta 支持 php5 中英文對照 ext static block 簡介 無法 最近在看手冊的時候發現PHP有好些個坑,一不註意就會掉進去,邊看邊將這些容易混淆的內容記載下來。 tips:看手冊的時候最好中英文對照著看,因為英文手冊上有好些個中文手冊沒有的東西(最新的P

詳談ApacheNginxtomcat的區別以及處理靜態頁面動態頁面的方式

請求 php腳本 特點 java類 是你 源碼 proc 總結 愛好者 就目前來說,網站主要分為靜態頁面和動態頁面,純靜態頁面的網站已經比較少見了,大型網站一般使用的是靜態頁面+動態頁面的建站技術,還有一部分網站是純動態頁面。負責處理這些頁面的軟件我們通常稱之為web容器,

靜態連結庫(LIB)動態連結庫(DLL),DLL靜態載入動態載入,兩種LIB檔案。

靜態連結庫(LIB)和動態連結庫(DLL),DLL的靜態載入和動態載入,兩種LIB檔案。 一、 靜態連結庫(LIB,也簡稱“靜態庫”)與動態連結庫(DLL,也簡稱“動態庫”)的區別 靜態連結庫與動態連結庫都是共享程式碼的方式,如果採用靜態連結庫,則無論你願不願意,lib 中的指令都全部被直接包含在最

【轉】Python基礎-封裝與擴充套件靜態方法類方法

【轉】Python基礎-封裝與擴充套件、靜態方法和類方法 一、封裝與擴充套件 封裝在於明確區分內外,使得類實現者可以修改封裝內的東西而不影響外部呼叫者的程式碼;而外部使用者只知道一個介面(函式),只要介面(函式)名、引數不變,使用者的程式碼永遠無需改變。這就提供一個良好的合作基礎——或者說,只要介面這個基

C語言函式篇(五)靜態動態庫的建立使用

使用庫函式是原始碼的一種保護?我猜的. 庫函式其實不是新鮮的東西,我們一直都在用,比如C庫. 我們執行pringf() 這個函式的時候,就是呼叫C庫的函式.   下面記錄靜態庫和動態庫的生成和使用.   靜態庫:libxxx.a 動態庫:libxxx.so  

Linux 建立刪除檔案資料夾命令

建立資料夾【mkdir】 一、mkdir命令使用許可權 所有使用者都可以在終端使用 mkdir 命令在擁有許可權的資料夾建立資料夾或目錄。 二、mkdir命令使用格式 格式:mkdir [選項] DirName 三、mkdir命令功能 通過 mkdir 命令

python基礎知識整理5——類方法靜態方法繼承相關問題

類方法和靜態方法 method - 通過例項呼叫 - 可以引用類內部的任何屬性和方法 classmethod - 無需例項化 - 可以呼叫類屬性和類方法 - 無法取到普通的成員屬性和方法 staticmethod - 無需例項化 - 無法渠道類內部的任何

中序線索二叉樹的建立線索化遍歷(前序遍歷後序遍歷)

線索二叉樹的概念 線索二叉樹的原理:線索二叉樹是將普通二叉樹左右孩子中的空鏈域利用起來,將左孩子空鏈域指向當前節點的線性遍歷前驅,將右孩子空鏈域指向當前節點的線性遍歷後繼,指向該線性序列中的前驅或後繼

建立刪除檔案資料夾命令

建立資料夾【mkdir】 一、mkdir命令使用許可權 所有使用者都可以在終端使用 mkdir 命令在擁有許可權的資料夾建立資料夾或目錄。 二、mkdir命令使用格式 格式:mkdir [選項] DirName 三、mkdir命令功能 通過 mkdir 命令可以實現在指定位置建立以 DirName(指定的檔名

Linux 學習之建立刪除檔案資料夾命令

今天學習了幾個命令,是建立、刪除檔案和資料夾的,在linux裡,資料夾是目錄,下面說下我學習的命令。 建立資料夾【mkdir】   一、mkdir命令使用許可權     所有使用者都可以在終端使用 mkdir 命令在擁有許可權的資料夾建立資料夾或目錄。     二

靜態動態庫的相互呼叫

在windows程式設計中,經常會遇到靜態庫裡呼叫動態庫的問題。 具體方法: 1,編寫一個動態連結庫,生成。 2,編寫一個靜態連結庫,裡面包含步驟1生成的動態連結庫的標頭檔案和lib, dll。 3,在呼叫該靜態連結庫時,將步驟2中的靜態連結庫的標頭檔案,lib檔案以及動態