Windows下DLL編程技術及應用

來源:互聯網
上載者:User
摘 要:
本文介紹了DLL技術在Windows編程中的基本運用方法及應用,給出了直接記憶體存取及連接埠I/O的兩個實用DLL的全部原始碼。
關鍵詞: DLL Windows編程 記憶體訪問 I/O

一 、引 言
由於Windows為微機提供了前所未有的標準使用者介面、圖形處理能力和簡單靈便的操作,絕大多數程式編製人員都已轉向或正在轉向Windows編程。在許多使用者設計的實際應用系統的編程任務中,常常要實現軟體對硬體資源和記憶體資源的訪問,例如連接埠I/O、DMA、中斷、直接記憶體存取等等。若是編製DOS程式,這是輕而易舉事情,但要是編製Windows程式,尤其是WindowsNT環境下的程式,就會顯得較困難。
因為Windows具有"與裝置無關"的特性,不提倡與機器底層的東西打交道,如果直接用Windows的API函數或I/O讀寫指令進行訪問和操作,程式運行時往往就會產生保護模式錯誤甚至死機,更嚴重的情況會導致系統崩潰。那麼在Windows下怎樣方便地解決上述問題呢?用DLL(Dynamic Link Libraries)技術就是良好途徑之一。
DLL是Windows最重要的組成要素,Windows中的許多新功能、新特性都是通過DLL來實現的,因此掌握它、應用它是非常重要的。其實Windows本身就是由許多的DLL組成的,它最基本的三大組成模組Kernel、GDI和User都是DLL,它所有的庫模組也都設計成DLL。凡是以.DLL、.DRV、.FON、.SYS和許多以.EXE為副檔名的系統檔案都是DLL,要是開啟Windows/System目錄,就可以看到許多的DLL模組。儘管DLL在Ring3優先順序下運行,仍是實現硬體介面的簡便途徑。DLL可以有自己的資料區段,但沒有自己的堆棧,使用與調用它的應用程式相同的堆棧模式,減少了編程設計上的不便;同時,一個DLL在記憶體中只有一個執行個體,使之能高效經濟地使用記憶體;DLL實現的代碼封裝性,使得程式簡潔明晰;此外還有一個最大的特點,即DLL的編製與具體的程式設計語言及編譯器無關,只要遵守DLL的開發規範和編程策略,並安排正確的調用介面,不管用何種程式設計語言編製的DLL都具有通用性。例如在BC31中編製的DLL程式,可用於BC、VC、VB、Delphi等多種語言環境中。筆者在BC31環境下編譯了Windows下直接記憶體存取和連接埠I/O兩個DLL,用在多個自製系統的應用軟體中,運行良好。

二、DLL的建立和調用
DLL的建立及調用方法在許多資料上有詳細的介紹,為了節省篇幅,在這裡僅作一些主要的概括。
1.DLL的建立
關於DLL的建立,有如下幾個方面的要素是不可缺少和必須掌握的:
入口函數LibMain( )
就象C程式中的WinMain( )一樣,Windows每次載入DLL時都要執行LibMain( )函數,主要用來進行一些初始化工作。通常的形式是:

int FAR PASCAL LibMain(HINSTANCE hInstance,WORD wDataSeg,WORD wHeapSize,LPSTR lpszCmdLine)
{
if(wHeapSize!=0) //使局部堆、資料區段可移動
UnlockData(0);
//解鎖資料區段
/*此處可進行一些使用者必要的初始化工作*/
return 1; //初始化成功
}

出口函數WEP( )
Windows從記憶體中卸載DLL時,調用相應的出口函數WEP( ),主要做一些清理工作,如釋放佔用的記憶體資源;丟棄某些字串、位元影像等資源;關閉開啟的檔案等等。

自訂的輸出函數
為了讓位於不同記憶體段的應用程式進行遠程調用,自訂的輸出函數必須定義為遠程函數(使用FAR關鍵字),以防使用近程指標而得到意外的結果;同時,加上PASCAL關鍵字可加快程式的運行速度,使代碼簡單高效,提高程式的運行速度。
輸出函數的引出方法

在DLL的模組定義檔案中(.DEF)由EXPORTS語句對輸出函數逐一列出。例如:
EXPORTS WEP @1 residentname
//residentname可提高DLL效率和處理速度
PortIn @2
PortOut @3 //通常對所有輸出函數附加系列號

在每個輸出函數定義的說明中使用_export關鍵字來對其引出。
以上兩種方法任選其中的一種即可,不可重複。後面的兩個執行個體分別使用了上述兩種不同的引出方式,請留意。

2.DLL的調用
載入DLL時,Windows尋找相應DLL的次序如下:
.當前工作盤。
Windows目錄;GetWindowsDirectory( )函數可提供該目錄的路徑名。
Windows系統目錄,即System子目錄;調用GetSystemDiretory( )函數可獲得這個目錄的路徑名。
DOS的PATH命令中羅列的所有目錄。
網路中映象的目錄列表中的全部目錄。

