主頁 > 作業系統 > 元件(DLL)

元件(DLL)

2020-10-01 23:27:56 作業系統

元件和靜態鏈接庫:

元件一般不能直接執行,而且它們一般也不接收訊息,

它們是包含許多函式的獨立檔案,這些函式可以被應用程式和其他 DLL 呼叫以完成某些特定的作業,

一個元件只有在另外一個模塊呼叫其所包含的函式時才被啟動,

 

“靜態鏈接” 一般是在程式開發程序中發生的,用于把一些檔案鏈接在一起創建一個 Windows 可執行檔案,

這些檔案包括各種各樣的物件模塊(.OBJ),運行時庫檔案(.LIB),通常還有已編譯的資源檔案(.RES),

與其相反,動態鏈接則發生在程式運行時, 

靜態庫:函式和資料被編譯進一個二進制檔案,擴展名為(.lib),

在使用靜態庫的情況下,在編譯鏈接可執行檔案時:

聯結器從靜態庫中復制這些函式和資料,并把它們和應用程式的其他模塊組合起來創建最終的可執行檔案(.exe),

當發布產品時,只需要發布這個可執行檔案,并不需要發布被使用的靜態庫,

 

“動態鏈接” 是指 Windows 的鏈接程序,在這個程序中它把模塊中的函式呼叫與在庫模塊中的實際函式鏈接在一起,

動態庫:在使用動態庫時,往往提供兩個檔案:一個匯入庫(.lib,非必須) 和一個(.dll)檔案,

匯入庫和靜態庫本質上的區別

靜態庫本身就包含了實際執行代碼和地址符號表等資料,

而對于匯入庫而言,其實際的執行代碼位于動態庫中匯入庫只包含了地址符號表等,確保程式找到對應函式的一些基本地址資訊,

元件的標準擴展名是(.dll),只有擴展名為(.dll)的元件才能被 Windows 作業系統自動加載,

如果該檔案有另外的擴展名,則程式必須明確地用 LoadLibrary() 或 LoadLibraryEx() 加載相應模塊,

 

撰寫元件

我們撰寫的程式都可以根據 UNICODE 識別符號的定義編譯成能夠處理 UNICODE 或者 非 UNICODE 字串的程式,

在創建一個 DLL 時,對于任何有字符或者字串引數的函式,它都應該包括 UNICODE 和非 UNICODE 兩個版本,

 VC++6.0 編譯器下:

File->New->Win32 Dynamic-Link Library->An empty DLL project || An Simple DLL project

An empty DLL project 和 An Simple DLL project 的區別是:后者有個簡單的示例代碼,

我以前者為例:

新建兩個檔案:MyDLL.h,MyDLL.cpp,

// MyDLL.h
#define Import extern "C" _declspec(dllexport)
Import int sum(int a, int b);
Import int sub(int a, int b);
// MyDLL.cpp#include"MyDLL.h"
Import int sum(int a, int b)
{
     return a+b;
}
Import int sub(int a, int b)
{
     return a-b;
}

最后編譯 MyDLL.cpp,如果成功則在 Debug 里可以看到 MyDLL.dll,

提示:

函式宣告前加上 "_declspec(dllexport)" 表明函式將輸出為元件,是必不可少的,

在相同的呼叫約定下,采用不同的編譯器,對函式名的修飾是不一樣的,

例如:C語言和C++語言匯出的dll檔案中,函式的修飾名是不一樣的,

如果要C語言風格的(.dll)檔案,就要再加上 "extern C" 進行修飾,或者把源檔案名的后綴改為(.c),

如果是要C++風格的(.dll)檔案,則源檔案名后綴必須為(.cpp),

 

呼叫方式:

隱式呼叫:

將 MyDLL.lib 和 MyDLL.h 拷貝到需要應用該 DLL 的工程的目錄下,將 MyDLL.dll 拷貝到產生的應用程式的目錄下,

并在需要應用該 DLL 中的函式的 CPP 檔案開頭添加如下幾行:

#include"MyDLL.h"
#pragma comment(lib,"MyDLL")

例如:

// MyDLL.cpp#include<stdio.h>
#include"MyDLL.h"
#pragma comment(lib,"MyDLL")
int main(void)
{
    printf("3+6=%d\n",sum(3,6));
    printf("8-6=%d\n",sub(8,6));
    return 0;
}

顯式呼叫:

1、將 MyDLL.lib 和 MyDLL.h 拷貝到需要應用該 DLL 的工程的目錄下,將 MyDLL.dll 拷貝到產生的應用程式的目錄下,

      在添加 CPP 檔案之前一步,需要在 Project->Setting->Link->Object/library modules 的框中增加 MyDll.lib 這個庫,

      最后,在創建的 CPP 檔案的開頭添加這一行:

#include"MyDLL.h"

