跳到主要內容

Windows DLL - How to use DLL?

Introduction

網路上能夠找到許多和DLL相關的教學。但我相信大家看了之後依然會存在許多疑問,或者想只要會用就好並不會去了解更深一層的意義。有時我會想: 別人所撰寫的API,我們是不是應該至少要去了解其原理或設計呢? 如果一直當個只懂Call API的Programmer,而沒去學習更多解決問題的方法,只要換個語言應該就和廢人無異了吧! 言歸正傳,今天這篇教學是特地寫給Albert兄的,教導大家DLL使用的兩種方法: 裝載期間動態連結與執行期間動態連結。

Write a DLL

寫DLL相當簡單。如果是VC的用戶,直接透過Wizard產生出DDL的專案,基本的Skeleton都有了。其中的2個重點如下: 匯出函式宣告與DLL檔定義。

匯出的函式我們必須使用_declspec(dllexport)或_declspec(dllimport)宣告,目的是為了讓裝載期間動態連結能夠找到DLL對應的函式。

#ifdef MYDLLHELLOWORLD_EXPORTS
#define MYDLLHELLOWORLD_API __declspec(dllexport)
#else
#define MYDLLHELLOWORLD_API __declspec(dllimport)
#endif
 
// Declare export functions
MYDLLHELLOWORLD_API void showHelloWorldString( LPCTSTR s);

如果是在執行期間動態連結,如果不是使用__declspec(dllexport),我們就必須宣告def檔。VC中新增def的畫面如下:

接著將要匯出的函式加入def中:

LIBRARY	"DLLHelloWolrd"
EXPORTS
	showHelloWorldString @1

在編譯之後,會產生DLLHelloWolrd.dll與DLLHelloWolrd.lib,這兩個檔案在使用DLL時是所被需要的。

裝載期間動態連結

使用裝載期間動態連結的重點如下:

  1. 將lib放置專案目錄下。
  2. 宣告_declspec(dllexport) void showHelloWorldString( LPCTSTR s)或_declspec(dllimport) void showHelloWorldString( LPCTSTR s)。一般會直接將DDL的header file include進來,目前看過third-party的library都會附上dll、lib與header file。
  3. 加入#pragma comment宣告我們所要載入的lib。如果這個lib不正確,在編譯時就會發生連結錯了。lib與dll相同也有32位元與64位元之區別。

#include "stdafx.h"
#include "DLLHelloWorld.h"
#include <atlbase.h> 
#include <iostream>
 
using namespace std;
 
#pragma comment(lib, "DLLHelloWolrd")
 
int _tmain(int argc, _TCHAR* argv[])
{
	LPCWSTR pw = W2T( L"Hello world!");
	showHelloWorldString(pw);
 
	system("pause");
	return 0;
}

執行期間動態連結

使用執行期間動態連結的重點如下:

宣告欲呼叫之函式之function pointer,如typedef void (*PFNEXPORTFUNC)(LPCTSTR)。

LoadLibrary與FreeLibrary。使用DLL之前,必須使用LoadLibrary將它載入,如果不是傳路徑給檔而是使用檔名,它會有一定的搜尋路徑。如果Load有問題,可確認HMODULE是否為NULL,最後用完當然要釋放資源。

透過GetProcAddress取得函式在DLL中的執行位置並呼叫。如GetProcAddress( hModule, “showHelloWorldString”),showHelloWorldString為要呼叫的函式名稱。也可透過判斷取得結果是否為NULL去確認呼叫是否正確。

#include "stdafx.h"
#include <atlbase.h> 
 
using namespace std;
 
LPCWSTR hwdll = W2T(L"DLLHelloWolrd.dll");
typedef void (*PFNEXPORTFUNC)(LPCTSTR);
 
int _tmain(int argc, _TCHAR* argv[])
{
	HMODULE hModule = ::LoadLibrary(hwdll);
	if( hModule != NULL ){
 
		PFNEXPORTFUNC fnHelloWorldDLL = (PFNEXPORTFUNC)::GetProcAddress( hModule, "showHelloWorldString");
		if( fnHelloWorldDLL != NULL ){
			fnHelloWorldDLL( W2T( L"Hello world!"));
		} else {
			cout << "Init function failure!" << endl;
		}
 
		FreeLibrary(hModule);
	} else {
		cout << "Load " << hwdll << " failure!" << endl;
	}
	system("pause");
 
	return 0;
}