DLL模組中輸出函數的調用方法:
不論使用何種語言對編譯好的DLL進行調用時,基本上都有兩種調用方式,即靜態調用方式和動態調用方式。靜態調用方式由編譯系統完成對DLL的載入和應用程式結束時DLL卸載的編碼(如還有其它程式使用該DLL,則Windows對DLL的應用記錄減1,直到所有相關程式都結束對該DLL的使用時才釋放它),簡單實用,但不夠靈活,只能滿足一般要求。動態調用方式是由編程者用API函數載入和卸載DLL來達到調用DLL的目的,使用上較複雜,但能更加有效地使用記憶體,是編製大型應用程式時的重要方式。具體來說,可用如下的方法調用.在應用程式模組定義檔案中,用IMPORTS語句列出所要調用DLL的函數名。如:
IMPORTS
MEMORYDLL.MemoryRead
MEMORYDLL.MemoryWrite
讓應用程式運行時與DLL模組動態連結
先用LoadLibrary載入DLL,再用GetProcAddress函數檢取其輸出函數的地址,獲得其指標來調用。如:
HANDLE hLibrary;
FARPROC lpFunc;
int PortValue;
hLibrary=LoadLibrary("PORTDLL.DLL");
//載入DLL
if(hLibrary>31) //載入成功
{
lpFunc=GetProcAddress(hLibrary,"PortIn");
//檢取PortIn函數地址
if(lpFunc!=(FARPROC)NULL)
//檢取成功則調用
PortValue=(*lpFunc)(port); //讀port連接埠的值
FreeLibrary(hLibrary);
//釋放佔用的記憶體
}

三、DLL應用執行個體來源程式
1.直接記憶體存取的DLL原始碼
//.DEF檔案
LIBRARY
MEMORYDLL
DESCRIPTION 'DLL FOR MEMORY_READ_WRITE '
EXETYPE WINDOWS
CODE
PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE SINGLE
HEAPSIZE 1024
//DLL無自己的堆棧,故沒有STACKSIZE語句
EXPORTS WEP @1 residentname
ReadMemory
@2
WriteMemory @3

//.CPP檔案
#include <windows.h>
int FAR PASCAL LibMain(HINSTANCE hInstance,WORD wDataSeg,WORD wHeapSize,LPSTR lpszCmdLine)
{
if(wHeapSize!=0)
UnlockData(0);
return
1;
}

int FAR PASCAL MemoryRead(unsigned int DosSeg,unsigned int DosOffset)
{
WORD wDataSelector,wSelector;
char far *pData;
char value;
wDataSelector=HIWORD((DWORD)(WORD FAR *)&wDataSelector);
wSelector=AllocSelector(wDataSelector);
//分配選取器
SetSelectorLimit(wSelector,0x2000);
//置存取界限
SetSelectorBase(wSelector,(((DWORD)DosSeg)<<4)+(DWORD)DosOffset);
//置基地址
pData=(char far *)((DWORD)wSelector<<16);
value=*pData;
FreeSelector(wSelector);
//釋放選取器
return (value);
}

void FAR PASCAL MemoryWrite(unsigned int DosSeg,unsigned int DosOffset,char Data)
{
WORD wDataSelector,wSelector;
char far *pData;
wDataSelector=HIWORD((DWORD)(WORD FAR *)&wDataSelector);
wSelector=AllocSelector(wDataSelector);
SetSelectorLimit(wSelector,0x2000);
SetSelectorBase(wSelector,(((DWORD)DosSeg)<<4)+(DWORD)DosOffset);
pData=(char far *)((DWORD)wSelector<<16);
*pData=Data;
FreeSelector(wSelector);
}

int FAR PASCAL WEP(int nParam)
{
return 1;
}

2.連接埠讀寫I/O的DLL原始碼
//.DEF檔案
LIBRARY PORTDLL
DESCRIPTION 'DLL FOR
PORT_IN_OUT '
EXETYPE WINDOWS
CODE PRELOAD MOVEABLE DISCARDABLE
DATA
PRELOAD MOVEABLE SINGLE
HEAPSIZE 1024

//.CPP檔案
#include
<windows.h>
#include <dos.h>

int FAR PASCAL
LibMain(HINSTANCE hInstance,WORD wDataSeg,WORD wHeapSize,LPSTR lpszCmdLine)
{
if(wHeapSize!=0)
UnlockData(0);
return
1;
}

int FAR PASCAL _export PortOut(int port,unsigned char value)
{
outp(port,value);
return 1;
}

int FAR PASCAL _export PortIn(int port)
{
int result;
result=inp(port);
return (result);
}

int FAR PASCAL _export WEP(int nParam)
{
return 1;
}

分別將上面兩個執行個體的.DEF檔案和.CPP檔案各自組成一個.PRJ檔案,並進行編譯連結成.EXE或.DLL檔案就可以在應用程式中對其進行調用。

四、結束語
在上面,我們利用DLL技術方便地實現了Windows環境下對記憶體的直接存取和連接埠I/O的訪問,仿效這兩個例子,還可以編製出更多的適合自己應用系統所需的DLL,如用於資料擷取卡的連接埠操作及擴充記憶體區訪問、視頻區緩衝區及BIOS資料區操作等許多實際應用的編程任務中。必要時只需直接更新DLL,而用不著對應用程式本身作任何改動就可以對應用程式的功能和使用者介面作較大的改善,實現版本升級。因此,掌握好DLL技術對Windows程式開發人員很有裨益。 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.