本文介紹如何在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還可以打開一個不可執行的程序,但該文件存在關聯的應用程序。以打開一個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地址的命令、以及命令輸出格式的不同,所以該程序不能直接運用到其它系統。要移植到其它系統只需針對命令的輸出格式稍作修改。