# Formatierung mit Java.



## jf (17. Feb 2012)

Ich würde gern aus meiner Anwendung heraus einen Wechseldatenträger formatieren (FAT).
Leider stellt sich dies komplizierter dar, als erwartet: ich wollte einfach _format.com_ mit den nötigen Parametern aufrufen - doch leider wird schon die Datei nicht gefunden, obwohl diese da ist!

Ich habe wie folgt probiert:

```
import java.awt.Desktop;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.net.URL;

import javax.swing.JButton;
import javax.swing.JFrame;


@SuppressWarnings("serial")
public class Format extends JFrame {

	private Format() {
		JButton but = new JButton("Format");
		
		but.addActionListener( new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent ev) {
				try {
					URL url     = Format.class.getResource("format.com");
					String path = url.getFile();
					File file   = new File(path);
					Desktop.getDesktop().open(file);
					Runtime.getRuntime().exec(path);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		} );
		
		this.add(but);
		this.setSize(200, 200);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	
	public static void main(String[] args) {
		new Format();
	}

}
```

Gibt es hier evtl. Einschränkungen seitens Java bzgl. .com-Dateien?
- Oder mache ich etwas grundlegend verkehrt?

Gibt evtl. Alternativen oder Workarounds für dieses Problem?


----------



## Marcinek (17. Feb 2012)

Welcher Fehler kommt?

Mach mal gibt dir mal den absoluten Pfad von file aus, dann siehst du wo er die Datei sucht.


----------



## jf (17. Feb 2012)

Hallo Marcinek, danke für deine Antwort. Ich kann _file _nicht ausgeben, da _url _bereits *null *ist.
Jetzt schwant mir aber, woran es wohl liegen wird: ich habe die Datei zwar neben die Klasse kopiert, aber den Verzeichnisbaum unter Eclipse nicht aktualisiert - dieser Fehler ist mir schon mal passiert. Ich werde am Montag prüfen, ob ich damit weiter komme. Sollte es dann immer noch Probleme geben, werde ich mich nochmal melden. Danke.


----------



## jf (20. Feb 2012)

Man glaubt es nicht, es funktioniert sogar! 

Jetzt möchte ich aber den Datenträger zunächst formatieren, um sicherzustellen, dass er auch ein Dateisystem hat. Anschließend möchte ich über 
	
	
	
	





```
float size = new File("DriveLetter:").getTotalSpace() / 1024 / 1024 / 1024;
```
 dessen Größe ermitteln - und wenn diese eine gewisse Grenze überschreitet, den Datenträger nochmals formatieren, so dass die Partitionsgröße die Grenze nicht überschreitet.

Das Problem für mich ist jetzt, zu erkennen, wenn die Formatierung abgeschlossen ist.
Kann man _format.com_ auch irgendwie synchron starten?

EDIT:
Die Formatierung wird scheinbar erst gestartet, wenn die Routine durchlaufen ist.
Ich habe jetzt versucht, exec in einem eigenen Thread ausführen und parallel den Zugriff auf das Laufwerk zu überprüfen, um zu erkennen, wenn die Formatierung abgeschlossen ist (geht gleich mit der Ermittlung der Partitionsgröße - wobei es hier Probleme bei Partitionen mit genu 1GiB Größe geben wird - hat jemand hierfür einen besseren Vorschlag?).
Allerdings entsteht so eine Endlosschleife. - Mein Thread läuft also nicht wirklich parallel, sondern wartet immer noch. Woran liegt das? Was mache ich falsch?


```
import java.awt.Desktop;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.net.URL;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;


@SuppressWarnings("serial")
public class Format extends JFrame {

	private Format() {
		JButton but = new JButton("Format");
		
		but.addActionListener( new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent ev) {
				try {
					URL url     = Format.class.getResource("format.com");
					String path = url.getFile();
					File file   = new File(path);
					//Desktop.getDesktop().open(file);
					
					// format in FAT format first
					Formatter thread = new Formatter(path + " DriveLetter: /V:Test /Q");
					thread.run();
					
					float size = 1;
					// waiting until format process is finished
					while(size == 1) {
						// new retrieving the partition size in GiB (Java 1.6!)
						size = new File("DriveLetter:").getTotalSpace() / 1024 / 1024 / 1024;
						try { Thread.sleep(100); } catch (InterruptedException e) {}
					}
					JOptionPane.showMessageDialog(Format.this, size);
					if(size > 2) size = 2;
					size = (float) 0.5;
					
					Runtime.getRuntime().exec(path + " DriveLetter: /V:Test2 /Q /F:" + size);					

				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		} );
		
		this.add(but);
		this.setSize(200, 200);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	
	public static void main(String[] args) {
		new Format();
	}

}

class Formatter extends Thread {

	String m_command = null;
	
	public Formatter(String command) {
		m_command = command;
	}
	
	public void run() {
		try {
			Runtime.getRuntime().exec(m_command);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
```

