[deepseek]Visual Studio 2022創建和使用DLL教程

我想在visual studio2022中使用c語言制作dll應該怎么辦,同時如果我寫了另一個c語言程序想要使用這個dll中的函數應該怎么編寫代碼?

好的,在 Visual Studio 2022 中使用 C 語言創建和使用 DLL 的過程可以分為兩大步。我會為你提供詳細的步驟和代碼示例。


第一部分:創建 DLL

步驟 1:創建新項目

  1. 打開 Visual Studio 2022。
  2. 點擊“創建新項目”。
  3. 在搜索框中,輸入 dynamic,然后選擇“動態鏈接庫 (DLL)”模板。如果找不到,也可以選擇“空項目”,然后手動配置。
  4. 點擊“下一步”,為項目命名(例如 MyMathDLL),選擇位置,然后點擊“創建”。

步驟 2:編寫 DLL 代碼

DLL 需要明確指定哪些函數是可供外部程序(稱為“客戶端”)使用的。我們通過 __declspec(dllexport) 關鍵字來實現。

在“解決方案資源管理器”中,右鍵點擊“源文件” -> “添加” -> “新建項…”,創建一個名為 mymath.c 的文件。

將以下代碼寫入 mymath.c

// mymath.c - DLL 源文件#include <stdio.h>// 聲明函數為導出函數
// __declspec(dllexport) 是關鍵,它告訴編譯器這個函數需要被導出
__declspec(dllexport) int add(int a, int b) {return a + b;
}__declspec(dllexport) int subtract(int a, int b) {return a - b;
}// 一個只在DLL內部使用的函數(輔助函數),不被導出
void internal_helper_function() {printf("This is an internal function, not exported.\n");
}

步驟 3:創建頭文件(可選但強烈推薦)

為了讓使用你 DLL 的程序員知道有哪些函數可用以及它們的參數,最好創建一個頭文件。

右鍵點擊“頭文件” -> “添加” -> “新建項…”,創建一個名為 mymath.h 的文件。

將以下代碼寫入 mymath.h

// mymath.h - DLL 的頭文件// 為了防止頭文件被多次包含時重復定義
#pragma once#ifdef __cplusplus
extern "C" {  // 如果被C++程序使用,確保以C語言的方式編譯和鏈接
#endif// 聲明導出函數。// 注意:這里用的是 `__declspec(dllexport)`,因為當前在編譯DLL本身。__declspec(dllexport) int add(int a, int b);__declspec(dllexport) int subtract(int a, int b);#ifdef __cplusplus
}
#endif

步驟 4:生成 DLL

  1. 在頂部菜單欄,確保解決方案配置是“Release”和“x64”(或者 x86,根據你的目標平臺選擇,建議初學用 x64)。
  2. 點擊“生成” -> “生成解決方案”。
  3. 如果成功,你會在項目目錄下的 x64/Release/ 文件夾中找到兩個重要文件:
    • MyMathDLL.dll:這就是動態鏈接庫本身,包含了編譯后的二進制代碼。
    • MyMathDLL.lib:這是導入庫,客戶端程序在鏈接時需要這個文件。它包含了幫助程序找到DLL中函數的信息。

第二部分:創建客戶端程序使用 DLL

現在,我們來創建另一個程序來調用剛剛生成的 DLL 中的函數。

方法一:隱式鏈接(最常用、最簡單)

這種方法需要在編譯時提供 .lib 文件,并在運行時需要 .dll 文件。

步驟 1:創建新的客戶端項目

  1. 在當前的 Visual Studio 解決方案中:
    • 右鍵點擊“解決方案 ‘MyMathDLL’” -> “添加” -> “新建項目”。
  2. 選擇“控制臺應用”模板,命名為 TestDLL,點擊“下一步”創建。
  3. 確保新項目 TestDLL 也被設置為 Releasex64(與 DLL 項目保持一致)。

步驟 2:配置客戶端項目

我們需要告訴客戶端項目在哪里可以找到 DLL 的頭文件和導入庫(.lib)。

  1. 添加頭文件路徑:

    • 右鍵點擊 TestDLL 項目 -> “屬性”。
    • 在左側,找到“C/C++” -> “常規”。
    • 在“附加包含目錄”中,添加 mymath.h 所在的路徑(即 DLL 項目的路徑)。例如:..\MyMathDLL
  2. 添加導入庫(.lib)路徑和文件名:

    • 在屬性頁,找到“鏈接器” -> “常規”。
    • 在“附加庫目錄”中,添加 MyMathDLL.lib 所在的路徑(即 x64/Release/)。例如:..\MyMathDLL\x64\Release
    • 找到“鏈接器” -> “輸入”。
    • 在“附加依賴項”中,添加 MyMathDLL.lib