現在就可以使用這個 DLL 檔案了,

2、將 MyDLL.lib 和 MyDLL.h 拷貝到需要應用該 DLL 的工程的目錄下,將 MyDLL.dll 拷貝到產生的應用程式的目錄下,

      簡單的呼叫 DLL 檔案的 CPP 檔案如下:

// MyDLL.cpp#include<stdio.h>
#include<windows.h>
int main(void)
{
    HMODULE hModule;
    typedef int (*pSum)(int a, int b);
    typedef int (*pSub)(int a, int b);
    pSum Sum = NULL;
    pSub Sub = NULL;
    hModule = LoadLibrary("MyDLL.dll");
    Sum = (pSum)GetProcAddress(hModule,"sum");
    Sub = (pSum)GetProcAddress(hModule,"sub");
    printf("3+6=%d\n",Sum(3,6));
    printf("8-6=%d\n",Sub(8,6));
    return 0;
}

介紹一下兩個函式:

LoadLibrary() 介紹:

功能:將指定模塊加載到呼叫行程的地址空間中,指定的模塊可能會導致加載其他模塊,

函式原型:HMODULE WINAPI LoadLibrary(

                  LPCTSTR lpFileName // 元件的名字,

                  );

回傳值:如果函式成功, 則回傳值是模塊的句柄,如果函式失敗, 回傳值為 NULL,

 

GetProcAddress() 介紹:

功能:從指定的元件 (DLL) 中檢索匯出函式或變數的地址,

函式原型:FARPROC WINAPI GetProcAddress(

                  HMODULE hModule,  // 模塊的句柄,

                  LPCSTR  lpProcName  // 函式或變數的名字, 或函式的序號值,

                  );

回傳值:如果函式成功, 則回傳值是匯出函式或變數的地址,如果函式失敗, 回傳值為 NULL,

 

再來看一下這段代碼:     typedef int (*pSum)(int a, int b);

我們通常見到的都是:     typedef unsigned Long uLong;

其實 typedef int (*pSum)(int a, int b); 的意思也挺好理解的:

就是定義一個別名為 pSum 函式指標,指向回傳值為 int 型并且含有兩個 int 型引數的函式指標,


VS2017下:

其實 VS2017 下的步驟和上面也一樣,這里介紹一下模塊定義檔案創建 DLL 檔案:

檔案->新建->專案->DLL

// MyDLL.cpp#include "stdafx.h"
int sum(int a, int b)
{
    return a + b;
}

int sub(int a, int b)
{
    return a - b;
}

解決方案->資源檔案->添加->新建項->代碼->模塊定義檔案

// Source.def
LIBRARY

EXPORTS
sum
sub

專案->MyDLL屬性->聯結器->輸入->模塊定義檔案:Source.def

最后:生成->MyDLL

 

隱式呼叫和顯式呼叫的對比:

1、隱式鏈接方式實作簡單,一開始就把dll加載進來,在需要呼叫的時候直接呼叫即可,

     但是如果程式要訪問十多個 DLL 檔案,如果都采用隱式鏈接方式加載他們的話,在該程式啟動時:

     這些dll都需要被加載到記憶體中,并映射到呼叫行程的地址空間,這樣將加大程式的啟動時間,

     而且一般來說,在程式運行程序中只是在某個條件滿足的情況下才需要訪問某個dll中的函式,

     這樣如果所有dll都被加載到記憶體中,資源浪費是比較嚴重的,

2、顯示加載的方法則可以解決上述問題,DLL 只有在需要用到的時候才會被加載到記憶體中,

     另外,其實采用隱式鏈接方式訪問 DLL 時,在程式啟動時也是通過呼叫 LoadLibrary() 加載該行程需要的元件的,

 

帶有 API 函式的 元件:

創建方式相同,只是得有個 DllMain() 入口函式,

// Dll.h 
/*
#define Import extern "C" _declspec(dllexport)
Import void Text(void);
*/
#include"Dll.h"
#include<windows.h>
BOOL APIENTRY DllMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpRserved)
{
   switch(ul_reason_for_call)
    {
     case DLL_PROCESS_ATTACH:
          Text();
          break;
     case DLL_PROCESS_DETACH:
          break;
     case DLL_THREAD_ATTACH:
          break;
     case DLL_THREAD_DETACH:
          break;
    }
   return 0;
}
Import void Text(void)
{
   MessageBox (NULL, TEXT ("Hello, World!"), TEXT ("HelloMsg"), MB_OKCANCEL);
}

DllMain() 簡介:

功能:元件 (DLL) 中的可選入口點,

函式原型:BOOL APIENTRY DllMain(
                  HMODULE hModule,  // DLL 模塊的句柄,
                  DWORD  ul_reason_for_call,  // 指示為什么呼叫 DLL 入口點函式的原因代碼,
                  LPVOID   lpvReserved // 保留值,通常為 NULL,
                  );

