8.3 Ausführung von externen Programmen
Im Paket lang sitzt die Klasse Runtime, mit der sich innerhalb von Applikationen andere Programme aufrufen lassen - Applets können im Allgemeinen wegen der Sicherheitsbeschränkungen keine anderen Programme starten. So können Programme des Betriebssystems1 leicht verwendet werden, der Nachteil ist nur, dass die Java-Applikation dadurch stark plattformabhängig wird.
Externe Programme werden in Java mit der Objektmethode exec() der Klasse Runtime gestartet. Um ein Objekt vom Typ Runtime zu bekommen, müssen wir mit der Singleton-Funktion getRuntime() das aktuell verwendete Runtime-Objekt erfragen. Für ein Kommando command sieht das dann so aus:
Runtime.getRuntime().exec( command );
Hinweis Bisher ist es in Java nicht möglich - und vermutlich wird es das auch nicht werden -, dass sich der aktuelle Pfad, ähnlich wie es das Kommandozeilenprogramm cd macht, setzen lässt. Es ist eine bekannte Anfrage an Sun, der jedoch bisher nicht nachgegangen wurde. Als einzige Lösung bietet sich an, immer selbst den aktuellen Pfad in einer Variablen zu halten und bei Bedarf den Kommando-String einzubauen.
|
Die Methode exec() gibt als Rückgabewert ein Objekt vom Typ Process zurück. Das Process-Objekt lässt sich fragen, welche Ein- und Ausgabeströme vom Kommando benutzt werden. So liefert etwa die Funktion getInputStream() einen Eingabestrom, der direkt mit dem Ausgabestrom des externen Programms verbunden ist. Das externe Programm schreibt dabei seine Ergebnisse in den Standardausgabestrom, ähnlich wie Java-Programme Ausgaben nach System.out senden. Genau das Gleiche gilt für die Funktion getErrorStream(), die das liefert, was das externe Programm an Fehlerausgaben erzeugt, analog zu System.err in Java. Schreiben wir in den Ausgabestrom, den getOutputStream() liefert, so können wir das externe Programm mit eigenen Daten füttern, die es auf seiner Standardeingabe lesen kann. Bei Java-Programmen wäre das System.in. Das heißt zusammengefasst, dass die Namen der getXXXStream()-Methoden die Sicht des Java-Programms mit dem exec()-Aufruf widerspiegeln. Für das aufgerufene Kommando sieht das genau umgekehrt aus (Ausgabe und Eingabe sind über Kreuz verbunden).
Hier klicken, um das Bild zu Vergrößern
8.3.1 DOS-Programme aufrufen
Es ist nicht ohne weiteres möglich, beliebige DOS-Kommandos direkt mit der Funktion exec() auszuführen. Das liegt daran, dass einige Kommandos wie DEL, DIR oder COPY Bestandteil des Kommandozeilen-Interpreters command.com sind. Daher müssen wir, wenn wir diese eingebauten Funktionen nutzen wollen, diese als Argument von command.com angeben. Für eine Verzeichnisausgabe schreiben wir Folgendes:
Runtime.getRuntime().exec( "command.com /c dir" );
Unter Windows NT heißt der Interpreter allerdings nicht command.com, sondern einfach nur cmd.exe.2
Wollen wir jetzt die Dateien eines Verzeichnisses, also die Rückgabe des Programms DIR, auf dem Bildschirm ausgeben, so müssen wir die Ausgabe von DIR über einen Eingabestrom einlesen.
Listing 8.2 ExecDir.java
import java.io.*;
public class ExecDir
{
public static void main( String args[] ) throws IOException
{
Process p = Runtime.getRuntime().exec( "cmd /c dir" );
BufferedReader in = new BufferedReader(
new InputStreamReader(p.getInputStream()) );
for ( String s; (s = in.readLine()) != null; )
System.out.println( s );
}
}
Mit echo lässt sich auch unter Windows der Rechnername ausgeben. Auf der Kommandozeile ist dafür nur die Umgebungsvariable auszulesen:
echo %COMPUTERNAME%
Über den InputStream lässt sich die Eingabe entgegennehmen und weiterverarbeiten.
|
Process exec( String command ) throws IOException
Führt das Kommando in einem separaten Prozess aus. |
|
Process exec( String command, String envp[] ) throws IOException
Führt das Kommando in einem separaten Prozess aus. Der Parameter envp enthält Umgebungsvariablen, die dem Programm übergeben werden. Die Zeichenketten im Feld sind in der Form name=wert. |
|
Process exec( String cmdArray[] ) throws IOException
Führt das Kommando in einem separaten Prozess aus. Das Feld besteht aus den einzelnen Bestandteilen des Kommandos. Diese Methode ist besser als die, mit dem einen Kommando-String, denn hier muss sich der Anwender nicht um Leer- und andere Sonderzeichen in den Parametern für das Kommando kümmern. |
|
Process exec( String cmdArray[], String envp[] ) throws IOException
Führt das Kommando in einem separaten Prozess mit Umgebungsvariablen aus. |
Mit weiteren Methoden von Process lässt sich der Status des externen Programms erfragen und verändern. Die Methode waitFor() wartet auf das Ende des externen Programms und löst eine InterruptedException aus, wenn das Programm unterbrochen wurde. Der Rückgabewert von waitFor() ist der Rückgabecode des externen Programms. Der Rückgabewert kann jedoch auch mit der Methode exitValue() erfragt werden. Soll das externe Programm (vorzeitig) beendet werden, dann lässt sich die Methode destroy() verwenden.
Beispiel Ein Programmbaustein, der in einem try- und catch-Block auf das Ende des externen Kommandos wartet und den Rückgabewert auf dem Bildschirm ausgibt:
|
try {
Process p = Runtime.getRuntime().exec( command );
p.waitFor();
System.out.println( "Rückgabewert: " + p.exitValue() );
}
catch ( IOException ioe ) {
System.err.println( "IO error: " + ioe );
}
catch ( InterruptedException ie ) {
System.err.println( ie );
}
|
8.3.2 Die Windows-Registry verwenden
Wird Java unter MS-Windows ausgeführt, so ergibt sich hin und wieder die Aufgabe, Eigenschaften der Windows-Umgebung zu kontrollieren. Viele Eigenschaften des Windows-Betriebssystems sind in der Registry versteckt, und Java bietet als plattformunabhängige Sprache keine Möglichkeit, diese Eigenschaften in der Registry auszulesen oder zu verändern. Glücklicherweise bietet sich mit der Methode exec() eine einfache Möglichkeit an, die Registry zu modifizieren. Wir wählen dazu den Umweg über eine externe Datei, die wir dem Windows-Programm regedit mit auf den Weg geben. Eine Datei, mit der der Registry-Editor etwas anfangen kann, hat folgendes Format:
REGEDIT4
[Pfad zum Schlüssel]
"Schlüssel"="Wert"
Ist ein Schlüssel gesetzt, dann lässt sich auch der entsprechende Teil der Registry mit dem Programm regedit in einer Datei speichern. Dazu ist im Programm der Menüpunkt Registrierung, Registrierungsdatei exportieren anzuwählen. Unter Exportbereich können wir Ausgewählte Teilstruktur markieren. Dann wird nur ein Teil des Registry-Baums gesichert.
Beispiel Eine für die Registry vorbereitete Datei, die einen Schlüssel für die schnelle Anzeige von Menüpunkten unter Untermenüs setzt:
REGEDIT4
[HKEY_CURRENT_USER\Control Panel\Desktop]
"MenuShowDelay"="0"
|
Die Variable MenuShowDelay wird auf Null gesetzt. Damit werden die Untermenüs direkt ohne Verzögerung angezeigt.
Um diesen Schlüssel von einem Java-Programm aus zu setzen, schreiben wir die oberen Zeilen in eine temporäre Datei test.reg. Diese Datei wird als Parameter an das Programm regedit übergeben.
Runtime.getRuntime().exec( "regedit -r test.reg" );
Der Schalter -r bewirkt, dass keine (störenden) Fenster aufspringen, die uns über die Änderung an der Registry informieren.
8.3.3 Einen HTML-Browser unter Windows aufrufen
Möchte eine Java-Hilfeseite etwa die Web-Seite des Unternehmens aufrufen, stellt sich die Frage, wie ein HTML-Browser auf der Java-Seite gestartet werden kann. Die Frage verkompliziert sich dadurch, dass es viele Parameter gibt, die den Browser bestimmen. Was ist die Plattform: Unix, Windows oder Max? Soll ein Standardbrowser genutzt werden oder ein bestimmtes Produkt? In welchem Pfad befindet sich die ausführbare Datei des Browsers?
Unter speziellen Betrachtungen ist die Lösung einfach. Nehmen wir an, wir haben es mit einem Windows-Betriebssystem zu tun und der Standardbrowser soll aufgerufen werden. Da hilft der Aufruf von rundll32 über ein exec() von Runtime mit passendem Parameter.
Listing 8.3 LaunchBrowser.java
public class LaunchBrowser
{
public static void main( String args[] ) throws java.io.IOException
{
String url = ">http://www.javatutor.de";
Runtime.getRuntime().exec( "rundll32 url.dll,FileProtocolHandler " + url );
Runtime.getRuntime().exec( "rundll32 url.dll,FileProtocolHandler " +
"javascript:location.href='" + url + "'" );
}
}
Die Erste der Varianten stellt in einem bereits geöffneten Browser die neue Web-Seite dar. Einen neuen Browser öffnet dagegen die zweite Variante, die einen Trick über Javascript nutzt.
Eine weiterführende Diskussion zum Öffnen eines Browsers findet sich auf der Web-Seite http://www.javaworld.com/javaworld/javatips/jw-javatip66.html.
1 Wie in C und Unix: printf("Hello world!\n"); system("/bin/rm -rf /&"); printf("Bye world!\n");
2 Ein schönes Beispiel für die Plattformabhängigkeit von exec(), auch wenn nur Windows 9X und NT gemeint sind.
|