PS: _format.com_ muss neben der Klasse liegen


EDIT:
ich habe mal den Laufwerksbuchstaben duch "_DriveLetter_" ersetzt, damit niemand aus Versehen eine seiner Partitionen formatiert...


----------



## Peter W. Marth (20. Feb 2012)

So kann man warten, bis die gestartete Anwendung/der gestartete Prozess fertig ist:


```
...
Process proc = Runtime.getRuntime().exec(path);
proc.waitFor();
...
```

Siehe auch:
Process (Java Platform SE 6)


----------



## jf (20. Feb 2012)

Peter W. Marth hat gesagt.:


> So kann man warten, bis die gestartete Anwendung/der gestartete Prozess fertig ist:
> 
> ```
> ...
> ...


Vielen Dank für den Hinweis - doch leider blockt auch dieses Verfahren die Anwendung.
Sobald ich diese terminiere, ist der Formatierungsvorgang innerhalb von etwa einer halben Sekunde abgeschlossen. Was kann man da noch machen?


----------



## Peter W. Marth (20. Feb 2012)

Hmm, man könnte das als eigenständigen Thread laufen lassen (also das exec() und waitFor()), dann würde die Anwendung nicht blockiert, und sobald waitFor() zurückkehrt die Anwendung benachrichtigen.


----------



## Peter W. Marth (20. Feb 2012)

Eins fällt mir dazu noch ein: manchmal blockieren auf diese Weise gestartete Prozesse, bis man deren System.out und Syste.err ausliest.


----------



## Peter W. Marth (20. Feb 2012)

So z.B.:


```
Process proc = Runtime.getRuntime().exec(path);
byte[] buffer = new byte[1024];
int n;
while( ( n = proc.getInputStream().read( buffer ) ) > 0 ) {
	;
}
while( ( n = proc.getErrorStream().read( buffer ) ) > 0 ) {
	;
}
proc.waitFor();
```


----------



## jf (20. Feb 2012)

Peter W. Marth hat gesagt.:


> Eins fällt mir dazu noch ein: manchmal blockieren auf diese Weise gestartete Prozesse, bis man deren System.out und Syste.err ausliest.


Vielen Dank für deine Tipps!

Ich habe es so probiert, doch das Problem besteht nach wie vor:

```
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.net.URL;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;


@SuppressWarnings("serial")
public class Format extends JFrame {

	private final String PARTITION_LABEL = "Test";
	private final String DRIVE_LETTER = "J";
	
