VC++動態鏈接庫編程之DLL典型實例
動態鏈接庫DLL實現了庫的共享,體現了代碼重用的思想。我們可以把廣泛的、具有共性的、能夠多次被利用的函數和類定義在庫中。這樣,在再次使用這些函數和類的時候,就不再需要重新添加與這些函數和類相關的代碼。具有共性的問題大致有哪些呢?筆者歸納如下:
圖像處理、視頻音頻解碼、壓縮與解壓縮、加密與解密通常采用某些特定的算法,這些算法較固定且在這類程序中往往經常被使用。
(2)純資源DLL
我們可以從DLL中獲取資源,對于一個支持多種語言的應用程序而言,我們可以判斷操作系統的語言,并自動為應用程序加載與OS對應的語言。這是多語言支持應用程序的一般做法。
(3)通信控制DLL
串口、網口的通信控制函數如果由DLL提供則可以使應用程序輕松不少。在工業控制、modem程序甚至socket通信中,經常使用通信控制DLL。
本節將給出DLL的三個典型應用實例。
7.1 算法DLL
我們直接用讀者的一個提問作為例子。
宋寶華先生,您好!
我在dev.yesky.com上看到你連載的《VC++動態鏈接庫編程》,覺得非常好。我以前主要是用Delphi的,C/C++學過,對Win32和VCL比較熟悉,但是沒有接觸過VC++,對MFC很陌生。這段時間和一個同學合作做光學成像的計算機模擬,用到傅立葉變換,手里面有例程是VC++寫的。我們的界面是用Delphi開發,需要將其傅立葉變換功能提出做一個DLL供Delphi調用。苦于不懂MFC,試了很多方法,都不成功,最后只得采用折衷方案,簡單修改一下程序,傳一個參數進去,當作exe來調用,才沒有耽擱后續進程。
……
謝謝!
致
禮!
某某
學習過較高級別數學(概率統計與隨機過程)、信號與線性系統及數字信號處理的讀者應該知道,傅立葉變換是一種在信號分析中常用的算法,用于時域和頻域的相互轉換。FFT變換算法通用而有共性,我們適宜把它集成在一個DLL中。
隨后,這位讀者提供了這樣的一個函數:
既然有了FFT這個函數,我們要把它做在DLL中,作為DLL的一個接口將是十分簡單的,其步驟如下:
(1)利用MFC向導建立一個非MFC DLL;
(2)在工程中添加fft.h和fft.cpp兩個文件;
fft.h的源代碼為:
在任何編程語言中使用Win32 API LoadLibrary都可以加載這個DLL,而使用GetProcAddress(hDll, "FFT")則可以獲得函數FFT的地址,讀者所提到的Delphi當然也不例外。
這個DLL中有兩點需要注意:
(1)使用extern "C"修飾函數聲明,否則,生成的DLL只能供C++調用;
(2)使用__stdcall修飾函數聲明及定義,__stdcall是Windows API的函數調用方式。
7.2純資源DLL
我們在應用程序中產生如圖18所示的資源(對話框),單擊此處下載本工程。
在與這個應用程序相同的工作區里利用MFC向導建立兩個簡單的DLL,把應用工程中的資源全選后分別拷貝到ChineseDll和EngLishDll,在EnglishDll工程的資源文件中搜索下面的語句:
將其改為:
并將其中所有的中文翻譯為英文。這個DLL為我們提供了如圖19所示的對話框資源。
修改應用工程的InitInstance()函數,在
之前(即對話框顯示之前)添加如下代碼:
這樣的應用程序將具有自適應性質,在中文OS中顯示中文資源,在英文OS中則顯示英文資源。
7.3通信控制DLL
我們在這里舉一個串口通信類的例子。
也許您需要了解一點串口通信的背景知識,其實串口到處都看得到,譬如PC機的COM口即為串行通訊口(簡稱串口)。如圖20,打開Windows的設備管理器,我們看到了COM口。
在Windows系統,需通過DCB(Device Control Block)對串口進行配置。利用Windows API GetCommState函數可以獲取串口當前配置;利用SetCommState函數則可以設置串口通訊的參數。
串行通信通常按以下四步進行:
(1)打開串口;
(2)配置串口;
(3)數據傳送;
(4)關閉串口。
由此可見,我們需要給串口控制DLL提供如下四個接口函數:
下面給出了DLL接口的主要源代碼框架:
我們編寫一控制臺程序來演示DLL的調用:
DLL的編寫與調用方法及主要應用皆已講完,在下一節里,我們將看到比較“高深”的主題――DLL木馬。曾幾何時,DLL木馬成為了病毒的一種十分重要的形式,是DLL的什么特性使得它能夠成為一種病毒?下一節我們將揭曉謎底。
(1)通用的算法
圖像處理、視頻音頻解碼、壓縮與解壓縮、加密與解密通常采用某些特定的算法,這些算法較固定且在這類程序中往往經常被使用。
(2)純資源DLL
我們可以從DLL中獲取資源,對于一個支持多種語言的應用程序而言,我們可以判斷操作系統的語言,并自動為應用程序加載與OS對應的語言。這是多語言支持應用程序的一般做法。
(3)通信控制DLL
串口、網口的通信控制函數如果由DLL提供則可以使應用程序輕松不少。在工業控制、modem程序甚至socket通信中,經常使用通信控制DLL。
本節將給出DLL的三個典型應用實例。
7.1 算法DLL
我們直接用讀者的一個提問作為例子。
宋寶華先生,您好!
我在dev.yesky.com上看到你連載的《VC++動態鏈接庫編程》,覺得非常好。我以前主要是用Delphi的,C/C++學過,對Win32和VCL比較熟悉,但是沒有接觸過VC++,對MFC很陌生。這段時間和一個同學合作做光學成像的計算機模擬,用到傅立葉變換,手里面有例程是VC++寫的。我們的界面是用Delphi開發,需要將其傅立葉變換功能提出做一個DLL供Delphi調用。苦于不懂MFC,試了很多方法,都不成功,最后只得采用折衷方案,簡單修改一下程序,傳一個參數進去,當作exe來調用,才沒有耽擱后續進程。
……
謝謝!
致
禮!
某某
學習過較高級別數學(概率統計與隨機過程)、信號與線性系統及數字信號處理的讀者應該知道,傅立葉變換是一種在信號分析中常用的算法,用于時域和頻域的相互轉換。FFT變換算法通用而有共性,我們適宜把它集成在一個DLL中。
隨后,這位讀者提供了這樣的一個函數:
/* 函數名稱:FFT() * 參數: * complex<double> * TD - 指向時域數組的指針 * complex<double> * FD - 指向頻域數組的指針 * r -2的冪數,即迭代次數 * 返回值: 無。 * 說明:該函數用來實現快速傅立葉變換 */ void FFT(complex<double> * TD, complex<double> * FD, int r) { LONG count; // 傅立葉變換點數 int i,j,k; // 循環變量 int bfsize,p; // 中間變量 double angle; // 角度 complex<double> *W,*X1,*X2,*X; count = 1 << r; //傅立葉變換點數 // 分配運算所需存儲器 W = new complex<double>[count / 2]; X1 = new complex<double>[count]; X2 = new complex<double>[count]; // 計算加權系數 for(i = 0; i < count / 2; i++) { angle = -i * PI * 2 / count; W[i] = complex<double> (cos(angle), sin(angle)); } // 將時域點寫入X1 memcpy(X1, TD, sizeof(complex<double>) * count); // 采用蝶形算法進行快速傅立葉變換 for(k = 0; k < r; k++) { for(j = 0; j < 1 << k; j++) { bfsize = 1 << (r-k); for(i = 0; i < bfsize / 2; i++) { p = j * bfsize; X2[i + p] = X1[i + p] + X1[i + p + bfsize / 2]; X2[i + p + bfsize / 2] = (X1[i + p] - X1[i + p + bfsize / 2]) * W[i * (1<<k)]; } } X = X1; X1 = X2; X2 = X; } // 重新排序 for(j = 0; j < count; j++) { p = 0; for(i = 0; i < r; i++) { if (j&(1<<i)) { p+=1<<(r-i-1); } } FD[j]=X1[p]; } // 釋放內存 delete W; delete X1; delete X2; } |
既然有了FFT這個函數,我們要把它做在DLL中,作為DLL的一個接口將是十分簡單的,其步驟如下:
(1)利用MFC向導建立一個非MFC DLL;
(2)在工程中添加fft.h和fft.cpp兩個文件;
fft.h的源代碼為:
#ifndef FFT_H #define FFT_H #include <complex> using namespace std; extern "C" void __declspec(dllexport) __stdcall FFT(complex<double> * TD, complex<double> * FD, int r); #define PI 3.1415926 #endif fft.cpp的源代碼為: /* 文件名:fft.cpp */ #include "fft.h" void __stdcall FFT(complex<double> * TD, complex<double> * FD, int r) { …//讀者提供的函數代碼 } |
在任何編程語言中使用Win32 API LoadLibrary都可以加載這個DLL,而使用GetProcAddress(hDll, "FFT")則可以獲得函數FFT的地址,讀者所提到的Delphi當然也不例外。
這個DLL中有兩點需要注意:
(1)使用extern "C"修飾函數聲明,否則,生成的DLL只能供C++調用;
(2)使用__stdcall修飾函數聲明及定義,__stdcall是Windows API的函數調用方式。
7.2純資源DLL
我們在應用程序中產生如圖18所示的資源(對話框),單擊此處下載本工程。
![]() 圖18 中文對話框 |
在與這個應用程序相同的工作區里利用MFC向導建立兩個簡單的DLL,把應用工程中的資源全選后分別拷貝到ChineseDll和EngLishDll,在EnglishDll工程的資源文件中搜索下面的語句:
///////////////////////////////////////////////////////////////////////////// // Chinese (P.R.C.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) #ifdef _WIN32 LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED #pragma code_page(936) #endif //_WIN32 |
將其改為:
///////////////////////////////////////////////////////////////////////////// // English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32 |
并將其中所有的中文翻譯為英文。這個DLL為我們提供了如圖19所示的對話框資源。
![]() 圖19英文對話框 |
修改應用工程的InitInstance()函數,在
CResourceDllCallDlg dlg; m_pMainWnd = &dlg; int nResponse = dlg.DoModal(); |
之前(即對話框顯示之前)添加如下代碼:
//獲取操作系統的語言 WORD wLangPID = PRIMARYLANGID( GetSystemDefaultLangID() ); if( LANG_CHINESE == wLangPID ) { hLanguageDll = LoadLibrary( "ChineseDll.dll" ); //加載中文資源 } else { hLanguageDll = LoadLibrary( "EnglishDll.dll" ); //加載英文資源 } if( NULL == hLanguageDll ) { AfxMessageBox( "Load DLL failure" ); return FALSE; } AfxSetResourceHandle( hLanguageDll ); //設置當前的資源句柄 |
這樣的應用程序將具有自適應性質,在中文OS中顯示中文資源,在英文OS中則顯示英文資源。
7.3通信控制DLL
我們在這里舉一個串口通信類的例子。
也許您需要了解一點串口通信的背景知識,其實串口到處都看得到,譬如PC機的COM口即為串行通訊口(簡稱串口)。如圖20,打開Windows的設備管理器,我們看到了COM口。
在Windows系統,需通過DCB(Device Control Block)對串口進行配置。利用Windows API GetCommState函數可以獲取串口當前配置;利用SetCommState函數則可以設置串口通訊的參數。
串行通信通常按以下四步進行:
(1)打開串口;
(2)配置串口;
(3)數據傳送;
(4)關閉串口。
![]() 圖20 PC的串口 |
由此可見,我們需要給串口控制DLL提供如下四個接口函數:
//打開指定的串口,其參數port為端口號 BOOL ComOpen(int port); //在這個函數里使用默認的參數設置串口 //將打開的串口關閉 void ComClose(int port); //將串口接收緩沖區中的數據放到buffer中 int GetComData(char *buf, int buf_len); //將指定長度的數據發送到串口 int SendDataToCom(LPBYTE buf,int buf_Len); |
下面給出了DLL接口的主要源代碼框架:
//com.h:com類通信接口 class AFX_EXT_CLASS com { public: ComOpen(int port) { … } int SendDataToCom(LPBYTE buf,int buf_Len) { … } int GetComData(char *buf, int buf_len) { … } void ComClose() { … } } |
我們編寫一控制臺程序來演示DLL的調用:
#include <iostream> #include <exception> using namespace std; #include <windows.h> #include "com.h" //包含DLL中導出類的頭文件 int main(int argc, char *argv[]) { try { char str[] = "com_class test"; com com1; com1.ComOpen (1); for(int i=0; i<100; i++) //以同步方式寫com的buffer { Sleep(500); com1.SendDataToCom (str,strlen(str)); } com1.ComClose (); } catch(exception &e) { cout << e.what() << endl; } return 0; } |
DLL的編寫與調用方法及主要應用皆已講完,在下一節里,我們將看到比較“高深”的主題――DLL木馬。曾幾何時,DLL木馬成為了病毒的一種十分重要的形式,是DLL的什么特性使得它能夠成為一種病毒?下一節我們將揭曉謎底。