步驟 3:編寫客戶端代碼

TestDLL 項目的源文件(如 TestDLL.c)中,寫入以下代碼:

// TestDLL.c - 客戶端程序// 包含DLL的頭文件,這樣我們就知道add和subtract函數的原型
#include "mymath.h"
#include <stdio.h>int main() {int a = 10, b = 5;// 直接調用DLL中的函數,就像調用普通函數一樣int sum = add(a, b);int difference = subtract(a, b);printf("a = %d, b = %d\n", a, b);printf("a + b = %d\n", sum);printf("a - b = %d\n", difference);return 0;
}

步驟 4:運行客戶端程序

  1. 右鍵點擊 TestDLL 項目 -> “設為啟動項目”。
  2. Ctrl + F5 運行。
  3. 重要: 確保 MyMathDLL.dll 文件在客戶端程序(TestDLL.exe)可以找到的地方。通常有幾種方法:
    • MyMathDLL.dll 復制到 TestDLL.exe 所在的目錄(即 TestDLL\x64\Release\)。
    • 或者更簡單:在 TestDLL 項目的“屬性” -> “調試” -> “工作目錄”中,設置為DLL所在的路徑(..\MyMathDLL\x64\Release)。

方法二:顯式鏈接(更靈活,更復雜)

這種方法在運行時通過 Windows API(LoadLibrary, GetProcAddress)手動加載 DLL 和獲取函數地址,不需要 .lib 和頭文件(但你需要知道函數的原型)。

客戶端代碼示例(不需要配置鏈接器):

#include <windows.h>
#include <stdio.h>// 定義函數指針類型,必須與DLL中的函數原型完全匹配
typedef int(*ADD_FUNC)(int, int);
typedef int(*SUBTRACT_FUNC)(int, int);int main() {HINSTANCE hDll;ADD_FUNC add_func;SUBTRACT_FUNC subtract_func;int a = 10, b = 5;// 1. 加載DLLhDll = LoadLibrary(TEXT("MyMathDLL.dll"));if (hDll == NULL) {printf("Failed to load DLL!\n");return 1;}// 2. 獲取函數地址add_func = (ADD_FUNC)GetProcAddress(hDll, "add");subtract_func = (SUBTRACT_FUNC)GetProcAddress(hDll, "subtract");if (add_func == NULL || subtract_func == NULL) {printf("Failed to get function address!\n");FreeLibrary(hDll);return 1;}// 3. 使用函數int sum = add_func(a, b);int difference = subtract_func(a, b);printf("a = %d, b = %d\n", a, b);printf("a + b = %d\n", sum);printf("a - b = %d\n", difference);// 4. 卸載DLLFreeLibrary(hDll);return 0;
}

使用這種方法,你只需要確保 MyMathDLL.dll 在程序可找到的路徑(如同一目錄)即可,不需要 .lib 文件和頭文件(在編譯時)。


總結

特性隱式鏈接顯式鏈接
易用性,像調用普通函數一樣,需要手動處理加載和函數指針
所需文件需要頭文件 (.h) 和導入庫 (.lib) 用于編譯只需要知道函數原型
靈活性低,啟動時如果找不到DLL則失敗,可以在運行時決定加載哪個DLL,優雅處理錯誤
性能稍快(函數調用直接)稍慢(需要通過指針調用)

對于大多數情況,隱式鏈接是更直接和首選的方法。顯式鏈接則在需要動態加載插件、處理不同版本DLL或需要更精細的錯誤控制時非常有用。

HINSTANCE是什么?

HINSTANCE 是 Windows API 中的一個核心數據類型,全稱是 Handle to an Instance(實例句柄)。

本質是什么?

HINSTANCE 實際上是一個句柄(Handle),它本質上是一個數值(在32位系統中是32位,64位系統中是64位),用來唯一標識和引用一個加載到內存中的模塊(如DLL、EXE)。

主要用途

