Child Prozesse andocken?

  • Moin,

    und zwar möchte ich mit der Hauptdatei (Parent Prozess) mehrere Child Prozesse starten (multi processing). Jedoch haben die Prozesse kein GUI.

    Ich wollte eigentlich _WinAPI_SetParent() benutzen, aber dafür benötigt man ja GUI's. Und ein "unsichtbares" GUI erstellen ist auch nicht so das Wahre.

    Mit dem AutoIt Gui (AutoItWinGetTitle) funktioniert das auch nicht so, wie ich mir das vorgestellt habe. Die Fenster kann man nicht zu 100% sicher identifizieren.

    Wenn ich nun den Taskmanager öffne (win10), sind die Prozesse wild in der Prozessliste verteilt. Es wäre aber viel besser, wenn die Child Prozesse am Parent Prozess

    andocken würden. :huh:

    Gibt es da eine Möglichkeit? Ich habe mir die WinAPIProc.au3 mal angeschaut, aber nichts gefunden. :rolleyes:

    So wie hier beim Windows Explorer: (Bild von Google)
    [Blockierte Grafik: https://www.windowscentral.com/sites/wpcentral.com/files/styles/large/public/field/image/2017/01/task-manager-processes-part.jpg?itok=EcrjtvVD]


    Viele Grüße,

    xSunLighTx3

    2 Mal editiert, zuletzt von xSunLighTx3 (11. Mai 2018 um 17:48)

  • Kommen diese "Child-Prozesse" nicht eigentlich nur zustande, wenn Multithreading betrieben wird? - Bei AutoIt dürfte das glaube ich so einfach nicht funktionieren, wenn es wirklich nur daran geht die Prozesse im Taskmanager untereinander anzuzeigen.

  • Kann sein, ich habe Multithreading noch gar nicht so genutzt. Beim ISN Studio ist mir auch aufgefallen, dass der externe Helper Prozess an dem Hauptprozess "gebunden" ist.

    Der Source des Projektes ist zwar öffentlich, aber ich habe es nach langem Ausprobieren nicht geschafft, da durchzusteigen. :(

    Naja, vielleicht probiere ich überhaupt erstmal, inwiefern ich ein simples Multi Processing mit AutoIt hinkriege. Ich habe auch vor, das Ergebnis zu veröffentlichen.

    Für bestimmte Anwendungszwecke ist das ja eigentlich ganz sinnvoll. :)

    Auf jeden Fall vielen Dank für deine Antwort!

  • Multiprocessing ist super easy. Einfach childs als Konsolenversion compilieren und in der Hauptdatei dann nutzen (fileinstall() in @temp und von dort starten, nachher artig aufräumen).

  • Multiprocessing ist super easy. Einfach childs als Konsolenversion compilieren und in der Hauptdatei dann nutzen (fileinstall() in @temp und von dort starten, nachher artig aufräumen).

    Wow, das klappt ja perfekt. Danke dir, Zec!

    Dann werde ich mich mal die nächsten Tage daransetzen. :)

  • Ja, ist der Hammer, nutze das inzwischen wenn immer möglich. Also z.B. alles, was etwas läuft und man nicht unbedingt zeitkritisch eine Erfolg/Misserfolgmeldung braucht. Ist zwar kein Mutlithreading, nutzt aber wenigstens mehrere Kerne / Hyperthreading.

  • Bei AutoIt gibt es für was alles ein workaround, aber dennoch hat man viele Einschränkungen. Multithreading wäre eine sehr wirkungsvolle Funktion gewesen.


    Aber mal sehen, inwiefern man mit mehreren Prozessen wirkungsvoll arbeiten kann. :)

    Ein Problem, welches ich jetzt schon erahne, ist das Einfrieren von Child Prozessen.

    Es kann ja auch vorkommen, dass andere Anwendungen den Prozessor auslasten, sodass

    einige Prozesse dann gar nicht mehr anständig verabreitet werden und somit das Hauptskript keine

    zeitgemäße Antwort mehr vom Child Prozess bekommt. ;(

    Das Austauschen von Informationen zwischen den Prozessen ist auch wieder so eine Sache, die nicht

    zwingend reibungslos und zuverlässig ablaufen muss.

  • Ja, ist richtig. Zum child ist problemlos über mehrere Wege möglich, aber zurück und einigermaßen schnell... Naja. AutoIt ist nunmal eine Skriptsprache. :S

  • Hallo xSunLighTx3

    Das Austauschen von Informationen zwischen den Prozessen ist auch wieder so eine Sache, die nicht

    zwingend reibungslos und zuverlässig ablaufen muss.

    Wenn du die GUI benutzbar halten willst, bieten sich die _Timer_*** - Funktionen an. (Also nicht mit RunWait oder einer Schleife auf Statusmeldungen warten, sondern mit einer über Timer gestarteten Funktion abfragen). So kannst du dein Fenster jederzeit bedienbar erhalten (und gut Multithreading simulieren).

    Grüße autoiter

  • Ja, ist richtig. Zum child ist problemlos über mehrere Wege möglich, aber zurück und einigermaßen schnell... Naja. AutoIt ist nunmal eine Skriptsprache. :S

    Ich werde auf jeden Fall einige Sachen ausprobieren und dann mein bestes Ergebnis posten. Aber ich bin nicht der beste, das muss ich noch anmerken. :D

    Hallo xSunLighTx3

    Wenn du die GUI benutzbar halten willst, bieten sich die _Timer_*** - Funktionen an. (Also nicht mit RunWait oder einer Schleife auf Statusmeldungen warten, sondern mit einer über Timer gestarteten Funktion abfragen). So kannst du dein Fenster jederzeit bedienbar erhalten (und gut Multithreading simulieren).

    Stimmt, mit Timern habe ich auch schon viel gearbeitet. Ich hatte leider öfters Probleme bei schwächeren PC's. Wenn du große Schleifen hast, die viel Zeit benötigen und

    zwischendurch immer die Timer ins Spiel kommen, kann sich das Skript schnell mal selbst aufhängen. Und die Timer schieben immer neue Aufgabe in die Que. :(

    Ich habe mir schon ein paar Abläufe überlegt und werde mal schauen, was am besten funktionieren wird. 8)

  • Oh, wir reden hier aneinander vorbei.

    Ich ging von deinem genannten Szenario mit weiteren gestarteten Prozessen aus und ich meine auch nicht AdlibRegister.

    Ich habe mir schon ein paar Abläufe überlegt und werde mal schauen, was am besten funktionieren wird.

    Gut. Ich bin der Meinung, wenn du dir vor der Umsetzung Gedanken machst, wie du eine Oberfläche immer bedienbar hältst, dann kriegst du das auch mit AutoIt hin.

    Grüße autoiter

  • Ich arbeite gerade an einem ähnlichen Problem (will div. berechnungen in CHild Prozessen ausführen lassen, damit ich mehrere Prozessorkerne verwenden kann).

    Habe mirüberlegt das mit Named Pipes zu machen, funktioniert das so oder sehe ich das falsch?

    Computers are like Airconditioning. They don´t work with open Windows.

  • Jaja, gerade bei Multiprocessing ist es sau schade, dass Windows die Funktionen exec und fork zusammenfasst... Ein einfaches fork wäre hier schon cool!

    Kommunikation geht auch sehr gut über TCP. Das hat den Vorteil, dass das Programm mit wenig Änderungen auch auf mehreren Rechnern (Stichwort "Verteilte Systeme") laufen kann.

  • Ich habe mir heute mal die SendMessage Funktion angeschaut und muss sagen, dass diese recht schnell ist. :)

    Das hätte ich nicht gedacht, ich werde mir das aber mal genauer anschauen.

  • Bin ich auch gerade bei. Hier eine UDF und ein Beispiel für hin und her. Ich weiß nicht mehr, wo ich sie gefunden habe, Ist jedenfalls nicht von mir. :saint:

    UDF

    #include-once

    Global Const $MHVersionInformation = "V1.04"

    Global $MHSendDataToFunc = " _DefaultMsgFunc"

    Global $MHhwmd_Receiver

    Global $MHAdditionalIdentifier = "_nffgmnhmzsjttsnhfgnfgh";just to make the window titles a little bit more unique

    ; Windows Definitions;

    Global Const $StructDef_COPYDATA = "dword none;dword count;ptr pointer"

    Global Const $WM_COPYDATA_MH = 0x4A

    ;Message Queue Setup

    Global $MHCallBackTimer = 200

    Global $pTimerProc, $uiTimer

    Global $aMessageQueue[1]=[0]

    Opt("OnExitFunc", "CallBack_Exit")

    ; #FUNCTION# ====================================================================================================================

    ; Name...........: _MHVersion

    ; Description ...: Gets Message Handler Version information

    ; Syntax.........: _MHVersion()

    ; Parameters ....: None

    ; Return values .: Message Handler Version Number

    ; Author ........: ChrisL

    ; ===============================================================================================================================

    Func _MHVersion()

    Return $MHVersionInformation

    EndFunc


    ; #FUNCTION# ====================================================================================================================

    ; Name...........: _SetCallBackTimerInterval

    ; Description ...: Sets the script up to accept messages

    ; Syntax.........: _SetCallBackTimerInterval(nnn)

    ; Parameters ....: $iTime - Callback timer in milliseconds to process each message in the queue

    ; Return values .: Success - The new callback timer

    ; Failure - @error is set and the previous callback timer setting is used. Default is 200ms

    ; Information ...: The timer must be changed before _SetAsReceiver() is called

    ; Author ........: ChrisL

    ; ===============================================================================================================================

    Func _SetCallBackTimerInterval($iTime = 200)

    If Not IsInt($iTime) then Return SetError(1,0,$MHCallBackTimer) ;not an integer so set error and return current setting

    If $MHhwmd_Receiver <> "" then Return SetError(1,0,$MHCallBackTimer) ;the receiver function is already set so the timer can not be changed

    If $iTime = 0 then Return $MHCallBackTimer ;Return existing value

    If $iTime < 50 then $iTime = 50

    $MHCallBackTimer = $iTime

    Return SetError(0,0,$iTime)

    EndFunc


    ; #FUNCTION# ====================================================================================================================

    ; Name...........: _CALLBACKQUEUE

    ; Description ...: Process any messages that are queued and adjust the message queue

    ; Syntax.........: None

    ; Parameters ....: None

    ; Return values .: None

    ; Author ........: ChrisL

    ; ===============================================================================================================================

    Func _CALLBACKQUEUE()

    Local $vMessage

    Local $queueLen = $aMessageQueue[0]

    If $queueLen > 0 then

    $vMessage = $aMessageQueue[1]

    $aMessageQueue[1] = ""

    For $i = 1 to $queueLen -1

    $aMessageQueue[$i] = $aMessageQueue[$i +1]

    Next

    Redim $aMessageQueue[$queueLen]

    $aMessageQueue[0] = $queueLen - 1

    Call($MHSendDataToFunc,$vMessage)

    EndIf

    EndFunc


    ; #FUNCTION# ====================================================================================================================

    ; Name...........: _QueueMessage

    ; Description ...: Queues messages to prevent script slowdown

    ; Syntax.........: _QueueMessage($vText)

    ; Parameters ....: $vText - The text to queue from the remote script

    ; Return values .: None

    ; Author ........: ChrisL

    ; ===============================================================================================================================

    Func _QueueMessage($vText)

    Redim $aMessageQueue[$aMessageQueue[0] +2]

    $aMessageQueue[$aMessageQueue[0] +1] = $vText

    $aMessageQueue[0]+=1

    EndFunc


    ; #FUNCTION# ====================================================================================================================

    ; Name...........: _SetAsReceiver

    ; Description ...: Sets the script up to accept messages

    ; Syntax.........: _SetAsReceiver($vTitle)

    ; Parameters ....: $vTitle - The Local_ReceiverID_Name

    ; Return values .: Success - Handle to the receiver window

    ; Failure - @error is set and the relevant message is displayed

    ; Author ........: ChrisL

    ; ===============================================================================================================================

    Func _SetAsReceiver($vTitle)


    If StringLen($vTitle) = 0 then

    Msgbox(16 + 262144,"Message Handler Error","A Local_ReceiverID_Name must be specified." & @crlf & _

    "Messages will not be received unless a unique Local_ReceiverID_Name is used!")

    Return SetError(1,1,-1);Make sure the user has specified a title

    EndIf

    $vTitle &= $MHAdditionalIdentifier;add on our additionalIdentifier which is unlikely to be used exept by scripts using this UDF

    If WInExists($vtitle) and WinGetHandle($vTitle) <> $MHhwmd_Receiver then ;already a window exists with this title and it's not ours highly unlikely unless 2 copies of the script are running

    Msgbox(16 + 262144,"Message Handler Error","The Local_ReceiverID_Name " & StringTrimRight($vTitle,StringLen($MHAdditionalIdentifier)) & " already exists." & @crlf & _

    "A unique Local_ReceiverID_Name must be specified." & @crlf & _

    "Messages will not be received unless a unique Local_ReceiverID_Name is used!")

    Return SetError(1,2,-1)

    EndIf

    $MHhwmd_Receiver = GUICreate($vTitle)

    GUIRegisterMsg($WM_COPYDATA_MH, "_GUIRegisterMsgProc")

    $pTimerProc = DllCallbackRegister("_CALLBACKQUEUE", "none", "")

    $uiTimer = DllCall("user32.dll", "uint", "SetTimer", "hwnd", 0, "uint", 0, "int", $MHCallBackTimer, "ptr", DllCallbackGetPtr($pTimerProc))

    $uiTimer = $uiTimer[0]

    Return $MHhwmd_Receiver

    EndFunc


    ; #FUNCTION# ====================================================================================================================

    ; Name...........: _SetReceiverFunction

    ; Description ...: Sets the function to call on receiving data

    ; Syntax.........: _SetReceiverFunction($vString)

    ; Parameters ....: $vString - The string of data to send

    ; Return values .: Success - The users function name to call

    ; Failure - @error is set and "" is returned

    ; Author ........: ChrisL

    ; ===============================================================================================================================

    Func _SetReceiverFunction($vString)

    If $vString = "" then return SetError(1,1,$vString)

    $MHSendDataToFunc = $vString

    Return SetError(0,0,$MHSendDataToFunc)

    EndFunc


    ; #FUNCTION# ====================================================================================================================

    ; Name...........: _SendData

    ; Description ...: Sends data to the registered window

    ; Syntax.........: _SendData($vData,$ReceiverTitle)

    ; Parameters ....: $vData - The string of data to send

    ; $ReceiverTitle - Remote_ReceiverID_Name specified in the script you wish to communicate to

    ; Return values .: Success - The count of the string length sent

    ; Failure - @error is set and 0 is returned

    ; Author ........: martin, piccaso and ChrisL

    ; ===============================================================================================================================

    Func _SendData($vData,$ReceiverTitle)

    Local $strLen,$CDString,$vs_cds,$pCDString,$pStruct,$hwndRec

    If StringLen($ReceiverTitle) = 0 then Return SetError(1,1,0);Make sure the user has specified a title

    $ReceiverTitle&= $MHAdditionalIdentifier

    $strLen = StringLen($vData)

    $CDString = DllStructCreate("char var1[" & $strLen +1 & "]");the array to hold the string we are sending

    DllStructSetData($CDString,1,$vData)

    $pCDString = DllStructGetPtr($CDString);the pointer to the string

    $vs_cds = DllStructCreate($StructDef_COPYDATA);create the message struct

    DllStructSetData($vs_cds,"count",$strLen + 1);tell the receiver the length of the string +1

    DllStructSetData($vs_cds,"pointer",$pCDString);the pointer to the string

    $pStruct = DllStructGetPtr($vs_cds)

    $hwndRec = WinGetHandle($ReceiverTitle)

    If $hwndRec = "" then

    $vs_cds = 0;free the struct

    $CDString = 0;free the struct

    Return SetError(1,2,0)

    EndIf


    DllCall("user32.dll", "lparam", "SendMessage", "hwnd", $hwndRec, "int", $WM_COPYDATA_MH, "wparam", 0, "lparam", $pStruct)

    If @error then

    $vs_cds = 0;free the struct

    $CDString = 0;free the struct

    return SetError(1, 3, 0) ;return 0 no data sent

    EndIf


    $vs_cds = 0;free the struct

    $CDString = 0;free the struct

    Return $strLen

    EndFunc


    ; #FUNCTION# ====================================================================================================================

    ; Name...........: _GUIRegisterMsgProc

    ; Description ...: Called when a messae is sent to the registered window

    ; Syntax.........: _GUIRegisterMsgProc($hWnd, $MsgID, $wParam, $lParam)

    ; Parameters ....: $hWnd - Window/control handle

    ; $iMsg - Message ID received

    ; $wParam - Could specify additional message-specific information

    ; $lParam - Specifies a pointer to the message

    ; Return values .: None - Calls user specified function

    ; Author ........: piccaso

    ; Modified.......: ChrisL and martin

    ; ===============================================================================================================================

    Func _GUIRegisterMsgProc($hWnd, $MsgID, $WParam, $LParam)

    Local $vs_cds,$vs_msg

    If $MsgID = $WM_COPYDATA_MH Then ; We Recived a WM_COPYDATA Message

    ; $LParam = Poiter to a COPYDATA Struct

    $vs_cds = DllStructCreate($StructDef_COPYDATA, $LParam)

    ; Member No. 3 of COPYDATA Struct (PVOID lpData;) = Pointer to Custom Struct

    $vs_msg = DllStructCreate("char[" & DllStructGetData($vs_cds, "count") & "]", DllStructGetData($vs_cds, "pointer"))

    ; Call the function to queue the received data

    _QueueMessage(DllStructGetData($vs_msg, 1))

    $vs_cds = 0

    $vs_msg = 0

    EndIf

    EndFunc ;==>_GUIRegisterMsgProc


    ; #FUNCTION# ====================================================================================================================

    ; Name...........: _DefaultMsgFunc

    ; Description ...: If no user function is specified this function i used to receive data

    ; Syntax.........: _DefaultMsgFunc($vText)

    ; Parameters ....: $vText - The data sent be the other script

    ; Return values .: None

    ; Author ........: ChrisL

    ; ===============================================================================================================================

    Func _DefaultMsgFunc($vText)

    Msgbox(0," _DefaultMsgFunc",$vText)

    EndFunc ;==>_DefaultMsgFunc


    ;Release the CallBack resources

    Func CallBack_Exit()

    If $MHhwmd_Receiver <> "" then

    DllCallbackFree($pTimerProc)

    DllCall("user32.dll", "int", "KillTimer", "hwnd", 0, "uint", $uiTimer)

    EndIf

    EndFunc

    Beispiel App 1

    ;Script1

    #include "MessageHandler.au3"

    $Local_ReceiverID_Name = "Script1sReceiverID";This is the ID that the other script will use to send data

    $Remote_ReceiverID_Name = "Script2sReceiverID";This is the ID of the script we want to send data too

    $hwnd = _SetAsReceiver($Local_ReceiverID_Name)

    ConsoleWrite("hwnd of the Local_ReceiverID_Name is " & $hwnd & @crlf)

    $myFunc = _SetReceiverFunction("_MyFunc2")

    ConsoleWrite("My data receiver function is " & $myFunc & @crlf)


    While 1

    Sleep(1000)

    WEnd

    Func _MyFunc2($vText)

    Msgbox(0,@ScriptName,"I am " & @ScriptName & " I have received some data" & @crlf & @crlf & $vText & @crlf & @crlf & "And now I'm sending the data back")

    $iSent = _SendData($vText,$Remote_ReceiverID_Name)

    Exit

    EndFunc

    Beispiel App 2

    ;Script2

    #include "MessageHandler.au3"

    $Local_ReceiverID_Name = "Script2sReceiverID";This is the ID that the other script will use to send data

    $Remote_ReceiverID_Name = "Script1sReceiverID";This is the ID of the script we want to send data too

    $hwnd = _SetAsReceiver($Local_ReceiverID_Name)

    ConsoleWrite("hwnd of the Local_ReceiverID_Name is " & $hwnd & @crlf)

    $myFunc = _SetReceiverFunction("_MyFunc2")

    ConsoleWrite("My data receiver function is " & $myFunc & @crlf)


    $Str = InputBox(@ScriptName," I am " & @ScriptName & @crlf & "Enter some data to be sent to the other script")

    $iSent = _SendData($Str,$Remote_ReceiverID_Name)

    While 1

    sleep(100)

    WEnd


    Func _MyFunc2($vText)

    Msgbox(0,@ScriptName,@ScriptName & " has received a message" & @crlf & $vText)

    Exit

    EndFunc

    Zum Testen am besten beide Apps compilieren.

  • Moin Zec,

    die UDF ist eigentlich ganz nice.

    Ich habe sie auch mal im autoitscript Forum gesehen, wenn mich nicht alles täuscht. :D

    So ähnlich wollte ich das auch gestalten. Vielleicht kann man ja auf der UDF aufbauen.

    Danke nochmals fürs Posten. :)