StdinWrite / -Read ohne Child Process möglich?

  • Hi zusammen,

    ich brauche erneut euren Rat und hoffe ihr könnt mir helfen.

    Folgende Ausgangssituation habe ich:

    Ich nutze Forking mit einer stark veränderten AuThreads UDF (sprich, ich starte mein eigenes Script (*.exe) mehrmals für unterschiedliche Tasks).

    Diese Subthreads werden kurzfristig gestartet, laufen 1-5 Sekunden und sollen mir eine Info an den Mainthread liefern.

    Eine dieser Info soll mir eine Consolen Anwendung eines anderen nicht von mir geschriebenen Programms liefern.

    Leider braucht diese andere Consolen Anwendung lange zum Starten.

    Bisher starte ich dieses Programm mit meinem MainThread zu Beginn mit und lasse es im Hintergrund laufen (PID bekannt).

    Wenn ich diese Consolen Anwendung abfrage funktioniert das auch perfekt und ich erhalte meine Rückgabe.

    Jetzt möchte ich gerne (aus Laufzeitgründen und zum besseren Handling) diese Abfrage auf einen SubThread abwälzen.

    Sprich, die Consolen Anwendung ist ein Child Prozess meines MainThreads, starte einen SubThread meines eigenen Programms, gib dem die Info der PID der Consolen Anwendung mit, die wiederum die Abfrage an die Consolen Anwendung schickt und auslesen soll.

    Gibt es hierfür Möglichkeiten?

    Unterm Strich würde ich gerne StdinWrite/-Read auf Programme anwenden, die KEINE Child Prozesse sind.

    Vielen Dank im Voraus. :)

    VG

    borsTiHD

  • Hm... so ganz verstehe ich das noch nicht, wäre aber interessant. :)

    Aber ich glaube das geht leider nicht... diese Consolen Anwendung von der ich rede, ist in Wirklichkeit ein Python Script, welches von seinem Ersteller in eine *.exe compiliert wurde.

    Zumindest manuell hab ich versucht in dieses Consolen Fenster nach dem Format '| batch_command ' einen Befehl einzugeben und das Programm versucht es genau so auszuwerten.

  • Du musst das Skript auch über die Shell ausführen, das Piping wird nämlich von der Shell übernommen.

    In AutoIt kannst du dafür die _RunDOS-Funktion verwenden.

    Kompilierst du ein Programm welches in StdOut seine Parameter aus $CMDLine schreibt, und du

    _RunDOS("compiled.exe myparam1 > output.txt", dann wirst du den StdOut auch in die Datei pipen können, oder direkt in deinen Prozess, wie du halt möchtest.

    Dabei wird die compiled.exe nur myparam1 als Parameter bekommen und den Rest vollständig ignorieren.

  • So ganz kann ich noch nicht folgen.

    Würde doch bedeuten mein SubThread müsste per _RunDOS("compiled.exe myparam1 > output.txt"), den Prozess "erneut" starten?

    Dann hätte ich wieder das Problem, dass die Anwendung zu lange startet.

    Bisher mache ich es so.

    - Ich starte "Main.exe" (mein AutoIT Programm)

    - Main.exe startet im Hintergrund unsichtbar "pythonApp.exe"

    - Nach längerer Zeit will ein User über das AutoIt Programm Infos Abfragen

    - Main.exe startet einen SubThread >> Sub.exe (auch wenn es egt die gleiche exe ist, also praktisch "Main.exe", aber für die Theorie "Sub.exe")

    - Über den Prozess von Sub.exe würde ich gerne auf den bereits gestarteten aber unsichtbaren Prozess von "pythonApp.exe" zugreifen, ohne ihn neustarten zu müssen

    Würde ich stattdessen über meinen SubThread erneut "pythonApp.exe" starten wollen, müsste ich leider zu lange warten, da dieses Programm über 10 Sekunden braucht bis alles bereit ist.

    Oder rede ich an dir vorbei? :/:saint:^^

  • Ich weiß ja nicht was deine Subthreads machen aber du kannst doch in einem Subthread einfach die python-exe starten und die Ausgabe in eine Datei pipen.

    Dein Subthread kann diese Datei zu jeder Zeit auslesen und ist nicht an die Startdauer von der python-exe gebunden.

    Main.exe startet Subthread. Subthread startet Python.exe mit Piping in eine Datei, Subthread lest die Datei zu jeder Zeit aus.

    Die _RunDOS verwendet RunWait, du kannst aber auch stattdessen nur Run nutzen, dann wartest du auch nicht bis die Anwendung beendet ist.

    // Ich dachte deine Python-exe wäre ein Subthread, aber du scheinst das ja schon vorher zu starten.

    Wenn du die Python-exe weit im voraus startest ohne etwas zu Pipen wirst du wohl nicht so einfach an das StdOut kommen wenn du im nachhinein ran möchtest.

    Also lass es im voraus in eine Datei pipen und lies sie aus.

  • Ah ok. Hab verstanden was du meinst.

    Leider würde ich gerne umgehen wollen, die PythonApp.exe erneut starten zu müssen. ?(

    Wenn die bereits gestartet ist, gehen die Abfragen nahezu direkt (dauern dann etwas um die eine Sekunde).

    Bisher hab ich die Abfragen von meiner Main.exe selbst durchführen lassen (per StdinWrite/-Read).

    Mein Gedanke war, damit meine Main.exe durch die ganze Abfrage nicht blockiert wird (oder um 1-2 Sekunden zusätzlich verzögert wird), den ganzen Ablauf über einen SubThread zu handeln. X/

    Zudem möchte ich auch nicht über 10 Sekunden warten bis ich die Antwort/Info von der PythonApp.exe erhalte. ;(

    €dit: Die PythonApp.exe zu editieren kommt leider auch nicht in Frage, da hab ich keinen Einfluss drauf.

  • Dann versuche doch mal, die PID der PythonApp.exe als Umgebungsvariable zu speichern, wenn du sie mit der Main.exe startest, damit dein SubThread sie dann mit EnvGet auslesen kann. Wenn das nicht funktioniert, speichere sie in ein Lockfile, dass dein SubThread sie daraus auslesen kann.

  • borsTiHD

    Lagere doch einfach die Arbeit mit dieser ominösen PythonApp.exe in ein anderes Skript / eine andere Anwendung aus. Wenn sie noch nicht gestartet ist, startest du sie aus deinem Hauptskript heraus. Ansonsten stellst du eben an diese Anwendung all deine Anfragen, die du allgemein bearbeitet haben möchtest.

    Grüße autoiter

  • Dann versuche doch mal, die PID der PythonApp.exe als Umgebungsvariable zu speichern, wenn du sie mit der Main.exe startest, damit dein SubThread sie dann mit EnvGet auslesen kann. Wenn das nicht funktioniert, speichere sie in ein Lockfile, dass dein SubThread sie daraus auslesen kann.

    Danke für die Idee. Auch interessant sowas, das werde ich mir mal generell merken.

    Die PID übertrage ich an meinen SubThread bereits mit einer Nachricht (speicher ich in einer Ini-Datei zwischen).

    Nur wenn ich von meinem SubThread per StdinWrite/-Read auf die PythonApp zugreifen möchte, passiert nichts.

    Meiner Recherche nach funktionieren die Funktionen nur wenn die jeweilige Anwendung ein "Child Prozess" ist?! :/

    borsTiHD

    Lagere doch einfach die Arbeit mit dieser ominösen PythonApp.exe in ein anderes Skript / eine andere Anwendung aus. Wenn sie noch nicht gestartet ist, startest du sie aus deinem Hauptskript heraus. Ansonsten stellst du eben an diese Anwendung all deine Anfragen, die du allgemein bearbeitet haben möchtest.

    Auf die "PythonApp.exe" hab ich leider keinen Einfluss. Es holt sich auch Infos aus Quellen die mir unbekannt sind, kann das System also leider auch nicht nachbauen.

    Derzeit starte ich diese PythonApp über mein Hauptskript. Da ich immer wieder Infos daraus abfrage bleibt die im Hintergrund einfach offen. Würde ich die jetzt kurzfristig über einen weiteren Thread starten wollen, hab ich mein ursprüngliches Problem, dass diese PythonApp auf die ich angewiesen bin zu lange braucht bis sie einsatzbereit ist. Die Daten bekomme ich dann trotzdem, aber die Laufzeit ist leider untragbar. X/


    €dit: @autoiter Achso. Ich glaube jetzt versteh ich was du meinst.

    Ich starte direkt zu Beginn meines MainThreads einen SubThread und über diesen SubThread starte ich die PythonApp.

    Immer wenn ich jetzt eine Abfrage von der PythonApp brauche, nutze ich meinen SubThread... dieser hat ständig die PythonApp parat und kümmert sich um das Handling mit schreiben/lesen.

    Einerseits irgendwie etwas unschön, dass ich einen weiteren SubThread ständig laufen lassen muss, aber anderseits eine für mich akzeptable Lösung.

    Der SubThread dient dann nur als Kommunikationsbrücke zws. MainThread <> PythonApp.

    Danke für die Idee. ;):thumbup:

    2 Mal editiert, zuletzt von borsTiHD (2. November 2018 um 16:38)

  • Nur wenn ich von meinem SubThread per StdinWrite/-Read auf die PythonApp zugreifen möchte, passiert nichts.

    Wie startest du denn die PythonApp.exe mit der main.exe?

    Meiner Recherche nach funktionieren die Funktionen nur wenn die jeweilige Anwendung ein "Child Prozess" ist?!

    Du meinst mit den nativen AutoIt-Funktionen... ja, das stimmt, aber es gibt ja noch andere Wege (DllCall), um an die Ausgaben einer Console zu kommen. Das Problem sollte für dich aber ohne Bedeutung sein, da die PythonApp.exe und auch die SubTasks von deiner main.exe gestartet wurden und diese somit Child-Prozesse der main.exe sind.

  • Mein MainThread startet mit $iPythonPID = Run($sPythonAPIPath & "Main_work.exe", $sPythonAPIPath, "", $STDIN_CHILD + $STDERR_MERGED) die PythonApp.

    Später startet mein MainThread einen SubThread mit $iPID = Run($sMultiThreadFile & " --au-thread """ & $sCallback & """").

    - (dabei ist $sMultiThreadFile eine beim Start erstellte Kopie im lokalen "Temp-Verzeichnis" von Windows, da mein MainThread aus einem Netzwerklaufwerk gestartet wird - zur reduzierung der Last auf dem Netzwerklaufwerk und es ist auch schneller).

    Anschließend übergebe ich der PID meines SubThreads ($iPID) eine Nachricht mit der PID der PythonApp ($iPythonPID).

    Ab hier würde ich dann gerne vom SubThread aus versuchen die PythonApp zu steuern.

    Meine PythonApp und auch der SubThread sind meiner Kenntnis nach jeweils ein Child Prozess meines MainThreads, wie du selbst sagst.

  • Mein MainThread startet mit $iPythonPID = Run($sPythonAPIPath & "Main_work.exe", $sPythonAPIPath, "", $STDIN_CHILD + $STDERR_MERGED) die PythonApp.

    Du gibst hier als showflag "" an... auch wenn es funktioniert, ich würde es ändern in @SW_HIDE oder Default.

    Später startet mein MainThread einen SubThread mit $iPID = Run($sMultiThreadFile & " --au-thread """ & $sCallback & """").

    Hier fehlt das opt_flag... und die Anführungszeichen... scheinen mir einige überflüssig zu sein.

    Versuche es mal so...

    $iPythonPID = Run($sPythonAPIPath & "Main_work.exe", $sPythonAPIPath, @SW_HIDE, BitOr($STDIN_CHILD, $STDERR_MERGED) )

    $iPID = Run($sMultiThreadFile & ' --au-thread "' & $sCallback & '"', '', @SW_HIDE, BitOr($STDIN_CHILD, $STDERR_MERGED))

    oder so...

    $iPID = Run(StringFormat('"%s" --au-thread "%s"', $sMultiThreadFile, $sCallback), '', @SW_HIDE, BitOr($STDIN_CHILD, $STDERR_MERGED))

    ...oder du nimmst für den SubThread als opt_flg $STDIO_INHERIT_PARENT, wenn der MainThread als Konsolenanwendung (CUI) kompiliert wurde.

  • Hm... werde ich mal probieren.

    Leider wird $STDIO_INHERIT_PARENT nicht funktionieren. Mein MainThread ist eine GUI.

    Die GUI wird über einen HotKey initialisert und fragt dann automatisch gewisse APIs direkt ab (jedesmal wenn der Hotkey genutzt wird).

    Dazu kann der User über manche Buttons auch Funktionen ausführen, die ich auf einen SubThread auslagere.

    Die SubThreads laufen meistens im hintergrund (bzw. genauer gesagt erscheint einfach keine GUI) und fragen gewisse APIs ab, damit ich in meinem MainThread/GUI nicht auf deren Antwort hätte warten müssen und damit ich mehrere gleichzeitige Abfragen laufen lassen kann.

    Allerdings nutze ich auch SubThreads die mit einer eigenen GUI starten, damit ich diese GUI unäbhängig von meinem MainThread/GUI bedienen kann.

    Darum hab ich mir bisher nie weitere Gedanken bzgl des "Showflags" gemacht, aber ich kann dann bestimmt auch @SW_SHOW nehmen, wenn es dafür mit Flag sauberer ist?

    Bzgl $STDIN_CHILD, $STDERR_MERGED beim SubThread, könnte das vlt helfen das ein SubThread befehle an die PythonApp senden kann?

    Ich kann es frühstens morgen testen.

    Vielen Dank an alle schon mal. :)

  • Darum hab ich mir bisher nie weitere Gedanken bzgl des "Showflags" gemacht, aber ich kann dann bestimmt auch @SW_SHOW nehmen, wenn es dafür mit Flag sauberer ist?

    Ja, wenn du die Console sehen willst, ansonsten eben @SW_HIDE oder Default.

    Bzgl $STDIN_CHILD, $STDERR_MERGED beim SubThread, könnte das vlt helfen das ein SubThread befehle an die PythonApp senden kann?

    Ich denke mal nein, denn das geht wohl nur, wenn die PythonApp von diesem SubThread gestartet wurde und somit dessen Child ist. $STDIN_CHILD, $STDERR_MERGED beim SubThread sogt nur dafür, dass der MainThread darauf zugreifen kann.

    Du könntest aber die Console der PythonApp mit _WinAPI_AttachConsole() an die des SubThread anfügen... dann sollte es mit StdinWrite() oder _WinAPI_WriteConsole() gehen.

    Schau dir mal die Console.au3 im Anhang an... das hilft dir bestimmt weiter.

  • Oh cool, danke dafür. ^^

    Werde mir das mal genauer anschauen.

    Nutzt du die "Console.au3" selbst? Das sieht so aus als wäre die auf eigene Bedürfnisse zugeschnitten.

    Finde ich jedenfalls sehr interessant... wenn ich mehr Zeit zum experimentieren finde, werde ich die Lösung mal für mein Problem probieren.

    Die Calls meiner SubThreads werde ich wohl auch nach deinem Vorbild aufrufen, das macht es vorallem übersichtlicher.

    Auch vielen Dank hierfür. :)

    -----------------------------------------------------

    Für mein ursprüngliches Problem, habe ich mich erstmal dazu entschieden es nach der Idee von autoiter umzusetzen.

    Ich starte direkt wenn mein Programm startet parallel einen SubThread, der ewig mitlaufen wird.

    Dieser SubThread selbst startet dann meine PythonApp und dient von nun an als eine Art "Brücke" zws. meinem MainThread und der PythonApp.

    ( MainThread <> SubThread <> PythonApp )

    Wenn ich jetzt eine Abfrage aus der PythonApp brauche, geht es wie folgt:

    • MainThread startet eine bestimmte Funktion (zb. per Button)
    • Die Funktion sendet eine Nachricht an den SubThread
    • SubThread erhält die Nachricht und geht die Abfrage mit der PythonApp durch und schickt die Antwort an den MainThread zurück
    • Erkennt der MainThread diese Antwort, so wird sie an eine bestimmte Funktion geschickt, die ich als Handler nutze (hab mich dazu entschieden, damit ich meinen generellen Thread Handler nicht umschreiben muss)
    • Dieser Handler wiederum kümmert sich nur darum, welche ursprüngliche Funktion die Abfrage losschickte und ruft diese Funktion mit zusätzlichen Parametern auf
    • Die ursprüngliche Funktion erkennt, dank der Parameter, dass es sich um eine Antwort der zu Beginn erstellten Anfrage handelt und wertet diese entsprechend aus (Label werden bsp. mit der Antwort gesetzt, etc.)

    Das schöne daran ist, der "Handler" entscheidet bzw. weiß wer die Antwort wollte, dadurch kann ich von verschiedenen Funktionen aus Anfragen an die PythonApp schicken, ohne jedesmal meinen MainThread/GUI sperren zu lassen, bzw auf die Antwort warten zu müssen. Sobald die Antwort fertig ist, wird diese quasi direkt ausgewertet.

    Vielen vielen Dank an alpines , Bitnugger und autoiter für die großartige Hilfe (wie immer ;)).

    Vorallem lernt man durch euch auch immer eine Menge und betrachtet Problemstellungen aus ganz anderen Perspektiven.

    Liebe Grüße

    borsTiHD

  • Nutzt du die "Console.au3" selbst? Das sieht so aus als wäre die auf eigene Bedürfnisse zugeschnitten.

    Nein, aber die war sehr hilfreich als erste Inspiration bzw. Wegweiser, als ich mich mit dem Thema Console befasst hatte, denn auch ich wollte wissen, ob sich fremde Consolen steuern lassen - und ja, es geht.

    Lediglich Zeile 15, 16 (haben da nichts zu suchen und sind nur eine vergessene Notiz zu einem Ticket im blauen Forum) und der Kommentarblock am Ende mit den Funktionsnamen stammen von mir.

    Übrigens: Nicht nur die Antworten bringen Erleuchtung, manchmal sind es auch die Fragen!