Diese Woche musste ich eine Lösung finden, eine mit „Console.ReadLine()“ wartende Konsolenanwendung aus einem Thread heraus neu starten zu können. Da diese Funktionalität alles andere als trivial ist, dachte ich mir, dass ein Artikel darüber für den einen oder anderen sehr praktisch sein könnte.
Senden von virtuellen Tastendrücken an die Konsolenanwendung
Wenn man aus einem Thread heraus die wartende Konsolenanwendung ansprechen will, geht das nur indem man ihr vorgaukelt, dass der Benutzer eine Tastenkombination eingegeben hat. Ich habe mich hier für die Kombination „r“ + Eingabe entschieden. Den Sinn bei „r“ für einen Neustart (englisch „restart“) muss ich hier nicht weiter erläutern. Um diese virtuellen Keypresses benutzen zu können, muss man sich aus der user23.dll die Funktion „PostMessage“ importieren.
Folgende Funktion ermöglicht das Senden von r + Eingabe an die aktuell laufende Konsolenanwendung.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
/// <summary> /// Posts a windows message. /// Imported from user32. /// </summary> /// <param name="hWnd">The window handle.</param> /// <param name="Msg">The message constant.</param> /// <param name="wParam">The w param.</param> /// <param name="lParam">The l param.</param> /// <returns>True if succeeded, otherwise false.</returns> [DllImport("user32.dll")] private static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam); /// <summary> /// Restarts the application. /// </summary> public void SendRestartKeys() { const uint WM_KEYDOWN = 0x0100; const int VK_R = 0x52; const int VK_RETURN = 0x0D; var proc = Process.GetCurrentProcess(); PostMessage(proc.MainWindowHandle, WM_KEYDOWN, VK_R, 0); PostMessage(proc.MainWindowHandle, WM_KEYDOWN, VK_RETURN, 0); } |
Auswerten der Eingabe in der wartenden Konsolenanwendung
Die wartende Konsolenanwendung muss dahingehend angepasst werden, dass die Rückgabe der Funktion „Console.ReadLine()“ ausgewertet wird:
1 2 3 4 5 6 7 8 |
var input = Console.ReadLine(); if (input == "r") { Log.Warn("Service restart invoked"); this.eventLogApplicationServ.WriteEntry("Restart invoked for WebService application", EventLogEntryType.Warning, Convert.ToInt32(EventId.InstanceIsRunning)); RestartApplication(); } |
Anwendung neu starten
Die Funktion „RestartApplication()“ führt dann die eigentliche Neustart-Logik aus. Diese wurde von mir wie folgt implementiert:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/// <summary> /// Restarts the application. /// </summary> private static void RestartApplication() { // create new process var newInstance = new Process { StartInfo = { FileName = Thread.GetDomain().BaseDirectory + Thread.GetDomain().FriendlyName, Verb = "runas" } }; // start new instance newInstance.Start(); } |
Zu beachten ist hier: Durch diese Funktion wird ein eigenständiger Prozess angelegt. Der neue Prozess steht nicht in einer Prozessstruktur verbunden zu dem aktuell laufenden Prozess. Darum wird der aktuell laufende Prozess auch ohne Probleme beendet, wenn der neue Prozess gestartet wurde.
Ich hoffe, ich konnte mit dem Artikel dem einen oder anderen bei der Problemlösung helfen. Kommentare, Anregungen und Kritik sind natürlich gern gesehen!