本文介紹如何在WindowXP/NT/2000環境中,編寫純Java程式,執行外部命令IPCONFIG,並通過分析該命令的輸入資料流而獲得原生MAC地址的編程方法。
1 引言
用Java編寫的程式,可以很方便地運行在各種平台的環境。但在實際的開發過程中,有時不得不涉及一些底層的編程。比如為了防止軟體盜用,我們 希望軟體只能在指定電腦上運行,所以需要程式讀取該機區分於其它電腦的硬體特徵,如MAC地址等。作為一種跨平台語言,給Java語言提出了挑戰。本 文正是針對該問題,提出一種直接用純Java語言,讀去MAC地址的編程方法。
我們知道,在每一個Java應用程式中都存在著一個與其運行環境相聯絡的Runtime對象。該對象可執行外部命令、查可用記憶體等。而多數操作 系統都提供有查詢該機MAC地址的命令。如在Microsoft的作業系統中,命令IPCONFIG等。本文的思路是在程式中運行一個外部命令,將該命令 的運行結果作為一個流(Stream),讀取並分析之,進而實現擷取MAC地址的目的。
2 Runtime類
在每一個Java 應用程式裡面,都有惟一的一個Runtime 對象。通過這個Runtime 對象,應用程式可以與其運行環境發生相互作用。
一般不執行個體化一個Runtime對象。但是可以通過調用靜態方法Runtime.getRuntime( )而獲得對當前Runtime對象的引用。Runtime 類的大多數方法是執行個體方法。
Runtime 對象的作用主要有:執行外部命令;返回空閑記憶體;運行記憶體回收行程;載入動態庫等。
Applets和其他不可信賴的程式由於沒有引起一個安全異常(SecurityException)而不能調用任何的Runtime方法。
下面的例子示範了怎樣使用Runtime 對象運行一個外部命令。
:
Process process = Runtime.getRuntime().exec("cmd.exe /c dir");
process.waitFor();
:
Runtime.getRuntime()返回當前應用程式的Runtime對象,該對象的exec()方法指示Java虛擬機器建立一個子進程 執行指定的可執行程式,並返回與該子進程對應的Process對象執行個體。通過Process可以控制該子進程的執行或擷取該子進程的資訊。第二條語句的目 的是等待子進程完成後再往下執行。
上面的程式在運行時會執行dir命令。如果在Windows95/98下,命令格式可以寫成"command.exe /c dir"。開關/C指明後面跟隨的字串是命令,並在執行命令後關閉DOS 視窗。
方法exec還可以開啟一個不可執行檔程式,但該檔案存在相關 App程式。以開啟一個word文檔Mydoc.doc檔案為例,Java中可以有以下兩種寫法:
exec(""cmd /E:ON /c start MyDoc.doc"");
exec(" c:Program FilesMicrosoft Officeofficewinword.exe .mydoc.doc");
在第一種方式中,被執行的命令是start Mydoc.doc,開關E:ON 指定DOS 命令處理器允許命令擴充,而start 命令會開啟一個單獨的視窗執行所提供的命令。
執行一個有標準輸出的DOS命令,程式執行完後往往不會自動關閉,從而導致Java應用程式阻塞在waitfor( )。導致該現象的原因可能是該命令的輸出比較多,而運行視窗的輸出緩衝區不夠大。解決的辦法是,利用Java的Process類提供的方法讓Java虛擬 機截獲DOS啟動並執行標準輸出,在waitfor()命令之前讀出該緩衝區的內容。以運行命令dir為例,典型的程式如下:
:
String line;
Process process = Runtime.getRuntime().exec("cmd /c dir");
BufferedReader bufferedReader = new BufferedReader ( new InputStreamReader(process.getInputStream()));
while ( (line = bufferedReader.readLine()) != -1) System. out.println(line);
process.waitFor( );
:
3 Process
Runtime.exec方法建立一個本機進程,並返回 Process 子類的一個執行個體,該執行個體可用來控制進程並擷取相關資訊。
抽象類別Process封裝了一個進程(process),一個正在執行的程式。它主要被當作由Runtime類中的exec( )方法所建立的對象的類型的超類。在抽象類別Process中,主要包含了如下一些抽象方法。
InputStream getInputStream( ):返回一個從進程的out輸出資料流中讀輸入的輸入資料流。
OutputStream getOutputStream( ):返回一個從進程的in輸入資料流中寫輸出的輸出資料流。
int waitFor( ) throws InterruptedException:返回由進程返回的退出碼。這個方法直到調用它的進程中止,才會返回。
4 程式編寫
我們先來分析ipconfig/all的輸出格式:
圖1
從圖1中我們看到MAC地址包含的行為:“ Physical Address. . . . . . . . . : 00-10-DC-A9-0B-2C”。為了找到MAC地址,我們一行一行讀取字元,只要找到字串“ Physical Address. . . . . . . . . :”,就可以找到MAC地址了。下面是實現的程式片段:
:
Process process = Runtime.getRuntime().exec("cmd /c ipconfig /all");
BufferedReader bufferedReader =
new BufferedReader(new InputStreamReader (process.getInputStream()));
while ( (line=bufferedReader.readLine()) != null){
if(line.indexOf("Physical Address. . . . . . . . . :") != -1){
if(line.indexOf(":") != -1){
physicalAddress = line.substring(line.indexOf(":")+2);
:
在上面的程式中,為了讀取命令輸出的字元,利用子進程process產生了一個資料流緩衝區。
依據上面的代碼,我們編寫了一個類ReadMAC,見下面程式的原始碼:
import java.io.*;
public class ReadMAC {
public static String physicalAddress = "read MAC error!";
public ReadMAC() {
}
public static String checkPhysicalAddress(){
try{
String line;
Process process = Runtime.getRuntime().exec("cmd /c ipconfig /all");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
while ( (line=bufferedReader.readLine()) != null){
if(line.indexOf("Physical Address. . . . . . . . . :") != -1){
if(line.indexOf(":") != -1){
physicalAddress = line.substring(line.indexOf(":")+2);
break; //找到MAC,推出迴圈
}
}
}
process.waitFor();
}catch(Exception e){
e.printStackTrace();
}
return physicalAddress;
}
public static void main(String[] args) {
System.out.println("原生MAC地址是: "+ ReadMAC.checkPhysicalAddress());
}
}
編譯運行該程式的輸出結果2所示。
圖2
由於每一台電腦的MAC地址都不同,所以該程式在不同電腦上運行結果都會不一樣。
5 結束語
作為一個跨平台語言,編寫的JAVA程式一般都與硬體無關,因而能運行在不同的作業系統環境。但這給編寫底層相關的程式時帶來不便。
Java的應用程式都存在著一個與其運行環境相聯絡的Runtime對象,利用該對象可執行外部命令,在WindowsXP/NT/2000環 境中的命令IPCONFIG的輸出包含有MAC地址。本文編寫的Java程式,執行外部命令IPCONFIG,並通過分析該命令的輸入資料流而獲得原生 MAC地址。由於IPCONFIG命令是作業系統命令,所以該方法既方便又可靠。
以上討論的程式適合於Windows XP/NT/2000作業系統,因為它是基於該作業系統的命令IPCONFIG格式的。由於不同作業系統讀取MAC地址的命令、以及命令輸出格式的不同, 所以該程式不能直接運用到其它系統。要移植到其它系統只需針對命令的輸出格式稍作修改。