在顯式鏈接DLL時,HINSTANCE 用來:

  1. 代表已加載的DLL模塊

    HINSTANCE hDll = LoadLibrary(TEXT("MyMathDLL.dll"));
    // 此時 hDll 就是 MyMathDLL.dll 在內存中的"身份證"
    
  2. 作為參數傳遞給 GetProcAddress() 來獲取函數地址

    ADD_FUNC add_func = (ADD_FUNC)GetProcAddress(hDll, "add");
    // 告訴系統:在 hDll 這個模塊中找名為 "add" 的函數
    
  3. 最后用于卸載DLL

    FreeLibrary(hDll);  // 通過句柄告訴系統卸載哪個DLL
    

底層細節

  • 32位系統HINSTANCEvoid* 類型,占4字節
  • 64位系統HINSTANCEvoid* 類型,占8字節
  • 實際定義:typedef HMODULE HINSTANCE;HMODULE 是模塊句柄)

簡單類比

你可以把 HINSTANCE 理解為:

  • 文件操作中的文件句柄(就像 FILE*
  • 指針的指針:它不直接指向DLL代碼,而是指向一個系統內部管理該DLL的數據結構

實際使用示例

#include <windows.h>
#include <stdio.h>typedef int(*ADD_FUNC)(int, int);int main() {// 1. 加載DLL,返回該DLL的實例句柄HINSTANCE hMyDll = LoadLibrary(TEXT("MyMathDLL.dll"));if (hMyDll == NULL) {printf("無法加載DLL! 錯誤代碼: %d\n", GetLastError());return 1;}// 2. 使用句柄來獲取函數地址ADD_FUNC add = (ADD_FUNC)GetProcAddress(hMyDll, "add");if (add == NULL) {printf("找不到函數! \n");FreeLibrary(hMyDll);return 1;}// 3. 使用獲取到的函數int result = add(10, 5);printf("10 + 5 = %d\n", result);// 4. 卸載DLL,傳入要卸載的DLL的句柄FreeLibrary(hMyDll);return 0;
}

總結

HINSTANCE 是 Windows 系統中用來標識和管理加載到內存中的模塊(如DLL)的句柄,在顯式鏈接DLL時起著關鍵作用,相當于你與操作系統之間關于該DLL的"通信憑證"。

客戶端代碼中,下面兩行代碼是什么意思:
ADD_FUNC add_func;
SUBTRACT_FUNC subtract_func;

這兩行代碼是函數指針變量的聲明

詳細解釋

1. 代碼含義

ADD_FUNC add_func;
SUBTRACT_FUNC subtract_func;
  • 聲明了兩個變量add_funcsubtract_func
  • 變量類型ADD_FUNCSUBTRACT_FUNC(之前用 typedef 定義的類型)
  • 用途:這兩個變量將用來存儲從DLL中獲取的函數地址

2. 類比理解

可以類比為普通的變量聲明:

int number;          // 聲明一個整型變量
ADD_FUNC add_func;   // 聲明一個函數指針變量
普通變量聲明函數指針聲明含義
int number;ADD_FUNC add_func;聲明一個變量
number 存儲整數add_func 存儲函數地址變量存儲的內容
number = 10;add_func = GetProcAddress(...);給變量賦值

3. 在完整代碼中的角色

// 1. 定義函數指針類型(藍圖)
typedef int(*ADD_FUNC)(int, int);
typedef int(*SUBTRACT_FUNC)(int, int);int main() {// 2. 聲明函數指針變量(創建容器)ADD_FUNC add_func;SUBTRACT_FUNC subtract_func;// 3. 給變量賦值(獲取真正的函數地址)add_func = (ADD_FUNC)GetProcAddress(hDll, "add");subtract_func = (SUBTRACT_FUNC)GetProcAddress(hDll, "subtract");// 4. 使用變量(通過指針調用函數)int result = add_func(10, 5);return 0;
}

4. 另一種寫法:聲明時直接賦值

你也可以在聲明的同時賦值:

// 聲明并立即賦值
ADD_FUNC add_func = (ADD_FUNC)GetProcAddress(hDll, "add");
SUBTRACT_FUNC subtract_func = (SUBTRACT_FUNC)GetProcAddress(hDll, "subtract");

5. 為什么要這樣寫?

這樣設計的好處是:

  • 類型安全:編譯器會檢查函數簽名是否匹配
  • 代碼清晰:通過類型名就知道這個指針指向什么類型的函數
  • 易于使用:可以像調用普通函數一樣調用 add_func(10, 5)