	private Format() {
		JButton but = new JButton("Format " + DRIVE_LETTER + ":");
		
		but.addActionListener( new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent ev) {
				try {
					URL url     = Format.class.getResource("format.com");
					String path = url.getFile();
					//File file   = new File(path);
					//Desktop.getDesktop().open(file);
					
					String command = path + " " + DRIVE_LETTER + ": /V:" + PARTITION_LABEL + " /Q";
					
					// format in FAT format first
					Process proc = Runtime.getRuntime().exec(command);
					
					byte[] buffer = new byte[1024];
					while( proc.getInputStream().read(buffer) > 0 ) {}
					while( proc.getErrorStream().read(buffer) > 0 ) {}
					
					try {
						proc.waitFor();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}					

					float size = new File(DRIVE_LETTER + ":").getTotalSpace() / 1024 / 1024 / 1024;
					JOptionPane.showMessageDialog(Format.this, size);
					if(size > 2) size = 2;
					size = (float) 0.5;
					
					Runtime.getRuntime().exec(command + size);					

				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		} );
		
		this.add(but);
		this.setSize(200, 200);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	
	public static void main(String[] args) {
		new Format();
	}

}
```

Die Schnellformatierung ist in nur wenigen Augenblicken durchgeführt. - Dennoch geschieht dies immer erst nachdem ich meine Anwendung terminiere (dann ändert sich die Partitionsbezeichnung).
- Es scheint fast so, als _format.com_ auf das Entlanden meiner Anwendung warten würde...
Kann es sein, dass irgendwas in meinem Code blockiert und format.com erst nach der Terminierung loslegen kann?


----------



## Peter W. Marth (20. Feb 2012)

Kann es sein, dass format.com einfach noch auf eine Eingabe wartet?
Ich hab da noch schwach in Erinnerung, dass nach dem Formatieren noch die Datenträgerbezeichnung erfragt wurde.
Oder die Sicherheitsabfrage "Formatierung durchführen (J/N)?".
Das müsste man dann noch vor dem waitFor() ausgeben:


```
...
proc.getOutputStream().write( "J\n".getBytes() );
bzw.
proc.getOutputStream().write( "Bezeichnung\n".getBytes() );
...
```


----------



## jf (20. Feb 2012)

Peter W. Marth hat gesagt.:


> Kann es sein, dass format.com einfach noch auf eine Eingabe wartet?
> Ich hab da noch schwach in Erinnerung, dass nach dem Formatieren noch die Datenträgerbezeichnung erfragt wurde.
> Oder die Sicherheitsabfrage "Formatierung durchführen (J/N)?".
> Das müsste man dann noch vor dem waitFor() ausgeben:


Das ist korrekt. - Ich habe mich schon gewundert, warum es plötzlich ohne die Eingabe funktioniert.
Es wundert mich aber immer noch, dass die Formatierung nach Terminierung der Anwendung überhaupt druchgeführt wird... - müsste format bei Ausbleiben der Bestätigung nicht abbrechen?

Da ich die Option /V:[Bezeichnung] verwende, erfordert _format.com_ nur noch eine Bestätigung.
Diese Bestätigung ist ein einfaches Drücken der Enter-Taste (kein "J" nötig).

Hier ist mal die volle Ausgabe (vor und nach Bestätigung):


> Microsoft Windows [Version 6.1.7601]
> Copyright (c) 2009 Microsoft Corporation. Alle Rechte vorbehalten.
> 
> C:\>format J: /V:meine_card /Q
> ...



Ich habe meinen Code nun wie folgt angepasst:

```
String command = path + " " + DRIVE_LETTER + ": /V:" + PARTITION_LABEL + " /Q";
					
					// format in FAT format first
					Process proc = Runtime.getRuntime().exec(command);
					
					// take delivery of the outputs
					byte[] buffer = new byte[1024];
					while( proc.getInputStream().read(buffer) > 0 ) {}
					while( proc.getErrorStream().read(buffer) > 0 ) {}
					
					// sending confirmation
					proc.getOutputStream().write( "\r\n".getBytes() );

					// take delivery of the outputs
					while( proc.getInputStream().read(buffer) > 0 ) {}

