李桂巖 魏 賓 賈瑞鳳
[摘要]介紹C++Builder在Windows 95/98操作系統平臺IO端口沒有受到保護的情況下,實現IO端口讀寫操作的兩種方法,在Windows NT/2000/XP操作系統平臺IO端口受到保護的情況下,應用WinIO程序庫實現IO端口讀寫的方法。對于應用C++Builder進行硬件開發的編程人員有重要參考價值。
[關鍵詞]C++Builder IO端口讀寫 WinIO程序庫
中圖分類號:TP3文獻標識碼:A文章編號:1671-7597(2009)0910070-02
C++Builder繼承了C語言簡潔、快速的優點,采用面向對象的軟件工程設計方法和可視化界面設計技術,融合了Windows編程、數據庫編程、網絡編程等技術,使得程序員可以快速高效地開發出高質量的Windows應用程序。但在C++Builder中,不能夠使用Turbo C中的outputb和inputb端口讀寫函數。給工業控制方面的開發帶來不便,特別是不利于IO卡的直接輸入輸出操作。筆者為在C++Builder中實現這個功能專門在Windows的不同版本下進行了嘗試取得了成功。現就具體方法介紹如下供C++Builder編程人員參考。
一、在Windows 95/98操作系統平臺下實現端口讀寫操作
共有兩種方法,一種為內嵌匯編語言,另一種為使用__emit__函數。
(一)通過內嵌匯編語言實現端口的讀寫
在C++Builder中,匯編語句必須被包含在以關鍵字asm為起始的一對大括號中:
asm {
匯編語句1
……
}
利用內嵌匯編語言編制端口輸出函數如下:
void OutPort(unsigned short port,unsigned char value)
//port參數為輸出端口地址,value參數為輸出值
{
asm{
mov dx , port //把端口地址送到處理器DX寄存器中
mov al , value // 把value 送到處理器AL寄存器中
out dx , al // 把AL寄存器中的值送到端口
};
}
該函數將無符號字符型8位的數據value寫入地址為port的端口上,port的數據類型是unsigned short,16位無符號短整形。
利用內嵌匯編語言編制端口輸入函數如下:
unsigned char InPort(unsigned short port)
//port參數為輸入端口地址,返回為輸入值
{
unsigned char value ;
asm{
mov dx, port // 把端口地址送到處理器DX寄存器中
in al, dx // 從DX指定端口中將一數據送到AL寄存器中
mov value,al // 把AL寄存器中的值賦給value
};
return value; //返回端口數據
}
函數InPort從地址為port的端口讀入一個無符號8位的字符型數據,其其參數只一個,即端口號。返回的數據為unsigned char類型的,為從端口讀取的值。
(二)通過__emit__函數實現端口的讀寫
__emit__ 函數一般極少用到。其用法如下:
void _ _emit_ _(argument, . . .);
該函數為C++Builder 的一個內部函數,調用的參數為機器語言指令。它在編譯的時候,將機器語言指令直接嵌入目標碼中,不必借助于匯編語言和匯編編譯程序。
利用__emit__函數編制端口輸出函數如下:
void OutPort(unsigned short port,unsigned char value)
//port參數為輸出端口地址,value參數為輸出值
{
__emit__(0x8b,0x95,&port); // 把端口地址送到處理器EDX寄存器中
__emit__(0x8a,0x85,&value); // 把value 送到處理器AL寄存器中
__emit__(0x66,0xee); // 把AL寄存器中的值送到端口
}
利用__emit__函數編制端口輸入函數如下:
unsigned char InPort(unsigned short port)
//port參數為輸入端口地址,返回為輸入值
{
unsigned char value;
__emit__(0x8b,0x95,&port) ; // 把端口地址送到處理器DX寄存器中
__emit__(0x66,0xec); // 從DX指定端口中將一數據送到AL寄存器中
__emit__(0x88,0x85,&value); // 把AL寄存器中的值賦給value
return value; //返回端口數據
}
由這兩種方法所編制的函數注釋可以看出,它們每一句的功能都是一樣的,只是一個是嵌入了匯編語言,另一個是直接使用機器語言。
二、在Windows NT/2000/XP操作系統平臺下實現端口讀寫操作
上述介紹的實現端口讀寫操作兩種方法,在Windows 95/98下面工作很正常,但是在Windows NT/2000/XP上就會出現非法指令調用的問題。這些非法指令來自于底層對IO端口的直接地址訪問。在Windows 95/98時代,這些操作都沒有受到保護的,而在Windows NT/2000/XP下就會出現保護問題。為了解決這個問題需要使用第三方提供的WinIO程序庫。
(一)WinIO程序庫簡介
WinIO程序庫允許在32位的Windows應用程序中直接對I/O端口和物理內存進行存取操作。通過使用一種內核模式的設備驅動器和其它幾種底層編程技巧,它繞過了Windows系統的保護機制。
WindowsNT/2000/XP下,WinIO函數庫只允許被具有管理者權限的應用程序調用。如果使用者不是以管理者的身份進入的,則WinIO.DLL不能夠被安裝,也不能激活WinIO驅動器。通過在管理者權限下安裝驅動器軟件就可以克服這種限制。然而,在這種情況下,ShutdownWinIo函數不能在應用程序結束之前被調用,因為該函數將WinIO驅動程序從系統注冊表中刪除。
該函數庫提供8個函數功能調用,其中直接對I/O端口操作有4個函數:
bool _stdcall InitializeWinIo();
本函數初始化WioIO函數庫。
必須在調用所有其它功能函數之前調用本函數。
如果函數調用成功,返回值為非零值。
如果調用失敗,則返回值為0。
void _stdcall ShutdownWinIo();
本函數在內存中清除WinIO庫
本函數必須在中止應用函數之前或者不再需要WinIO庫時調用,
bool _stdcall GetPortVal(WORD wPortAddr, PDWORD pdwPortVal, BYTE bSize);
使用此函數從一個輸入或輸出端口讀取一個字節/字/雙字數據。
參數:
wPortAddr輸入輸出端口地址
pdwPortVal指向雙字變量的指針,接收從端口得到的數據。
bSize需要讀的字節數,可以是1 (BYTE), 2 (WORD) or 4 (DWORD).
如果調用成功,則返回非零值。
如果函數調用失敗,則函數返回值為零。
bool _stdcall SetPortVal(WORD wPortAddr, DWORD dwPortVal, BYTE bSize);
使用本函數將一個字節/字/雙字的數據寫入輸入或輸出接口。
參數:
wPortAddr輸入輸出口地址
dwPortVal要寫入口的數據
bSize要寫的數據個數,可以是 1 (BYTE), 2 (WORD) or 4 (DWORD).
如果調用成功,則返回非零值。
如果函數調用失敗,則函數返回值為零。
(二)WinIO程序庫的應用
在C++Builder中應用WinIO程序庫需要做如下工作。
1.首先將 winio.dll, winio.vxd 和 winio.sys三個文件拷貝到用C++Builder開發的工程文件目錄下;
2.在DOS提示符下用 implib 命令創建導入庫。implib 命令格式如下:
implibwinio.libwinio.dll;
3.將winio.lib 添加到用C++Builder開發的工程中。其操作方法是,在C++BuilderIDE 中選擇 Project→Add to project…命令,在彈出的Add to project對話框中“文件類型”下拉列表框中選擇Library file (*.lib)項,會出現 .lib文件。選擇winio.lib文件并單擊“打開”按鈕,添加操作成功;
4.將winio.h中的WINIO_API刪除;
5.在源文件中添加頭文件“#include winio.h”;
6.調用初始化命令函數 InitializeWinIo();
7.調用庫函數GetPortVal、SetPortVal實現端口的輸入輸出操作;
8.當所有的端口輸入輸出操作全部完成,調用庫函數ShutdownWinIo
在內存中清除WinIO庫。
上述的幾種方法筆者在不同的應用環境下使用都是正常的沒有發現異常現象,其中內嵌匯編語言和使用__emit__函數的方法編制IO端口讀寫函數,在Windows 98操作系統和C++Builder5.0編程語言環境下使用多年,工作一直很穩定,沒有出現任何問題。后來因操作系統升級為Windows XP發現原有的函數不能用了,后在C++Builder6.0編程語言環境下,改用WinIO程序庫的輸入或輸出函數進行端口的直接操作工作一切正常。