元件和靜態鏈接庫:
元件一般不能直接執行,而且它們一般也不接收訊息,
它們是包含許多函式的獨立檔案,這些函式可以被應用程式和其他 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)