總結

ADD_FUNC add_func; 這行代碼的意思是:聲明一個名為 add_func 的變量,這個變量的類型是 ADD_FUNC(即指向一個接受兩個int參數并返回int的函數的指針),用來存儲從DLL中獲取的 add 函數的地址。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/98112.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/98112.shtml
英文地址,請注明出處:http://en.pswp.cn/web/98112.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Elasticsearch集群節點部署與索引策略對比分析及性能優化

Elasticsearch集群節點部署與索引策略對比分析及性能優化 本文深入探討了Elasticsearch在生產環境中不同集群節點部署模式與索引策略的多種方案&#xff0c;比較了各自優缺點&#xff0c;并結合真實業務場景的性能測試結果&#xff0c;提出選型建議與優化實踐&#xff0c;幫助有…

神經網絡與深度學習基礎:從線性回歸到分類模型

主題12??核心模型??線性回歸 → 神經網絡Softmax回歸??解決問題??回歸問題&#xff08;預測連續值&#xff09;分類問題&#xff08;預測離散類別&#xff09;??關鍵創新??引入激活函數解決線性不可分問題引入獨熱編碼和交叉熵損失解決分類問題??優化算法??梯度…

前端開發的破局與突圍:AI賦能、全棧架構與跨端開發新路徑

作為一名前端開發者,你是否曾感到焦慮:技術迭代太快,競爭越來越激烈?別擔心,新的機遇正在涌現!本文將帶你探索前端開發的新出路。 這里寫目錄標題 前言:前端開發的現狀與挑戰 一、AI賦能:從"代碼編寫"到"智能協作" 1. AI代碼助手大幅提升開發效率 …

Java 在 Excel 中查找并高亮數據:詳細教程

在日常的開發工作中&#xff0c;我們經常需要處理各種格式的數據&#xff0c;其中 Excel 文件因其廣泛的應用而占據重要地位。面對海量的 Excel 數據&#xff0c;如何高效地查找特定內容并進行標記&#xff08;如高亮顯示&#xff09;&#xff0c;成為了許多開發者和辦公自動化…

Tessent_ijtag_ug——第 5 章IJTAG 網絡插入 (1)

第 5 章IJTAG 網絡插入 IJTAG 網絡插入功能使您能夠連接現有的instrument&#xff0c;并插入 SIB、TDR 和 ScanMux 以創建您自己的 IJTAG 網絡。 IJTAG 網絡插入功能使您能夠將網絡連接到 TAP 控制器或設計中已有的 TAP 控制器。IJTAG 網絡插入的原理是使用 create_dft_specifi…

同步與互斥學習筆記

一、基本概念同步與互斥是多任務/多線程編程中的兩個核心機制&#xff1a;同步&#xff1a;指多個任務之間存在明確的先后順序&#xff0c;一個任務必須等待另一個任務完成某些操作后才能繼續執行。互斥&#xff1a;指多個任務在同一時刻爭搶使用同一資源&#xff08;臨界資源&…

Tomcat 啟動流程與類加載機制

Tomcat 啟動流程與類加載機制1. 引言 Tomcat 的啟動不僅僅是簡單的 java -jar 或 catalina.sh start。 它背后包含 Bootstrap 啟動器、Catalina 控制器、Server/Service/Connector/Container 初始化 等關鍵步驟。 另一方面&#xff0c;Tomcat 為了支持 熱部署、不同應用間類隔離…

MTK Linux Charger驅動分析(十二)- mtk_pd_adapter.c

1. 代碼整體分析 mtk_pd_adapter.c(源文件) 主要內容: 該文件實現了MediaTek平臺的USB PD(Power Delivery)適配器驅動,基于Linux內核的電源管理和Type-C端口控制器(TCPC)框架。 它處理PD協議事件,包括PD連接狀態、Type-C狀態、水檢測(WD_STATUS)、Sink VBUS變化等。…

Spring Boot Logback 日志配置詳解:從基礎到分布式追蹤

日志是應用程序不可或缺的組成部分&#xff0c;它不僅能幫助我們調試問題&#xff0c;還能監控系統運行狀態。在 Spring Boot 生態中&#xff0c;Logback 憑借其高性能和靈活性成為首選的日志框架。本文將通過一個實際的 Logback 配置文件&#xff0c;詳細解析其各個組件的功能…