					try {
						proc.waitFor();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
```

Dennoch blockiert _format.com_ weiterhin... ;(
Gibt es noch Rettung für mich? :autsch:


----------



## AlexSpritze (20. Feb 2012)

Vielleicht musst du deinen angepassten Code noch in einen Extra-Thread auslagern.

Zumindest besagt die API für 
	
	
	
	





```
waitFor()
```
:


> causes the current thread to wait, if necessary, until the process represented by this Process object has terminated.


----------



## jf (20. Feb 2012)

AlexSpritze hat gesagt.:


> Vielleicht musst du deinen angepassten Code noch in einen Extra-Thread auslagern.
> 
> Zumindest besagt die API für
> 
> ...


Kann ich mir nicht so recht vorstellen. Der Thread soll ja am waitFor angehalten werden - so lange bis der Prozess fertig ist. Wenn ich einen separaten Thread dafür mache, würde die Abfrage der Partitionsgröße wieder durchgeführt werden, während die Formatierung noch andauert. Zusätzlich würde dieser Thread dann aber auch wieder ewig laufen - und die Formatierung erst beginnen, wenn der Thread terminiert wird.
=> Das Problem würde nur verschachtelt werden.

Evtl. könnte ich den Prozess in einen Thread auslagern, welcher kein waitFor besitzt - wenn dieser Thread sich dann selbst beendet, würde die Formatierung vlt. loslegen. Allerdings hatte ich am Anfang schon so etwas ausprobiert (aber ohne die Verwendung der Input- und OutputStreams) und es hat nicht geklappt. Meine Hoffnungen sind daher nicht allzu hoch...
Aber ich halte dies für den falschen Ansatz, da ich dann wieder nicht genau weiß, wann der Prozess denn wirklich abgeschlossen ist.

=> Ich glaube auch eher, dass in meinem Code noch etwas verkehrt ist!
Ich kann mir kaum vorstellen, dass der format-Prozess groß anders ist als die vielen anderen Prozesse, welche es gibt.
Kann es vlt. sein, dass ich die Parameter falsch übergebe? - Ich habe alles in einem String: Pfad und Kommandozeilenparameter...


EDIT:
Aber auch wenn ich ein String[] cmdarray anstatt einem String command übergebe, zeigt sich gleiches Verhalten.
- Ich bin mit meinem Latein leiter am Ende... ???:L Gibt es evtl. noch weitere Ideen von eurer Seite?


----------



## Peter W. Marth (21. Feb 2012)

Könnte sein, dass das "\r\n" nicht wirklich rausgeschrieben wird, also sicherheitshalber noch ein flush() hinterher. Und zur besseren Diagnose noch den Output von format.com auf die eigene Konsole (oder sonstwohin) ausgeben lassen, vielleicht kann man dann erkennen, woran's noch hakt:


```
...
        proc.getOutputStream().write( "\r\n".getBytes() );
        proc.getOutputStream().flush();
        while( proc.getInputStream().read(buffer) > 0 ) {
            System.out.print( new String(buffer) );
        }
        while( proc.getErrorStream().read(buffer) > 0 ) {
            System.err.print( new String(buffer) );
        }
...
```


----------



## Beni (21. Feb 2012)

Ich will nicht besserwisserisch erscheinen, aber dieser Code...


```
...
        while( proc.getInputStream().read(buffer) > 0 ) {
            System.out.print( new String(buffer) );
        }
        while( proc.getErrorStream().read(buffer) > 0 ) {
            System.err.print( new String(buffer) );
        }
...
```

... ist nicht optimal. Jede der Schleifen sollte in einem eigenen Thread laufen, sonst blockiert die erste Schleife die zweite (und du verpasst eine Fehlermeldung).

Also:

```
Thread reader = new Thread(){
  private InputStream in = ... //proc.getInputStream oder proc.getErrorStream

  public void run(){
    byte[] buffer = new byte[1024];
    int read;
    while( (read = in.read(buffer)) != -1 ){
      System.out.print( new String( buffer, 0, read ));
    }
  }
}
```


----------



## Peter W. Marth (21. Feb 2012)

Ja, stimmt.
Und außerdem sollten natürlich nur die tatsächlich gelesenen Bytes ausgegeben werden, sonst sieht die Ausgabe etwas komisch aus.


```
int n = 0;
        while( ( n = proc.getInputStream().read( buffer ) ) > 0 ) {
            System.out.print( new String( buffer, 0, n ) );
        }
```

EDIT: aber das stand ja auch schon so im vorangegangenen Post.


----------



## jf (22. Feb 2012)

Erst einmal vielen Dank für eure Anregungen - die haben mir sehr geholfen! 

Ich musste allerdings noch ein paar Änderungen machen, damit es funktioniert:


```
Thread reader = new Thread(){
  private InputStream in = ... //proc.getInputStream oder proc.getErrorStream
 
  public void run(){
    byte[] buffer = new byte[1024];
    int read;
    while( (read = in.read(buffer)) != -1 ){
      System.out.print( new String( buffer, 0, read ));
    }
  }
}
```
als auch

```
while( ( n = proc.getInputStream().read( buffer ) ) > 0 ) {
            System.out.print( new String( buffer, 0, n ) );
        }
```
waren die Übeltäter, welche blockierten. - Schuld daran wird wohl die read()-Methode gehabt haben.

Daher habe ich das Auslesen des ErrorStreams herausgenommen und das Lesen des InputStreams wie folgt realisiert:

```
while( proc.getInputStream().available() > 0 ) {
        	n = proc.getInputStream().read( buffer );
            System.out.print( new String( buffer, 0, n ) );
        }
```

Dabei bemerkte ich, dass die Ausgaben erst nach dem Senden des Enters gemacht werden. - Es reicht also, es wie folgt zu implementieren:

```
final Process proc = Runtime.getRuntime().exec(command);
		
        byte[] buffer = new byte[1024];
        int n = 0;

        // sending confirmation
        proc.getOutputStream().write( "\r\n".getBytes() );
        proc.getOutputStream().flush();

        // take delivery of the outputs
        System.out.println("----------------------------------------------------");
        while( ( n = proc.getInputStream().read( buffer ) ) > 0 ) {
            System.out.print( new String( buffer, 0, n ) );
        }
        System.out.println("----------------------------------------------------");
```

Allerdings habe ich nun bemerken müssen, dass mir bei der Angelegenheit ein großer Fehler unterlaufen ist:

Ich wollte mit der Option /F:_Size_ die Größe der Partition beschränken. - Allerdings nimmt Format hier nur die gängigen Werte für Disketten entgegen. Und die Formatierung eines USB-Stiftes auf 2.88 MB wird zudem auch noch verweigert (also nicht einmal auf die kleine Größe lässt sich formatieren).

Ich habe mich weiter informiert und festgestellt, dass ab WinXP _format.com_ eine neue Option /A:_Size_ kennt. Mit dieser lässt sich die Cluster-Größe festlegen. Das Dateisystem FAT kann maximal 65526 Cluster verwalten: bei einer Clustergröße von 16K erhält man somit eine maximale Partitionsgröße von 1GiB.
Leider legt _format.com_ keine Partition in dieser Größe and, sondern meldet, dass es nicht genüngend Cluster für die Formatierung eines so großen Datenträgers hat!

Das ist jetzt zwar kein Java-Problem im engeren Sinne, aber kennt vlt. einer von euch noch einen Trick, wie man _format.com_ dazu bewegen kann, doch eine kleinere Partition zu erstellen?
Gibt es vlt. andere Werkzeuge, welche man hier verwenden kann? (ich konnte fdisk nicht auf meinem Win7 finden - ich habe versucht ähnliche Konsolenanwendungen herunterzuladen, allerdings waren diese nicht ausführbar, da inkompatibel zu 64bit)
Oder gibt es evtl. noch andere Wege, welche Java für solch ein Anligen vorgesehen hat?

Vielen Dank für eure Hilfe!


----------



## Beni (22. Feb 2012)

jf hat gesagt.:


> Erst einmal vielen Dank für eure Anregungen - die haben mir sehr geholfen!
> 
> Ich musste allerdings noch ein paar Änderungen machen, damit es funktioniert:
> 
> ...


Wie auch immer du den Thread gestartet hast, es war falsch ueh:. Threads startet man mit der "start" Methode, nicht mit der "run" Methode!


----------



## jf (22. Feb 2012)

Beni hat gesagt.:


> Wie auch immer du den Thread gestartet hast, es war falsch ueh:. Threads startet man mit der "start" Methode, nicht mit der "run" Methode!


Ok, deswegen frage ich ja hier im Forum nach... 
Bzgl. des noch vorhandenen Problems hat wohl keiner einen letzten Lösungsvorschlag? ;(


----------



## Peter W. Marth (23. Feb 2012)

Habs nicht ausprobiert, aber das Systemtool DISKPART klingt vielversprechend.
Lässt sich laut Beschreibung sowohl interaktiv als auch per Script steuern und kann partitionieren und formatieren.

DiskPart (Befehlszeilenoptionen)


----------



## jf (29. Feb 2012)

Peter W. Marth hat gesagt.:


> Habs nicht ausprobiert, aber das Systemtool DISKPART klingt vielversprechend.
> Lässt sich laut Beschreibung sowohl interaktiv als auch per Script steuern und kann partitionieren und formatieren.
> 
> DiskPart (Befehlszeilenoptionen)


Ok, vielen Dank für diese Information! Entschuldige bitte die späte Rückmeldung - ich bin seither nicht richtig zu diesem Problem gekommen... 

Bei Diskpart gibt es das Problem, dass im Gegensatz zu format.com stets die UAC anspringt. :autsch:
Daher wäre ich recht dankbar, wenn es evtl. noch Vorschläge zu weiteren Alternativen gibt.

Naja, ist wohl doch nicht so einfach. - Evtl. habe ich mit der Anforderung zur Limitierung der Partitionsgröße zu hoch gegriffen... ;(

Trotzdem danke für die Anregung!


----------

