Beschreibung:
Mit diesem Programm können Konsolenprogramme (CMD), oder Befehle parallel auf verschiedenen Servern ausgeführt werden. Minderst Voraussetzung ist eine "server.txt", mit zeilenweise Namen der Server im gleichen Verzeichnis und die Übergabe von dem Dateinamen der Batch, oder eines Befehls, das mehrfach ausgeführt werden soll.
Die Namen der Server werden bei einem Befehl als %%1 und bei Batchdateien als %1 übergeben.
Wenn das Programm läuft, legt es ein "tmp" - Verzeichnis an, in dem die Ausgaben (StdOut, StdErr) in eine Datei (Servernamen) geschrieben werden. Am Ende wird eine Datei erzeugt, mit Datum und Zeit erweitert, in der die Information aus allen Servern zusammen gefasst wird.
Die Hilfe wird wie üblich mit dem Parameter /? oder ohne Angabe von Parametern ausgegeben.
Hier noch die Anzeige der Hilfe:
Startet mehrere Prozesse gleichzeitig mittels Übergabe einer Liste von Server
und der optionalen Vorgabe der maximal gleichzeitig laufenden Prozesse.
Zusätzlich besteht die Möglichkeit vor dem ausführen der Prozesse, die
Erreichbarkeit der Server mittels PING zu ermitteln.
Die Ausgaben (stdOut/stdErr) werden in separaten Protokolldateien erfaßt und
anschließend in einer Protokolldatei zusammengefügt. Fehlermeldungen werden
zusätzlich als Information auf der Konsole ausgegeben.
Die Reihenfolge in der die Server in der Protokolldatei ausgegeben werden,
ist abhängig von der Geschwindigkeit mit der die Server antworten.
Der Name der Protokolldatei hat folgenden Syntax:
FastCMD dd.mm.yyyy hh-mm-ss.txt
Copyright 2014 by Andreas Potsch - Mail an [FastCMD@web.de](mailto:FastCMD@web.de)
Syntax: FastCmd [Befehl|Programm] [Server.txt] [pTIMEOUT] [maxAnzahl]
Befehl/Programm Befehl|Programm, der|daß mehrfach ausgeführt werden soll.
server.txt Eine Textdatei, in der die Servername zeilenweise angegeben
sind, die bei einem Befehl als %%1 und bei einem
Batch-Programm als %1 übergeben werden.
Wichtig!!! Servernamen dürfen nur einmal in der server.txt
vorhanden sein!!!
pTIMEOUT Mit diesem Parameters ist es möglich, vor dem ausführen
der Befehle|Programme ein PING auf den entsprechenden
Server auszuführen. Ist der PING erfolglos, dann wird
dies in die Protokolldatei eingetragen und zusätzlich in
der Konsole ausgegeben. Die Zahl nach dem Buchstaben legt
die Wartezeit (timeout) bis zur Antwort fest. Ist keine
Zahl angegeben, wird der Wert auf 1000ms festgelegt.
Der Parameter ist optional.
maxAnzahl Legt die maximale Anzahl der gleichzeitig ausgeführten
Befehle/Programme fest. Wird dieser Parameter nicht
vorgegeben, starten soviel Befehle/Programme gleichzeitig,
wie Server in der Liste vorhanden sind (maximal 999).
ACHTUNG!! Die Anzahl der gleichzeitig gestarteten Programme,
ist abhängig von dem zur Verfügung stehenden Speicher.
Der Parameter ist optional.
Beispiele:
FastCmd dir \\%%1\c$\Daten server.txt
FastCMD batch.cmd server.txt p5000 50
In der batch.cmd wird %1 als Platzhalter für den jeweiligen
Name des Server aus der server.txt übergeben.
Programm.cs
/*********************************************************************************************************
*
* Mit diesem (Consolen/CMD) Programm können mehrere Tasks gleichzeitigt ausgeführt werden.
* Das Programm ist hauptsächlich für die Arbeit von Systemadministratoren unter dem
* Betriebssystem Windows gedacht.
*
* Voraussetzungen für das ausführen des Programms ist die Installation von DotNetFramwork 2.0
*
* Hilfe bzw. Syntax ist durch Eingabe von "/?" oder Eingabe keiner/falscher Parameter zu erreichen.
* Am Ende im Quelltext ist die Ausgabe des Hilfetextes als Funktion zu finden.
*
* Fragen bzw. Anregungen können unter FastCMD@web.de gestellt werden.
*
\**********************************************************************************************************/
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.Collections.Generic;
namespace FastCmd
{
class Program
{
// Zähler die aktuell laufenden Prozesse
static int prozessRunning = 0;
// Zähler die abgeschlossenen Prozesse
static int prozessComplete = 0;
// Zähler die bisher gestarteten Prozesse
static int prozessStarted = 0;
// gesamt Anzahl der auszuführenden Prozesse
static int prozessesExecuted = 0;
// nimmt den Retunwert der Prozesse auf
static StringBuilder returnString = null;
// Hauptprogramm starten
static int Main(string[] args)
{
try
{
// Array für die Aufnahme der Server
List<string> server = new List<string>();
// Flag, ist Übergabe eine Datei oder ein Befehl
bool isFile = false;
// Flag, ist Ping angefordert
bool isPing = false;
// Timeout für Ping-Abfrabe. Default 1000ms
int timeout = 1000;
// Objekt für den Returnwert der Prozesse erstellen
returnString = new StringBuilder("");
// Anzahl der Maximal gleichzeitig ausgeführten Prozesse.
// Wird durch die Anzahl der Server bzw. der vorgegebenen
// Anzahl der Max. Prozesse angepaßt
int MaxProzessCount = 999;
// Hilfe ausgeben
if (args.Length == 0)
{
// Hilfe anzeigen
Help();
return 0;
}
// Hilfe ausgeben
if (args[0] == "/?")
{
// Hilfe anzeigen
Help();
return 0;
}
// wenn wenige als zwei Argumente übergeben werden
if (args.Length < 2)
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Bitte mindestens zwei Parameter angeben!");
Console.WriteLine("");
Help();
return 1;
}
// Prüfen ob Server.txt-Datei vorhanden ist
if (!File.Exists(args[1]))
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Serverdatei ist nicht vorhanden!!!");
Console.WriteLine("");
Help();
return 1;
}
// prüfen, ob ein drittes Argument vorhanden und eine Zahl oder ein Buchstabe ist
if (args.Length > 2)
{
// Ist in dem 3. Argument der Buchstabe "p" enthalten
if (args[2].ToLower().ToString().Contains("p") == true)
{
// ist das 3. Argument länger als ein Zeichen
if (args[2].Length > 1)
{
// Prüfen ob nach dem Buchstaben eine Zahl folgt
int num;
bool test = int.TryParse(args[2].Substring(1), out num);
if (test == true)
{
// ist die Zahl größer als 0
if (num > 0)
{
// dann die Zahl für Ping-Timeout übernehmen
timeout = num;
}
else
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Die Zahl nach 'p' muß größer als null sein!!!");
Console.WriteLine("");
Help();
return 1;
}
}
else
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Nach dem Buchstaben 'p' muß eine Zahl erfolgen!!!");
Console.WriteLine("");
Help();
return 1;
}
}
// Ping ist angefordert
isPing = true;
}
// Ist das 3. Argument nur eine Zahl
else
{
int num;
bool test = int.TryParse(args[2], out num);
if (test == false)
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Bitte eine Zahl als dritten Parameter eingeben,");
Console.WriteLine(" oder mit dem Buchstaben 'p' beginnen lassen!!!");
Console.WriteLine("");
Help();
return 1;
}
// Beträgt die Zahl maximal 999
if (num >= 999)
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Bitte keine Zahl über 999 eingeben!!!");
Console.WriteLine("");
Help();
return 1;
}
// maximale Anzahl der gleichzeitigen laufenden Prozesse übernehmen
MaxProzessCount = num;
}
}
// prüfen, ob ein vierte Argument vorhanden und eine Zahl ist
if (args.Length > 3)
{
int num;
bool test = int.TryParse(args[3], out num);
if (test == false)
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Bitte eine Zahl als vierten Parameter eingeben!!!");
Console.WriteLine("");
Help();
return 1;
}
// Beträgt die Zahl maximal 999
if (num >= 999)
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Bitte keine Zahl über 999 eingeben!!!");
Console.WriteLine("");
Help();
return 1;
}
// maximale Anzahl der gleichzeitigen laufenden Prozesse übernehmen
MaxProzessCount = num;
}
// Prüfen ob Befehl oder Batchdatei übergeben wurde
if (File.Exists(args[0]))
isFile = true;
// Objekt Stopuhr erstellen
Stopwatch stopWatch = new Stopwatch();
// Stopuhr starten
stopWatch.Start();
// Wenn tmp-Verzeichnis vorhanden ist
if (Directory.Exists(Environment.CurrentDirectory + "\\tmp"))
// dann tmp-Verzeichnis löschen
Directory.Delete(Environment.CurrentDirectory + "\\tmp", true);
// Neues tmp-Verzeichnis anlegen
Directory.CreateDirectory(Environment.CurrentDirectory + "\\tmp");
// Server aus der Serverdatei einlesen
string line;
StreamReader file = new StreamReader(args[1]);
// Erste Zeile auslesen und überprüfen, ob sie Text enhält
line = file.ReadLine().ToLower().Trim();
if ((line == null) || (line == ""))
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Keine Server in der Serverdatei vorhanden,");
Console.WriteLine(" oder die erste Zeile ist leer!!!");
Console.WriteLine("");
Help();
return 1;
}
else
// Server dem Array hinzufügen
server.Add(line);
// Solange bis alle Zeilen ausgelesen sind
while ((line = file.ReadLine().ToLower().Trim()) != null)
{
// nur wenn die Zeile Text (Server) enthält
if (line != String.Empty)
{
// Ist der Server vorhanden
if (!server.Contains(line))
{
// nein, Server einfügen
server.Add(line);
}
else
{
// ja, Meldung ausgeben
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Servername: " + line.ToUpper() + " ist doppelt vorhanden!!!");
Console.WriteLine("");
Help();
return 1;
}
}
}
// die Anzal aller Prozesse für die Anzeige in der Konsole festlegen (showAktiviti)
prozessesExecuted = server.Count;
// Array vom Objekt NewProzess erstellen
NewProzess[] prozessList = new NewProzess[server.Count];
// Alle Server aus der Datei mit Befehl|Programm in das Array eintragen
// und die Prozedur für das "Prozesscompleted"-Ereignis zuweisen
for (int i = 0; i < server.Count; i++)
{
prozessList[i] = new NewProzess(args[0], server[i], isFile, isPing, timeout);
prozessList[i].prozessCompleted +=
new NewProzess.ProzessCompletedDelegat(newProzess_completeProzess);
}
// Ist die maximale Anzahl der Server kleiner als die Anzahl der Server in der txt,
// dann Anzahl der max. Prozesse auf Anzahl der Server anpassen
if (prozessList.Length < MaxProzessCount)
MaxProzessCount = prozessList.Length;
// Task für die dynamische Anzeige über Taskstatus in der Konsole
Thread showActiviti = new Thread(new ThreadStart(StartActiviti));
// Starte Task
showActiviti.Start();
// solange nicht alle Server abgearbeitet sind
while (prozessComplete < server.Count)
{
// Begrenzung der Max. gleichzeitig laufenden Prozesse überprüfen
if (prozessRunning < MaxProzessCount)
{
// ist das Ende der Anzahl der Prozesse erreicht
if (prozessStarted < prozessList.Length)
{
// Prozesse starten
prozessList[prozessStarted].StartProzess();
// Zähler für die Anzahl laufender Prozesse erhöhen
prozessRunning++;
// Zähler für die Anzahl der bisher gestarteten Prozesse erhöhen
prozessStarted++;
}
}
}
// Format für das Datum in der Protokolldatei
DateTime time = DateTime.Now;
string format = "dd.MM.yyyy HH-mm-ss";
// Name der gesamt Protokolldatei wird gebildet
string writeFileName = string.Format("{0}\\{1}.txt",
Environment.CurrentDirectory,
Process.GetCurrentProcess().ProcessName +
" " + time.ToString(format));
// erstellen der gesamt Protokolldatei
StreamWriter proFile = new StreamWriter(writeFileName);
// Wenn Puffer voll ist, dann automatisch schreiben
proFile.AutoFlush = true;
// Alle Textdateien aus dem tmp-Verzeichnis zusammenfügen
for (int i = 0; i < server.Count; i++)
{
// nimmt den Inhalt der einzelnen Protokolldatei auf und erweitert sie um den Servernamen
string allLine = "####### " + server[i] +
" ###########################################################################" +
Environment.NewLine;
// nimmt den Namen der einzelnen Protokolldateinamen auf
string readFileName = String.Empty;
// Der Name der Protokolldatei im tmp wird gebildet
readFileName = string.Format("{0}\\tmp\\{1}.txt",
Environment.CurrentDirectory, server[i]);
// Prüfen ob die Protokolldatei existiert
if (File.Exists(readFileName))
{
// Öffnen der Protokolldatei
StreamReader resfile = new StreamReader(readFileName);
// Alle Zeilen auslesen
allLine += resfile.ReadToEnd();
// In Datei Schreiben
proFile.Write(allLine);
// Protokolldatei schließen
resfile.Close();
}
// Wenn keine Datei vorhanden ist
else
{
// In Datei Schreiben
proFile.Write(allLine);
proFile.WriteLine("");
proFile.WriteLine("!!! Ausgabedatei nicht vorhanden, oder kein Ping möglich !!!");
proFile.WriteLine("");
}
}
// Reste aus dem Puffer schreiben
proFile.Flush();
// Warten bis alles geschrieben ist
Thread.Sleep(50);
// Protokolldatei schließen
proFile.Close();
// Wenn keine Dateien im Verzeichnis existieren
if(!File.Exists(Environment.CurrentDirectory + "\\tmp"))
// dann tmp-Verzeichnis löschen
Directory.Delete(Environment.CurrentDirectory + "\\tmp", true);
// Stopuhr stoppen
stopWatch.Stop();
// Zeitdifferenz auslesen
TimeSpan ts = stopWatch.Elapsed;
// Ausgabeformat für die Laufzeit festlegen
string elapsedTime = String.Format("{0:D2}:{1:D2}.{2:D3}",
ts.Minutes, ts.Seconds,
ts.Milliseconds);
// Wenn ein Fehler aufgetreten ist
if (returnString.ToString() != "")
{
// Leerzeile ausgeben
Console.WriteLine("");
// Fehlermeldung ausgeben
Console.WriteLine(returnString);
}
// Leerzeilen ausgeben
Console.WriteLine("");
Console.WriteLine("");
// Ausgabe auf der Laufzeit in der Konsole
Console.WriteLine("Laufzeit: " + elapsedTime);
// Leerzeilen ausgeben
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
return 0;
}
// Ausführen im Fehlerfall
catch (Exception ex)
{
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine(ex.ToString());
Console.WriteLine("");
return 1;
}
}
// Funktion die bei dem Ereignis "Prozesscompleted" aufgerufen wird
static void newProzess_completeProzess(object sender, prozessReturnArgs e)
{
try
{
// Zähler für die Anzahl der laufende Prozesse negieren
prozessRunning--;
// Anzahl der fertigen Prozesse erhöhen
prozessComplete++;
// Prüfen ob ein Fehler aufgetreten ist
if (e.server != String.Empty)
{
// ist Text in e.ErrMsg vorhanden
if (e.ErrMsg != String.Empty)
{
// Fehlermeldung anhängen
returnString.Append(Environment.NewLine +
"kein Ping möglich -> " + e.server);
}
// ist kein Text in e.ErrMsg vorhanden
else
{
// Fehlermeldung anhängen
returnString.Append(Environment.NewLine +
"Fehler in Logdatei -> " + e.server);
}
}
}
// Ausführen im Fehlerfall
catch (Exception ex)
{
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine("");
}
}
// Anzeige der Aktivitäten in der Konsole
static void StartActiviti()
{
try
{
// Zähler für die activeSymbol (Array)
int counter = 0;
// Zähler für die Verzögerungsschleife
int delay = 0;
// Das Flag zeigt an ob alle Prozesse beendet sind
bool notCompeled = true;
// Textinhalt der Ausgabezeile aller Aktivitäten
string line = "";
Console.WriteLine("");
// Die "activeSymbol" - Zeichen, die angezeigt werden
string[] activSymbol = new string[] { "|", "/", "-", "\\" };
// solange nicht alle Prozesse abgearbeitet sind
while (notCompeled)
{
// Wenn noch nicht alle Prozesse abgearbeitet sind
if (prozessComplete != prozessesExecuted)
{
// Den aktuellen Status anzeigen mit activeSymbols
line = string.Format("Prozesse aktiv: {0,3} -> {1,3} Prozesse beendet ({2,3}%) {3} \r",
prozessRunning,
prozessComplete,
prozessComplete * 100 / prozessesExecuted,
activSymbol[counter]);
}
// Wenn alle Prozesse abgearbeitet sind
else
{
// Den aktuellen Status (bei 100%) ohne activeSymbol anzeigen
line = string.Format("Prozesse aktiv: {0,3} -> {1,3} Prozesse beendet ({2,3}%) \r",
prozessRunning,
prozessComplete,
prozessComplete * 100 / prozessesExecuted);
// Alle Prozesse abgearbeitet
notCompeled = false;
}
// Ausgabe aktueller Status
Console.Write(line);
// 5 ms warten
Thread.Sleep(5);
// Verzögerungsschleife
if (++delay > 5)
{
// Zähler zurücksetzen
delay = 0;
// Hochzählen der "activeSymbol" (Array)
if (++counter > activSymbol.Length - 1)
// Zähler für "actveSymbol" zurücksetzen
counter = 0;
}
}
}
// Ausführen im Fehlerfall
catch (Exception ex)
{
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine("");
}
}
// Inhalt der Hilfe
static void Help()
{
Console.WriteLine("");
Console.WriteLine("Startet mehrere Prozesse gleichzeitig mittels Übergabe einer Liste von Server");
Console.WriteLine("und der optionalen Vorgabe der maximal gleichzeitig laufenden Prozesse.");
Console.WriteLine("");
Console.WriteLine("Zusätzlich besteht die Möglichkeit vor dem ausführen der Prozesse, die");
Console.WriteLine("Erreichbarkeit der Server mittels PING zu ermitteln.");
Console.WriteLine("");
Console.WriteLine("Die Ausgaben (stdOut/stdErr) werden in separaten Protokolldateien erfaßt und");
Console.WriteLine("anschließend in einer Protokolldatei zusammengefügt. Fehlermeldungen werden");
Console.WriteLine("zusätzlich als Information auf der Konsole ausgegeben.");
Console.WriteLine("");
Console.WriteLine("Die Reihenfolge in der die Server in der Protokolldatei ausgegeben werden,");
Console.WriteLine("ist abhängig von der Geschwindigkeit mit der die Server antworten.");
Console.WriteLine("");
Console.WriteLine("Der Name der Protokolldatei hat folgenden Syntax:");
Console.WriteLine("");
Console.WriteLine(" " + Process.GetCurrentProcess().ProcessName +
" dd.mm.yyyy hh-mm-ss.txt");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("Copyright 2014 by Andreas Potsch - Mail an FastCMD@web.de");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("Syntax: FastCmd [Befehl|Programm] [Server.txt] [pTIMEOUT] [maxAnzahl]");
Console.WriteLine("");
Console.WriteLine(" Befehl/Programm Befehl|Programm, der|daß mehrfach ausgeführt werden soll.");
Console.WriteLine("");
Console.WriteLine(" server.txt Eine Textdatei, in der die Servername zeilenweise angegeben");
Console.WriteLine(" sind, die bei einem Befehl als %%1 und bei einem");
Console.WriteLine(" Batch-Programm als %1 übergeben werden.");
Console.WriteLine(" Wichtig!!! Servernamen dürfen nur einmal in der server.txt");
Console.WriteLine(" vorhanden sein!!!");
Console.WriteLine("");
Console.WriteLine(" pTIMEOUT Mit diesem Parameters ist es möglich, vor dem ausführen");
Console.WriteLine(" der Befehle|Programme ein PING auf den entsprechenden");
Console.WriteLine(" Server auszuführen. Ist der PING erfolglos, dann wird");
Console.WriteLine(" dies in die Protokolldatei eingetragen und zusätzlich in");
Console.WriteLine(" der Konsole ausgegeben. Die Zahl nach dem Buchstaben legt");
Console.WriteLine(" die Wartezeit (timeout) bis zur Antwort fest. Ist keine");
Console.WriteLine(" Zahl angegeben, wird der Wert auf 1000ms festgelegt.");
Console.WriteLine(" Der Parameter ist optional.");
Console.WriteLine("");
Console.WriteLine(" maxAnzahl Legt die maximale Anzahl der gleichzeitig ausgeführten");
Console.WriteLine(" Befehle/Programme fest. Wird dieser Parameter nicht");
Console.WriteLine(" vorgegeben, starten soviel Befehle/Programme gleichzeitig,");
Console.WriteLine(" wie Server in der Liste vorhanden sind (maximal 999).");
Console.WriteLine(" ACHTUNG!! Die Anzahl der gleichzeitig gestarteten Programme,");
Console.WriteLine(" ist abhängig von dem zur Verfügung stehenden Speicher.");
Console.WriteLine(" Der Parameter ist optional.");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Beispiele: " + Process.GetCurrentProcess().ProcessName +
" \"dir \\\\%%1\\c$\\Daten\" server.txt");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" " + Process.GetCurrentProcess().ProcessName +
" batch.cmd server.txt p5000 50");
Console.WriteLine("");
Console.WriteLine(" In der batch.cmd wird %1 als Platzhalter für den jeweiligen");
Console.WriteLine(" Name des Server aus der server.txt übergeben.");
Console.WriteLine("");
Console.WriteLine("");
}
}
}
NewProzess
/*********************************************************************************************************
*
* Diese Klasse stellt den Prozess dar, der die Befehle für einen Server ausgeführt.
*
* Von dieser Klasse werden soviele Objekte erstellt, wie Server in der server.txt vohanden sind.
* Jedes Objekt dieser Klasse wird in einem eigenen Thread ausgeführt und läuft quasi parallel.
*
\**********************************************************************************************************/
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Diagnostics;
namespace FastCmd
{
class NewProzess
{
// Delegat für die Ereignisfunktion
public delegate void ProzessCompletedDelegat(object sender, prozessReturnArgs e);
// Funktion die das Ereignis darstellt
public event ProzessCompletedDelegat prozessCompleted;
// Ojekt zum ausführen eines Prozesses erstellen
private Process myProzess = new Process();
// nimmt die Variable "arguments" aus dem Konstruktor auf
private string arguments = String.Empty;
// nimmt die Variable "fileName" aus dem Konstruktor auf
private string fileName = String.Empty;
// nimmt die Variable "ping" aus dem Konstruktor auf
private bool isPing = false;
// nimmt die Variable "timeout" aus dem Konstruktor auf
private int timeout = 0;
// nimmt den Stream von stdOutput auf
private StringBuilder stdOutput = null;
// Anzahl der durch StdOutput empfangenden Zeilen
private int numOutputLines = 0;
// zeigt an ob der Prozess fehlerhaft beendet wurde
private string taskError = String.Empty;
// enthält die Fehlermeldung
private string msgError = String.Empty;
// Objekt zur Aufnahme der Rückgabewerte (taskError, msgError)
prozessReturnArgs args = new prozessReturnArgs();
// Konstruktor für die Klasse mit Übergabe der Parameter
public NewProzess(string fileName, string arguments, bool isFile, bool isPing, int timeout)
{
try
{
// Argumente an die globale Variable übergeben
this.arguments = arguments;
// Argumente an die globale Variable übergeben
this.fileName = fileName;
// Argumente an die globale Variable übergeben
this.isPing = isPing;
// Argumente an die globale Variable übergeben
this.timeout = timeout;
// Ereignisfunktion für StdOutput festlegen
myProzess.OutputDataReceived += new DataReceivedEventHandler(myProzess_OutputDataReceived);
// Ereignisfunktion für StdError festlegen
myProzess.ErrorDataReceived += new DataReceivedEventHandler(myProzess_ErrorDataReceived);
// Ist der Parameter "arguments" eine Datei
if (isFile == true)
{
// Programmname
myProzess.StartInfo.FileName = fileName;
// Programmargumente
myProzess.StartInfo.Arguments = arguments;
}
// Ist der Parameter "arguments" ein Befehl
else
{
// Programmname
myProzess.StartInfo.FileName = "cmd.exe";
// ist %1 in arguments enthalten
if (arguments.Contains("%1"))
{
// Der Platzhalter wird durch den Servernamen ersetzt
fileName = fileName.Replace("%1", arguments);
}
// Damit das Programm an Ende automatisch beendet wird
fileName = string.Format(" /c {0}", fileName);
// Programmargumente
myProzess.StartInfo.Arguments = fileName;
}
// Prozess ist nicht sichtbar
myProzess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
// StdOutput und StdError empfangen
myProzess.StartInfo.UseShellExecute = false;
// StdOutput zulassen
myProzess.StartInfo.RedirectStandardOutput = true;
// StdError zulassen
myProzess.StartInfo.RedirectStandardError = true;
}
// Fehlerbehandlung
catch (Exception ex)
{
Console.WriteLine("");
Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine("");
}
}
// Wird ausgelöst, wenn Daten über StdError eingehen
void myProzess_ErrorDataReceived(object sendingProzess, DataReceivedEventArgs e)
{
try
{
// wenn Daten von StdError verfügbar sind
if (!String.IsNullOrEmpty(e.Data))
{
// Zeile hochzählen, wenn Daten verfügbar sind
numOutputLines++;
// Text dem StringBuilder hinzufügen
stdOutput.Append(Environment.NewLine + "ERROR: " + e.Data);
// Festlegen, das ein Fehler aufgetreten ist
taskError = arguments;
}
}
// Fehlerbehandlung
catch (Exception ex)
{
Console.WriteLine("");
Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine("");
}
}
// Wird ausgelöst, wenn Daten über StdOutput eingehen
void myProzess_OutputDataReceived(object sendingProzess, DataReceivedEventArgs e)
{
try
{
// wenn Daten von StdOutput verfügbar sind
if (!String.IsNullOrEmpty(e.Data))
{
// Zeile hochzählen, wenn Daten verfügbar sind
numOutputLines++;
// Text dem StringBuilder hinzufügen
stdOutput.Append(Environment.NewLine + e.Data);
}
}
// Fehlerbehandlung
catch (Exception ex)
{
Console.WriteLine("");
Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine("");
}
}
// Funktion zum starten des Prozesses in dem Objekt
public void StartProzess()
{
try
{
// Thread erstellen
Thread myThread = new Thread(new ThreadStart(StartTask));
// Thread starten
myThread.Start();
}
// Die Ausnahme wird ausgelöst, wenn ein NULL-Verweis an die Methode übergeben wird.
catch (System.ArgumentNullException ex)
{
Console.WriteLine("");
Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine("");
}
// Die Ausnahme wird ausgelöst, wenn nicht genügend Arbeitsspeicher zur Verfügung steht.
catch (System.OutOfMemoryException ex)
{
Console.WriteLine("");
Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine("");
}
// Die Ausnahme wird bei allen anderen Fehlern ausgelöst.
catch (Exception ex)
{
Console.WriteLine("");
Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine("");
}
}
// eigenen Task für den Prozess starten
private void StartTask()
{
try
{
// Objekt erstellen
stdOutput = new StringBuilder("");
// wird ein Ping angefordert
if (isPing == true)
{
// Ping Objekt erstellen und ausführen
PingIP ping = new PingIP(arguments, timeout);
// Konnte ein Ping erfolgreich empfangen werden
if (ping.result == true)
{
// den Prozess starten
myProzess.Start();
// Startet asynchrones lesen des Outputstreams
myProzess.BeginOutputReadLine();
// Startet asynchrones lesen des Errorstreams
myProzess.BeginErrorReadLine();
// warten bis der Prozess beendet ist, maximal 10 Minuten
myProzess.WaitForExit(600000);
// Sind Daten von StdOutput vorhanden, dann in Textdatei schreiben
if (numOutputLines > 0)
{
// Dateiname für die StdOutput Rückmeldung festlegen
string place = string.Format("{0}\\tmp\\{1}.txt", Environment.CurrentDirectory, arguments);
// Objekt für den Stream von StdOutput erzeugen
StreamWriter file = new StreamWriter(place);
// Leerzeilen hinzufügen
stdOutput.Append(Environment.NewLine + Environment.NewLine);
// StdOutput in Textdatei schreiben
file.Write(stdOutput);
// Reste aus dem Puffer schreiben
file.Flush();
// Warten bis alles geschrieben ist
Thread.Sleep(50);
// StreamWriter schließen
file.Close();
}
}
// Ping nicht erfolgreich gewesen
else
{
taskError = arguments;
msgError = "Server nicht erreichbar !!!";
}
}
// Es wird kein Ping angefordert
else
{
// den Prozess starten
myProzess.Start();
// Startet asynchrones lesen des Outputstreams
myProzess.BeginOutputReadLine();
// Startet asynchrones lesen des Errorstreams
myProzess.BeginErrorReadLine();
// warten bis der Prozess beendet ist
myProzess.WaitForExit();
// Sind Daten von StdOutput vorhanden, dann in Textdatei schreiben
if (numOutputLines > 0)
{
// Dateiname für die StdOutput Rückmeldung festlegen
string place = string.Format("{0}\\tmp\\{1}.txt", Environment.CurrentDirectory, arguments);
// Objekt für den Stream von StdOutput erzeugen
StreamWriter file = new StreamWriter(place);
// Leerzeilen hinzufügen
stdOutput.Append(Environment.NewLine + Environment.NewLine);
// StdOutput in Textdatei schreiben
file.Write(stdOutput);
// Reste aus dem Puffer schreiben
file.Flush();
// Warten bis alles geschrieben ist
Thread.Sleep(50);
// StreamWriter schließen
file.Close();
}
}
// Status wird als Argument übergeben
args.server = taskError;
// Fehlermeldung wird als Argument übergeben
args.ErrMsg = msgError;
// Prozess beendet, Ereignis auslösen
OnEvent(args);
}
// Fehlerbehandlung
catch (Exception ex)
{
Console.WriteLine("");
Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine("");
}
}
// Funktion löst das Ereigis "prozessCompleted" aus
protected virtual void OnEvent(prozessReturnArgs e)
{
try
{
// Ereignisfunktion für StdOutput lösen
myProzess.OutputDataReceived -= new DataReceivedEventHandler(myProzess_OutputDataReceived);
// Ereignisfunktion für StdError lösen
myProzess.ErrorDataReceived -= new DataReceivedEventHandler(myProzess_ErrorDataReceived);
// sind Empfänger für das Ereignis vorhanden
if (prozessCompleted != null)
// Ereignis auslösen
prozessCompleted(this, e);
}
// Fehlerbehandlung
catch (Exception ex)
{
Console.WriteLine("");
Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine("");
}
}
}
// Klasse für die Rückgabeparameter
public class prozessReturnArgs : EventArgs
{
public string server { get; set; }
public string ErrMsg { get; set; }
}
}
PingIP.cs
/*********************************************************************************************************
*
* Diese Klasse stellt ein Objekt dar, welches ein Ping auf den übergebenen Servernamen ausführt.
*
* Quelle:
* http://msdn.microsoft.com/en-us/library/system.net.networkinformation.ping%28v=vs.80%29.aspx
*
\**********************************************************************************************************/
using System;
using System.Net;
using System.Text;
using System.Threading;
using System.Net.NetworkInformation;
namespace FastCmd
{
class PingIP
{
// Ergebnis der Ping-Abfrage
bool status = false;
// Eigenschaft der Klasse, enthält das Ergebnis aus der Ping-Abfrage
public bool result
{
get { return status; }
}
// Konstruktor mit übergabe der Servers und timeout
public PingIP(string hostName, int timeout)
{
try
{
IPHostEntry host;
IPAddress Adresse = null;
// Die IP-Adresse des Servers wird abgefragt
host = Dns.GetHostEntry(hostName);
// Liste aller IP-Adressen durchlaufen
foreach (IPAddress ip in host.AddressList)
Adresse = ip;
AutoResetEvent waiter = new AutoResetEvent(false);
Ping pingSender = new Ping();
// When the PingCompleted event is raised,
// the PingCompletedCallback method is called.
pingSender.PingCompleted += new PingCompletedEventHandler(PingCompletedCallback);
// Create a buffer of 32 bytes of data to be transmitted.
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
// Set options for transmission:
// The data can go through 64 gateways or routers
// before it is destroyed, and the data packet
// cannot be fragmented.
PingOptions options = new PingOptions(64, true);
// Send the ping asynchronously.
// Use the waiter as the user token.
// When the callback completes, it can wake up this thread.
pingSender.SendAsync(Adresse, timeout, buffer, options, waiter);
// Prevent this example application from ending.
// A real application should do something useful
// when possible.
waiter.WaitOne();
}
// Fehlerbehandlung
catch (Exception)
{
// Ping nicht erfolgreich
status = false;
}
}
public void PingCompletedCallback(object sender, PingCompletedEventArgs e)
{
// If the operation was canceled or an error occurred, generate a message to the user.
if ((e.Cancelled)||(e.Error != null))
{
// Ping nicht erfolgreich
status = false;
// Let the main thread resume.
// UserToken is the AutoResetEvent object that the main thread
// is waiting for.
((AutoResetEvent)e.UserState).Set();
return;
}
// Ping erfolgreich
status = true;
// Let the main thread resume.
((AutoResetEvent)e.UserState).Set();
}
}
}
Anregungen und Fragen können unter FastCMD@web.de gestellt werden.
Schlagwörter: FastCMD Admin Betriebsführung Multi Thread Parallel
Hi,
danke für den Code. Ein paar Anmerkeungen von mir
Kommentare passen oft nicht zum Code:
// prüfen, ob ein drittes Argument vorhanden und eine Zahl oder ein Buchstabe ist
if (args.Length > 2)
{
Manche Prüfungen basieren wohl auf nicht ganz 100%tigem Verständnis von .NET und sind unnötig aufwändig
// Ist in dem 3. Argument der Buchstabe "p" enthalten
if (args[2].ToLower().ToString().Contains("p") == true)
{
.. oder Fehleranfällig (Path-Klasse verwenden!)
// Wenn tmp-Verzeichnis vorhanden ist
if (Directory.Exists(Environment.CurrentDirectory + "\\tmp"))
Sehr unglücklich gewähler Klassenname
// Array vom Objekt NewProzess erstellen
NewProzess[] prozessList = new NewProzess[server.Count];
Streams packt man immer in usings, wenn möglich
// Öffnen der Protokolldatei
StreamReader resfile = new StreamReader(readFileName);
Sowas ist nicht nur schlechter stil; es macht auch keinen Sinn.
Wenn auf irgendwas gewartet werden muss, dann arbeitet man mit Events o.ä.. Insgesamt fehler dem Code ein deutlich Touch OOP.
// Warten bis alles geschrieben ist
Thread.Sleep(50);
// 5 ms warten
Thread.Sleep(5);
Insgesamt eine gute Basis und eine gute Idee; wäre mir aber viel zu fehleranfällig für den produktiven Betrieb.
Zudem gibt es bessere Möglichkeiten um zB CommandLine zu parsen, wie das NuGet-Package CommandLineParser.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Hi,
danke für die konstruktiven Anmerkungen.
Was den Quellcode angeht bin ich für Verbesserungsvorschläge empfänglich.
Es können natürlich auch Änderungen/Verbesserungen an dem Code vorgenommen und dann neu gepostet werden, hoffentlich.
Alles und jeder ist fehlbar. Nichts und niemand ist unfehlbar.
Gruß
Andreas
Hallöchen,
solche Programme sind ganz gut um sich in neue Themen einzuarbeiten. Wenn das noch aktuell ist und für dich interessant, versuch mal deinen Code selbst zu überarbeiten. Du kannst vieles in kleine Methoden auslagern (zum beispiel die Konsolenausgabe der Exceptions). Dadurch dürfte dein gesamter Quelltext auf ca 20 % des jetzigen schrumpfen.
Was die Benennung angeht...solange man weiß wofür etwas da ist reichts immer. Du musst selbst deinen Quellcode nach nem Jahr noch ohne Nachdenken lesen können, das machts auch viel leichter für andere, wenn die nicht raten müssen.
So als kleiner Anreiz für eine zusätzliche Optimierung:
Für die Kommandozeilen kannst du dir sowas wie nen "Wrapper" schreiben. Eine Klasse die in etwa so aussieht:
class CommandLineParams
{
public CommandLineParams(string[] args)
{
// das hier schreibt man etwas eleganter aber sollte für dich verständlich sein
foreach (var argument in args)
ShowHelp = argument.ToLower() == "/?"; // hier kommt true oder false raus
}
//Gegen diese Eigenschaft kannst du dann prüfen ob du eine Hilfe anzeigen möchtest
public bool ShowHelp {get; protected set;}
}
Nun kannst du direkt nach dem Programmstart deine args aus Main() parsen (->übersetzen) und dein Programm wird später viel einfach zu verstehen sein:
var commandLineParams = new CommandLineParams(args);
if (commandLineParams.ShowHelp)
{
ShowHelp();
}
So hast du deinen Papyrusrollenquellcode (weils so schön untereinandersteht) viel leichter im Griff, wenn jemand nähmlich mal ein Programm wiederverwendet wird er oder sie vielleicht nach einer Methode suchen, die ShowHelp entspricht.
vg
Wenn's zum weinen nicht reicht, lach drüber!