Interprozesskommunikation

    • Offizieller Beitrag

    Ich habe hier mal ein Beispielscript geschrieben, das eine Kommunikation zwischen zwei Prozessen ermöglicht.
    Dazu wird eine TCP-Verbindung über den Localhost (IP: 127.0.0.1) aufgebaut. Das Script ist mit zahlreichen Kommentaren versehen, sodass es hoffentlich verständlich ist.

    Edit: Ich habe das Script noch etwas abgeändert. Es wird jetzt für den Server eine eigene GUI benutzt, sodass man besser sehen kann, wie man das Beispiel in das eigene Script einbauen kann.
    Es ist zum testen auch nicht mehr nötig das Script vorher zu compilieren. Es wird automatisch doppelt gestartet.

    Spoiler anzeigen
    [autoit]


    #include <GUIConstantsEx.au3>
    #include <Misc.au3>

    [/autoit] [autoit][/autoit] [autoit]

    Global $sTitle = 'Interprozesskommunikation' ; Programmname (für _Singleton)
    Global $sIPAddress = '127.0.0.1' ; Localhost-IP-Adresse
    Global $iPort = 55555 ; beliebige Portnummer (sollte im Bereich zwischen 49152 und 65535 liegen)
    Global $hGui, $ID_Input, $MainSocket, $ConnectedSocket = -1, $sData, $sRecv, $iPID

    [/autoit] [autoit][/autoit] [autoit]

    TCPStartup()
    If _Singleton($sTitle, 1) = 0 Then ; wenn Server bereits gestartet, dann hier als Client weitermachen
    $ConnectedSocket = TCPConnect($sIPAddress, $iPort) ; Verbindung zum Server herstellen
    If @error Then ; wenn nicht erfolgreich, dann Client beenden mit Fehlermeldung
    MsgBox(16, 'FEHLER!', 'TCPConnect fehlgeschlagen! Fehlernr.: ' & @error)
    Else ; TCP-Verbindung war erfolgreich
    Do ; Schleife mit InputBox solange aufrufen, bis der Benutzer auf 'Abbrechen' klickt oder ein Fehler beim senden auftritt
    $sData = InputBox($sTitle & ' - Client', 'Daten eingeben die an den Server gesendet werden sollen:', '', '', 420, 140)
    If @error Then ExitLoop ; Benutzer hat auf 'Abbrechen' geklickt, also Schleife verlassen
    $sData = StringToBinary($sData, 4) ; Daten umwandeln
    TCPSend($ConnectedSocket, $sData) ; Daten an den Server senden
    Until @error ; mit der Schleife fortfahren, wenn beim senden kein Fehler aufgetreten ist
    TCPCloseSocket($ConnectedSocket) ; Verbindung zum Server trennen
    EndIf
    Else ; Server läuft noch nicht, also Server starten
    $hGui = GUICreate($sTitle & ' - Server', 360, 480)
    $ID_Input = GUICtrlCreateEdit('', 10, 10, 340, 460)
    GUISetState(@SW_SHOW, $hGui)
    $MainSocket = TCPListen($sIPAddress, $iPort)
    If $MainSocket = -1 Then Exit MsgBox(0, $sTitle, 'Server konnte nicht gestartet werden!')
    GUICtrlSetData($ID_Input, '> Server gestartet!' & @CRLF, 1)
    GUICtrlSetData($ID_Input, '> Starte Client...' & @CRLF, 1)
    Run(@AutoItExe & ' "' & @ScriptFullPath & '"') ; starte das Script nochmal (als Client)
    If @error Then Exit MsgBox(0, $sTitle, 'Client konnte nicht gestartet werden.')
    GUICtrlSetData($ID_Input, '> Client gestartet.' & @CRLF, 1)
    Do ; in dieser Schleife wartet der Server auf Daten und es wird GUIGetMsg abgefragt
    If $ConnectedSocket = -1 Then ; wenn noch keine Verbindung hergestellt wurde, dann..
    $ConnectedSocket = TCPAccept($MainSocket) ; abfragen, ob sich der Client verbunden hat
    If $ConnectedSocket <> -1 Then ; wenn Verbindung hergestellt, dann Meldung ausgeben
    GUICtrlSetData($ID_Input, '> Verbindung hergestellt.' & @CRLF, 1)
    GUICtrlSetData($ID_Input, '> Warte auf Daten...' & @CRLF, 1)
    EndIf
    Else ; Verbindung ist hergestellt
    $sRecv = TCPRecv($ConnectedSocket, 2048) ; Daten abholen
    If @error Then ; wenn dabei ein Fehler aufgetreten ist, dann ist die Verbindung unterbrochen (Client beendet)
    $ConnectedSocket = -1
    GUICtrlSetData($ID_Input, '> Verbindung unterbrochen.' & @CRLF, 1)
    GUICtrlSetData($ID_Input, '> Warte auf Verbindung...' & @CRLF, 1)
    Else ; Daten wurden korrekt empfangen
    $sRecv = BinaryToString($sRecv, 4) ; Daten umwandeln
    If $sRecv <> '' Then GUICtrlSetData($ID_Input, $sRecv & @CRLF, 1) ; und im Editfeld ausgeben
    EndIf
    EndIf
    Until GUIGetMsg() = $GUI_EVENT_CLOSE
    GUICtrlSetData($ID_Input, '> Verbindung getrennt.' & @CRLF, 1)
    GUICtrlSetData($ID_Input, '> Server wird beendet.' & @CRLF, 1)
    TCPCloseSocket($ConnectedSocket)
    Sleep(1000)
    EndIf
    TCPShutdown()
    Exit

    [/autoit]
    • Offizieller Beitrag

    Schönes Tutorial, Oscar.^^
    Hast du vor, auch noch andere Wege der Interprozesskommunikation zu erklären?
    Also... z.B. Exchange Variables, Std-Stream... ^^

    Danke für das Lob!
    Aber die anderen Wege überlasse ich besser denen, die sich damit auskennen. Ich hab's schonmal mit NamedPipes probiert, aber da bekomme ich kein funktionierendes Beispiel hin.
    Alle Scripte, die ich dazu gefunden habe, sind auch mehr oder weniger fehlerbehaftet. Vielleicht fühlt sich ja jemand berufen, mal ein einfaches Beispiel zu schreiben?! :)

  • So hier mal das ganze mit NamedPipes.

    Spoiler anzeigen
    [autoit]

    #include <GUIConstantsEx.au3>
    #include <Misc.au3>
    #include <NamedPipes.au3>
    #include <WinAPI.au3>

    [/autoit] [autoit][/autoit] [autoit]

    Global $sTitle = 'Interprozesskommunikation' ; Programmname (für _Singleton)
    Global $hGui, $ID_Input, $sData, $sRecv, $iPID
    Global $sPipeName = "\\.\pipe\virtual01"
    Global $BUFFER_SIZE = 4096
    Global $hPipe, $bData, $tDataBuffer, $iRead

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    If _Singleton($sTitle, 1) = 0 Then ; wenn Server bereits gestartet, dann hier als Client weitermachen
    Do ; Schleife mit InputBox solange aufrufen, bis der Benutzer auf 'Abbrechen' klickt oder ein Fehler beim senden auftritt
    $sData = InputBox($sTitle & ' - Client', 'Daten eingeben die an den Server gesendet werden sollen:', '', '', 420, 140)
    If @error Then ExitLoop ; Benutzer hat auf 'Abbrechen' geklickt, also Schleife verlassen
    $bData = StringToBinary($sData, 4)
    $tDataBuffer = DllStructCreate("byte[" & BinaryLen($bData) & "]")
    DllStructSetData($tDataBuffer, 1, $bData)
    _NamedPipes_CallNamedPipe($sPipeName, DllStructGetPtr($tDataBuffer), BinaryLen($bData), 0, 0, $iRead)
    Until @error ; mit der Schleife fortfahren, wenn beim senden kein Fehler aufgetreten ist
    Else ; Server läuft noch nicht, also Server starten
    $hGui = GUICreate($sTitle & ' - Server', 360, 480)
    $ID_Input = GUICtrlCreateEdit('', 10, 10, 340, 460)
    GUISetState(@SW_SHOW, $hGui)

    [/autoit] [autoit][/autoit] [autoit]

    $hPipe = _NamedPipes_CreateNamedPipe( _
    $sPipeName, _ ; Pipe name
    2, _ ; The pipe is bi-directional
    2, _ ; Overlapped mode is enabled
    0, _ ; No security ACL flags
    1, _ ; Data is written to the pipe as a stream of messages
    1, _ ; Data is read from the pipe as a stream of messages
    0, _ ; Blocking mode is enabled
    1, _ ; Maximum number of instances
    $BUFFER_SIZE, _ ; Output buffer size
    $BUFFER_SIZE, _ ; Input buffer size
    0, _ ; Client time out
    0) ; Default security attributes

    [/autoit] [autoit][/autoit] [autoit]

    If $hPipe = -1 Then
    MsgBox(48 + 262144, "NamedPipe", "Pipe konnte nicht erstellt werden!" & @CRLF & "Wahrscheinlich ist die Adresse schon vergeben.")
    Exit
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    Global $OVERLAPPED_Struct = DllStructCreate($tagOVERLAPPED)

    [/autoit] [autoit][/autoit] [autoit]

    GUICtrlSetData($ID_Input, '> Server gestartet!' & @CRLF, 1)
    GUICtrlSetData($ID_Input, '> Starte Client...' & @CRLF, 1)
    Run(@AutoItExe & ' "' & @ScriptFullPath & '"') ; starte das Script nochmal (als Client)
    If @error Then Exit MsgBox(0, $sTitle, 'Client konnte nicht gestartet werden.')
    GUICtrlSetData($ID_Input, '> Client gestartet.' & @CRLF, 1)
    Do ; in dieser Schleife wartet der Server auf Daten und es wird GUIGetMsg abgefragt
    $tDataBuffer = DllStructCreate("byte[" & $BUFFER_SIZE & "]")
    _WinAPI_ReadFile($hPipe, DllStructGetPtr($tDataBuffer), $BUFFER_SIZE, $iRead, DllStructGetPtr($OVERLAPPED_Struct))
    $sData = BinaryToString(DllStructGetData($tDataBuffer, 1), 4)
    $sData = StringLeft($sData, $iRead)
    If $iRead > 0 Then
    _NamedPipes_DisconnectNamedPipe($hPipe)
    GUICtrlSetData($ID_Input, $sData & @CRLF, 1)
    _NamedPipes_ConnectNamedPipe($hPipe, DllStructGetPtr($OVERLAPPED_Struct))
    EndIf
    Until GUIGetMsg() = $GUI_EVENT_CLOSE
    GUICtrlSetData($ID_Input, '> Verbindung getrennt.' & @CRLF, 1)
    GUICtrlSetData($ID_Input, '> Server wird beendet.' & @CRLF, 1)
    _NamedPipes_DisconnectNamedPipe($hPipe)
    _NamedPipes_DisconnectNamedPipe($sPipeName)
    Sleep(1000)
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    Exit

    [/autoit]

    Achja. Wenn ihr es compiliert. Dann nicht als asInvoker, sondern requireAdministrator.
    Weil sonst kommt die Fehlermeldung:

    Code
    Error: Failed to append script data to end of updated executable. Try recompiling your script.rc:2


    Und eure Firewall meckert wegen einen Trojaner ^^

    2 Mal editiert, zuletzt von m-obi (26. April 2013 um 19:04)