總結

到底用哪一個方法好呢? 裝載期間動態連接的優點是: 我們不需要去宣告有的沒的函式指標,也不用擔心介面宣告錯誤的問題。 但缺點是: 如果DDL檔案一但不見,這隻程式會永遠起不來。執行期間動態連結的缺點是: 一堆有的沒的函式指標宣告,稍微打錯就ㄘㄨㄚˋ賽了。 但優點是: 由於是在執行期間載入,如果讀取不到會獲得一個NULL,程式設計人員也可以根據這種情況做些例外處置。舉例來說,支援32與64位元的程式,會載入32與64位元的DLL。假如它所使用的名稱如example32.dll與example64.dll,我們可以透過先讀取example32.dll是否成功,再決定是否讀取example64.dll。另外一種使用情況也許是對於功能的alternative選擇。

另外大家看完這篇文章後,可能還有以下疑問:

  1. dllexport與dllimport差在哪?
  2. showHelloWorldString @1的@1有何用途?

第一個問題經過Silvius的指導, dllexport與dllimport的函義如下:

  • 以上述的例子而言,DLL專案在前處理器會加入MYDLLHELLOWORLD_EXPORTS一同編譯。這將使得函式宣告為dllexport以做匯出之用。
  • 而使用此DLL的Client,會使用dllimport宣告,將DLL函式做匯入之用。


第二個問題,可以參考此篇,是為了相容於舊系統使用序數的載入方式。

Reference

留言

這個網誌中的熱門文章

PostgreSQL - Unattended installation on windows

Introduction 要將別人軟體包裝到自己軟體中,不可或缺的東西就是Unattended installation。以Unattended installation來說,我們可以選擇透過Installer的silent mode安裝,也可以透過把目標軟體做成portable的版本。本篇文章分享這兩種方法,教導大家如何將PostgreSQL透過Unattended installation方式安裝到目標系統成為service。 Note. 本篇以PostgreSQL 10.7為例。 Install with installer Tips 安裝程式或反安裝程式的參數,除了可以直接上官網搜尋Installation User Guide以外,也可以直接使用help參數查詢: postgresql- 10.7 - 2 -windows-x64.exe --help Windows安裝程式主要有EnterpriseDB與BigSQL兩種。BigSQL版本安裝元件是透過網路下載且支援參數不如EnterpriseDB版本多,以我們需求來說,我們傾向於使用EnterpriseDB版本。接下來分享給大家安裝與反安裝方法。 Installation @ echo off set INSTALL_DIR =C:\postgres10 set INSTALLER =postgresql- 10.7 - 2 -windows-x64.exe   rem options for installation set SSMDB_SERVICE =postgresql- 10 set MODE =--unattendedmodeui none --mode unattended   set DB_PASSWD =--superpassword postgres set DB_PORT =--serverport 5432   set SERVICE_NAME =--servicename % SSMDB_SERVICE %   set PREFIX =--prefix "%INSTALL_DIR%" set DATA_DIR =--datadir "%INSTALL_DIR%\data"   set OPTIONS =

How to install RIDE on Windows?

Introduction 多年沒在Windows上開發RobotFramework,趁著這次整理一下RIDE安裝方法。 目前RIDE最新版本與Python對應版本如下: (3.6 < python <= 3.11) Install current released version (2.0.8.1) with: pip install -U robotframework-ride 安裝Python 直接到Python官網找尋最新的3.11版本,我使用3.11.9: link 。安裝就是一直下一步而已。 安裝wxPython 每次安裝RIDE最困難的都是wxPython。看了一下 官網 描述,我就姑且相信一下: 接著進入下 載頁面 就有安裝教學。基本上就是到Python目錄下的Scripts直接執行以下command: pip install -U wxPython 安裝RIDE 接著就如RIDE官網所說,執行以下command: pip install -U robotframework-ride 啟動RIDE 直接在相同目錄下執行ride就可以啟動了,你也可以直接在桌面建ride連結,加快下次啟動時間。 沒想到這次這麼順利就安裝完成了。因為我是使用java去啟動robot framework,就不特別講要怎麼使用pip安裝robot framework了。

Hello World!

即將要搬家,因此舊網頁內容將慢慢轉移至Blogger。 如果要存取舊網頁,可以使用以下連結: https://wiki.tonylin.idv.tw/dokuwiki/doku.php