引數:ul_reason_for_call

含義
DLL_PROCESS_ATTACH 當 dll 檔案第一次被行程加載時,呼叫該值下的 DLL 函式,
DLL_PROCESS_DETACH 當 dll 檔案從行程中被解除時,呼叫該值下的 DLL 函式,TerminateProcess() 除外,
DLL_THREAD_ATTACH 當行程創建一個執行緒時,新建的執行緒將呼叫該值下的 DLL 函式,
DLL_THREAD_DETACH 當執行緒呼叫 ExitThread() 結束時,該行程將呼叫該值下的 DLL 函式,TerminateThread() 除外,

回傳值:當系統使用 DLL_PROCESS_ATTACH 值呼叫 DllMain 函式時,

               如果呼叫成功則回傳 TRUE,否則回傳 FALSE,

轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/146616.html

標籤:Windows

上一篇:linux

下一篇:元件(DLL)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • CA和證書

    1、在 CentOS7 中使用 gpg 創建 RSA 非對稱密鑰對 gpg --gen-key #Centos上生成公鑰/密鑰對(存放在家目錄.gnupg/) 2、將 CentOS7 匯出的公鑰,拷貝到 CentOS8 中,在 CentOS8 中使用 CentOS7 的公鑰加密一個檔案 gpg -a ......

    uj5u.com 2020-09-10 00:09:53 more
  • Kubernetes K8S之資源控制器Job和CronJob詳解

    Kubernetes的資源控制器Job和CronJob詳解與示例 ......

    uj5u.com 2020-09-10 00:10:45 more
  • VMware下安裝CentOS

    VMware下安裝CentOS 一、軟硬體準備 1 Centos鏡像準備 1.1 CentOS鏡像下載地址 下載地址 1.2 CentOS鏡像下載程序 點擊下載地址進入如下圖的網站,選擇需要下載的版本,這里選擇的是Centos8,點擊如圖所示。 決定選擇Centos8后,選擇想要的鏡像源進行下載,此 ......

    uj5u.com 2020-09-10 00:12:10 more
  • 如何使用Grep命令查找多個字串

    如何使用Grep 命令查找多個字串 大家好,我是良許! 今天向大家介紹一個非常有用的技巧,那就是使用 grep 命令查找多個字串。 簡單介紹一下,grep 命令可以理解為是一個功能強大的命令列工具,可以用它在一個或多個輸入檔案中搜索與正則運算式相匹配的文本,然后再將每個匹配的文本用標準輸出的格式 ......

    uj5u.com 2020-09-10 00:12:28 more
  • git配置http代理

    git配置http代理 經常遇到克隆 github 慢的問題,這里記錄一下幾種配置 git 代理的方法,解決 clone github 過慢。 目錄 git配置代理 git單獨配置github代理 git配置全域代理 配置終端環境變數 git配置代理 主要使用 git config 命令 git單獨 ......

    uj5u.com 2020-09-10 00:12:33 more
  • Linux npm install 裝包時提示Error EACCES permission denied解

    npm install 裝包時提示Error EACCES permission denied解決辦法 ......

    uj5u.com 2020-09-10 00:12:53 more
  • Centos 7下安裝nginx,使用yum install nginx,提示沒有可用的軟體包

    Centos 7下安裝nginx,使用yum install nginx,提示沒有可用的軟體包。 18 (flaskApi) [root@67 flaskDemo]# yum -y install nginx 19 已加載插件:fastestmirror, langpacks 20 Loading ......

    uj5u.com 2020-09-10 00:13:13 more
  • Linux查看服務器暴力破解ssh IP

    在公網的服務器上經常遇到別人爆破你服務器的22埠,用來挖礦或者干其他嘿嘿嘿的事情~ 這種情況下正確的做法是: 修改默認ssh的22埠 使用設定密鑰登錄或者白名單ip登錄 建議服務器密碼為復雜密碼 創建普通用戶登錄服務器(root權限過大) 建立堡壘機,實作統一管理服務器 統計爆破IP [root ......

    uj5u.com 2020-09-10 00:13:17 more
  • CentOS 7系統常見快捷鍵操作方式

    Linux系統中一些常見的快捷方式,可有效提高操作效率,在某些時刻也能避免操作失誤帶來的問題。 ......

    uj5u.com 2020-09-10 00:13:31 more
  • CentOS 7作業系統目錄結構介紹

    作業系統存在著大量的資料檔案資訊,相應檔案資訊會存在于系統相應目錄中,為了更好的管理資料資訊,會將系統進行一些目錄規劃,不同目錄存放不同的資源。 ......

    uj5u.com 2020-09-10 00:13:35 more
最新发布
  • vim的常用命令

    Vim的6種基本模式 1. 普通模式在普通模式中,用的編輯器命令,比如移動游標,洗掉文本等等。這也是Vim啟動后的默認模式。這正好和許多新用戶期待的操作方式相反(大多數編輯器默認模式為插入模式)。 2. 插入模式在這個模式中,大多數按鍵都會向文本緩沖中插入文本。大多數新用戶希望文本編輯器編輯程序中一 ......

    uj5u.com 2023-04-20 08:43:21 more
  • vim的常用命令

    Vim的6種基本模式 1. 普通模式在普通模式中,用的編輯器命令,比如移動游標,洗掉文本等等。這也是Vim啟動后的默認模式。這正好和許多新用戶期待的操作方式相反(大多數編輯器默認模式為插入模式)。 2. 插入模式在這個模式中,大多數按鍵都會向文本緩沖中插入文本。大多數新用戶希望文本編輯器編輯程序中一 ......

    uj5u.com 2023-04-20 08:42:36 more
  • docker學習

    ###Docker概述 真實專案部署環境可能非常復雜,傳統發布專案一個只需要一個jar包,運行環境需要單獨部署。而通過Docker可將jar包和相關環境(如jdk,redis,Hadoop...)等打包到docker鏡像里,將鏡像發布到Docker倉庫,部署時下載發布的鏡像,直接運行發布的鏡像即可。 ......

    uj5u.com 2023-04-19 09:26:53 more
  • 設定Windows主機的瀏覽器為wls2的默認瀏覽器

    這里以Chrome為例。 1. 準備作業 wsl是可以使用Windows主機上安裝的exe程式,出于安全考慮,默認情況下改功能是無法使用。要使用的話,終端需要以管理員權限啟動。 我這里以Windows Terminal為例,介紹如何默認使用管理員權限打開終端,具體操作如下圖所示: 2. 操作 wsl ......

    uj5u.com 2023-04-19 09:25:49 more
  • docker學習

    ###Docker概述 真實專案部署環境可能非常復雜,傳統發布專案一個只需要一個jar包,運行環境需要單獨部署。而通過Docker可將jar包和相關環境(如jdk,redis,Hadoop...)等打包到docker鏡像里,將鏡像發布到Docker倉庫,部署時下載發布的鏡像,直接運行發布的鏡像即可。 ......

    uj5u.com 2023-04-19 09:19:04 more
  • Linux學習筆記

    IP地址和主機名 IP地址 ifconfig可以用來查詢本機的IP地址,如果不能使用,可以通過install net-tools安裝。 Centos系統下ens33表示主網卡;inet后表示IP地址;lo表示本地回環網卡; 127.0.0.1表示代指本機;0.0.0.0可以用于代指本機,同時在放行設 ......

    uj5u.com 2023-04-18 06:52:01 more
  • 解決linux系統的kdump服務無法啟動的問題

    問題:專案麒麟系統服務器的kdump服務無法啟動,沒有相關日志無法定位問題。 1、查看服務狀態是關閉的,重啟系統也無法啟動 systemctl status kdump 2、修改grub引數,修改“crashkernel”為“512M(有的機器數值太大太小都會導致報錯,建議從128M開始試,或者加個 ......

    uj5u.com 2023-04-12 09:59:50 more
  • 解決linux系統的kdump服務無法啟動的問題

    問題:專案麒麟系統服務器的kdump服務無法啟動,沒有相關日志無法定位問題。 1、查看服務狀態是關閉的,重啟系統也無法啟動 systemctl status kdump 2、修改grub引數,修改“crashkernel”為“512M(有的機器數值太大太小都會導致報錯,建議從128M開始試,或者加個 ......

    uj5u.com 2023-04-12 09:59:01 more
  • 你是不是暴露了?

    作者:袁首京 原創文章,轉載時請保留此宣告,并給出原文連接。 如果您是計算機相關從業人員,那么應該經歷不止一次網路安全專項檢查了,你肯定是收到過資訊系統技術檢測報告,要求你加強風險監測,確保你提供的系統服務堅實可靠了。 沒檢測到問題還好,檢測到問題的話,有些處理起來還是挺麻煩的,尤其是線上正在運行的 ......

    uj5u.com 2023-04-05 16:52:56 more
  • 細節拉滿,80 張圖帶你一步一步推演 slab 記憶體池的設計與實作

    1. 前文回顧 在之前的幾篇記憶體管理系列文章中,筆者帶大家從宏觀角度完整地梳理了一遍 Linux 記憶體分配的整個鏈路,本文的主題依然是記憶體分配,這一次我們會從微觀的角度來探秘一下 Linux 內核中用于零散小記憶體塊分配的記憶體池 —— slab 分配器。 在本小節中,筆者還是按照以往的風格先帶大家簡單 ......

    uj5u.com 2023-04-05 16:44:11 more