軟件體系結構——后端三層架構

三層架構——Controller、Service、Dao 不僅是對代碼進行的邏輯分層。其真正的本質&#xff0c;是將業務、技術和數據剝離。搞業務的專心做業務&#xff0c;搞技術的專心搞技術&#xff0c;做數據存儲的專心做數據存儲。三方通過接口進行對接&#xff0c;任一部分重構&#xff…

QML學習筆記(一)基本了解和工程配置

前言&#xff1a; 已經從事QT開發幾年了&#xff0c;但對于QML這個東西始終是沒有徹底掌握&#xff0c;一方面實際工作中沒有用到過&#xff0c;其次它的語法對我來說是全新的東西&#xff0c;不像QWidget那一套可以直接在C中去寫。這就是為什么網上都說qml更簡單&#xff0c;我…

SAP HANA Scale-out 04:緩存

結果緩存靜態結果緩存 Vs 動態結果緩存FeatureStatic Result CacheDynamic Result CacheTarget Scenario對復雜視圖&#xff08;通常是頂層視圖&#xff09;的查詢頻繁更新的大表&#xff08;例如ACDOCA&#xff09;上的聚合查詢Query result非實時數據實時數據ScopeTarget obj…

嘉興禾潤 HTR7216 (S) LED 驅動芯片:特性與應用

在如今智能設備飛速普及的時代&#xff0c;無論是智能家居的氛圍營造、IoT 設備的狀態提示&#xff0c;還是個人消費電子的視覺呈現&#xff0c;都離不開高性能 LED 驅動芯片的支撐。嘉興禾潤推出的 HTR7216 (S) LED 驅動芯片&#xff0c;憑借豐富的功能、精準的控制以及出色的…

Python實現劍龍優化算法 (Stegosaurus Optimization Algorithm, SOA)優化函數(付完整代碼)

Python實現劍龍優化算法 (Stegosaurus Optimization Algorithm, SOA)優化函數&#xff08;付完整代碼&#xff09;1.劍龍優化算法介紹劍龍優化算法&#xff08;Stegosaurus Optimization Algorithm&#xff0c;SOA&#xff09;是一種受劍龍獨特生理結構和行為模式啟發而設計的元…

分布式拜占庭容錯算法——權益證明(PoS)算法詳解

Java 實現權益證明&#xff08;PoS&#xff09;算法詳解 一、PoS 核心機制 #mermaid-svg-Sbj0HU6MjOl1yo5L {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Sbj0HU6MjOl1yo5L .error-icon{fill:#552222;}#mermaid-s…

【論文閱讀】谷歌:生成式數據優化,只需請求更好的數據

谷歌DeepMind團隊通過Generative Data Refinement&#xff08;GDR&#xff09;技術&#xff0c;成功將極端有毒的4chan討論數據轉化為安全且語義豐富的訓練素材&#xff0c;推動了LLM訓練數據凈化的新范式&#xff1a; ? GDR利用預訓練大模型對原始數據進行“重寫”&#xff0…

C++ 多線程實戰 10|C++20 的信號量、閂鎖與屏障

目錄 前言 學習目標 1. 信號量&#xff08;Semaphore&#xff09; 示例&#xff1a;限制并發下載任務 2. 閂鎖&#xff08;Latch&#xff09; 示例&#xff1a;賽跑 3. 屏障&#xff08;Barrier&#xff09; 示例&#xff1a;圖像處理流水線 4. 常見坑與對策 5. 實踐作…

【Java SE】01. 初識Java

1. 認識Java Java是一種優秀的程序設計語言&#xff0c;它具有令人賞心悅目的語法和易于理解的語義。Java還是一個有一系列計算機軟件和規范形成的技術體系&#xff0c;這個技術體系提供了完整的用于軟件開發和跨平臺部署的支持環境&#xff0c;并廣泛應用于嵌入式系統、移動終…

解鎖倉儲智能調度、運輸路徑優化、數據實時追蹤,全功能降本提效的智慧物流開源了

AI 視頻監控平臺&#xff1a;全鏈路協同驅動的智能監控解決方案AI 視頻監控平臺是一款融合高性能功能與輕量化操作的實時算法驅動型視頻監控系統&#xff0c;其核心愿景在于深度破除不同芯片廠商間的技術壁壘&#xff0c;省去冗余重復的適配環節&#xff0c;最終達成芯片、算法…