Motivation

Immer dann, wenn ein Programm ein anderes aufruft, z.B. eine IDE einen Cross-Compiler, und dabei etwas schief läuft, ist häufig guter Rat teuer. Während man bei direkt aufgerufenen Programmen den Aufrufvorgang unter eigener Kontrolle hat, ist dies im vorher geschilderten Szenario nicht der Fall. Insbesondere dann, wenn nicht alles so klappt, wie es sein sollte, wäre es interessant zu erfahren, wie das auszuführende Programm aufgerufen wird, d.h. wie sieht die Kommandozeile aus und was sind die Ausgaben des Programms. Letzteres ist besonders dann wichtig, wenn es sich um ein fensterloses Programm handelt, das seine Ausgaben in die Standard-Datenströme stdout und stderr schreibt. Und nicht zuletzt: was ist der Wert des Exit-Codes?

Das Programm DisplayCommandLine zeigt diese Informationen

Verwendung

Als Beispiel soll die Programm-Kombination Gpx und GpxUi dienen. Gpx ist ein Konsolen-Programm, das G-Code zur Steuerung von 3D-Druckern in das X3G-Format umwandelt. GpxUi ist eine grafische Oberfläche, die den Aufruf von Gpx erleichtert. Für GpxUi wäre diese Aktion nicht wirklich notwendig, da diese Applikation sämtliche Angaben im eigenen Ausgabefenster anzeigt. Aber das macht die Kontrolle einfach.

Das zu untersuchende Programm ("gpx.exe") muss umbenannt werden ("gpx.org.exe"). Wenn dabei einfach ein ".org" vor das ".exe" gesetzt wird, findet DisplayCommandLine diese Datei und ruft sie mit den ursprünglichen Kommandozeilenparametern auf. Erhält die umbenannte Datei einen anderen Namen, wird sie nicht aufgerufen.

Die ausführbare Datei "DisplayCommandLine.exe" wird in das Verzeichnis des zu untersuchenden Programms kopiert und umbenannt, so dass sie wie das Originalprogramm heißt: "DisplayCommandLine.exe" -> "gpx.exe".

Verzeichnis

Das war's. Wenn nun das Wrapper-Programm das zu untersuchende Programm aufruft, findet es stattdessen das umbenannte DisplayCommandLine und ruft dieses auf. DisplayCommandLine rruft das Original-Programm mit ".org.exe" am Schluss auf, fängt die Ausgaben ab und zeigt sie an. Wenn DisplayCommandLine geschlossen wird, werden die Ausgaben an das einschließende Programm weitergeleitet.

GpxUi wird gestartet GpxUi Hauptfenster
GpxUi ruft Gpx auf. GpxUi ruft Gpx auf
DisplayCommandLine übernimmt die Kontrolle DisplayCommandLine-Fenster
GpxUi erhält die Ausgaben via DisplayCommandLine DisplayCommandLine beendet

Programm

Das Programm ist recht kurz, hat aber einige Spezialitäten.

Suche nach der Original-Datei

Den Dateipfad zur Original-Datei erhält man, wenn ein ".org" vor das ".exe" im Pfad eingeschoben wird. Um sicher zu gehen, den vollständigen Dateipfad zu erhalten, wird dieser über die Methode Application.ExecutablePath() bezogen. Ihn direkt aus der Kommandozeile zu extrahieren, kann fehlschlagen, da hier relative Pfad-Angaben oder Bezüge auf Environment-Variablen möglich sind. Ebenfalls kann das ".exe" fehlen. Application.ExecutablePath() liefert eine normierte Darstellung.

Extraktion der Kommandozeilen-Parameter

DDiese müssen zwingend aus der bereitgestellten, originalen Kommandozeile extrahiert werden, die man über die Eigenschaft Environment.CommandLine abrufen kann. Die z.B. durch Environment.GetCommandLineArgs() bereitgestellten, einzelnen Parameter kann man nicht nutzen, da diese z.T. bereits aufbereitet sind. Z.B. fehlen evt. Anführungszeichen.

Ausgabe in die Standard-Datenströme

EEiner .NET Windows.Forms-Applikation stehen die Standard-Datenströme stdout und stderr  (d.h. console.write() und console.error.write()) im Normalfall nicht zur Verfügung. Die Ausgaben dorthin "verschwinden" einfach. Über die Kernel-Funktionspan AttachConsole() kann man erreichen, dass die Standard-Datenströme an den einschließenden Prozess weitergeleitet werden.

<llImport("kernel32.dll")>
   Shared Function AttachConsole(ProcessId As Integer) As Boolean
   End Function
   Const ATTACH_PARENT_PROCESS As Integer = -1

   Private Sub cmdClose_Click(sender As Object, e As EventArgs) Handles cmdClose.Click
      AttachConsole(ATTACH_PARENT_PROCESS) ' Console auf den einschließenden-Prozess umlenken
      Console.Write(txtStdOut.Text) ' Auugabenndesdes Originalprogramms weiterleiten
      Console.Error.Write(txtStdErr.Text)
      Console.Out.Flush()
      Console.Error.Flush()
...

Programm-Abschluss

Zum Schluss muss nur noch dafür gesorgt werden, dass der Exit-Code korrekt zurückgeliefert wird:

Environment.Exit(mExcitCode) ' Programm mit dem Exitcode des Originalprogramms beenden

Download

Binary: DisplayCommandLine

Das VB-Projekt für Visual Studio 2012