Sign in to follow this  
Followers 0
dubi

Interprocess communcation breaks down with StdinWrite and StdoutRead

3 posts in this topic

Hi,

 

Background: i have a number of instances of the same application that I want to automate in parallel.

Unfortunately, I cannot completely automate these instances in the background. So, from time to time these instances need to have the focus so that I can interact with the controls via “send” directly.

Each of the application instances is controlled by a au3 complied script. Each script (called with a parameter) manages the automation of the respective application-instance. Each of the (complied) script (instances) is called by a (central) front end with a gui. The front end controls if the “focus” is available to do the “send” and “mouseclick” modifications. The central front end either allows a child to have the focus or prevents it to get the focus (in which case the child will wait and checks again). The code for the front end is included. Apologies for the lengthy explanation.

#RequireAdmin
#include <ButtonConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <EditConstants.au3>
#include <GUIEdit.au3>
#include <ScrollBarConstants.au3>
#include <Array.au3>

Global Const $APF_ALLOWMULTILINE = 1
Global Const $APF_RIGHTALIGN = 6
Global Const $APF_RIGHTALIGNCOL = 2
Global Const $APF_RIGHTALIGNDATA = 4
Global Const $APF_PRINTROWNUM = 8

Global Const $APF_DEFAULT = $APF_PRINTROWNUM
Global $PID[9], $FocusAvailable = True, $previousEditMsg
Global $PID_waiting[0][2], $logfile
$logfile = "D:\MultiInstance\Logfiles\FrontEnd.txt"
If FileExists($logfile) Then FileDelete($logfile)

#Region ### START Koda GUI section ### Form=
$hGui_1 = GUICreate("Instanzenmanager", 493, 1226, 1807, 93)
$grpInst1 = GUICtrlCreateGroup("          1          ", 8, 8, 233, 121)
$btnPause01 = GUICtrlCreateCheckbox("Pause", 16, 64, 50, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
$tbnStop01 = GUICtrlCreateCheckbox("Stop", 16, 96, 218, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
$btnMin01 = GUICtrlCreateCheckbox("Minimize", 72, 64, 66, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
$btnWdh01 = GUICtrlCreateCheckbox("Restore", 144, 64, 90, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
$btnStart01 = GUICtrlCreateCheckbox("Start", 16, 32, 218, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
GUICtrlSetBkColor(-1, 0x00FF00)
GUICtrlCreateGroup("", -99, -99, 1, 1)
$grpInst3 = GUICtrlCreateGroup("          3          ", 8, 136, 233, 121)
$btnPause03 = GUICtrlCreateCheckbox("Pause", 16, 192, 50, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
$tbnStop03 = GUICtrlCreateCheckbox("Stop", 16, 224, 218, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
$btnMin03 = GUICtrlCreateCheckbox("Minimize", 72, 192, 66, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
$btnWdh03 = GUICtrlCreateCheckbox("Restore", 144, 192, 90, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
$btnStart03 = GUICtrlCreateCheckbox("Start", 16, 160, 218, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
GUICtrlSetBkColor(-1, 0x00FF00)
GUICtrlCreateGroup("", -99, -99, 1, 1)
$grpInst2 = GUICtrlCreateGroup("          2          ", 248, 8, 233, 121)
$btnPause02 = GUICtrlCreateCheckbox("Pause", 256, 64, 50, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
$tbnStop02 = GUICtrlCreateCheckbox("Stop", 256, 96, 218, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
$btnMin02 = GUICtrlCreateCheckbox("Minimize", 312, 64, 66, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
$btnWdh02 = GUICtrlCreateCheckbox("Restore", 384, 64, 90, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
$btnStart02 = GUICtrlCreateCheckbox("Start", 256, 32, 218, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
GUICtrlSetBkColor(-1, 0x00FF00)
GUICtrlCreateGroup("", -99, -99, 1, 1)
$grpInst4 = GUICtrlCreateGroup("          4          ", 248, 136, 233, 121)
$btnPause04 = GUICtrlCreateCheckbox("Pause", 256, 192, 50, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
$tbnStop04 = GUICtrlCreateCheckbox("Stop", 256, 224, 218, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
$btnMin04 = GUICtrlCreateCheckbox("Minimize", 312, 192, 66, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
$btnWdh04 = GUICtrlCreateCheckbox("Restore", 384, 192, 90, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
$btnStart04 = GUICtrlCreateCheckbox("Start", 256, 160, 218, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
GUICtrlSetBkColor(-1, 0x00FF00)

$Edit1 = GUICtrlCreateEdit("", 8, 720, 473, 497)
$btnPauseAll = GUICtrlCreateCheckbox("Pause all", 8, 656, 474, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
$btnStopAll = GUICtrlCreateCheckbox("Stop all", 7, 688, 474, 25, BitOR($GUI_SS_DEFAULT_CHECKBOX, $BS_PUSHLIKE))
GUISetState(@SW_SHOW)
#EndRegion ### END Koda GUI section ###

While 1
    CheckGuiMsg()
    FileWrite($logfile, @HOUR & ":" & @MIN & ":" & @SEC & "> " & "CheckGuiMsg" & @CRLF)

    CheckClientMessages()
    FileWrite($logfile, @HOUR & ":" & @MIN & ":" & @SEC & "> " & "CheckClientMessages" & @CRLF)
WEnd

Func CheckGuiMsg()
    $nMsg = GUIGetMsg()
    Switch $nMsg
        Case $GUI_EVENT_CLOSE
            Exit
        Case $btnStart01
            AddTextToEdit("Starting Instance 1")
            $PID[0] = Run("D:\XVM05\Entwicklung_FilterTest_Multi_ToFileV001.exe 1", @ScriptDir, Default, 3)
        Case $btnStart02
            AddTextToEdit("Starting Instance 2")
            $PID[1] = Run("D:\XVM05\Entwicklung_FilterTest_Multi_ToFileV001.exe 2", @ScriptDir, Default, 3)
        Case $btnStart03
            AddTextToEdit("Starting Instance 3")
            $PID[2] = Run("D:\XVM05\Entwicklung_FilterTest_Multi_ToFileV001.exe 3", @ScriptDir, Default, 3)
        Case $btnStart04
            AddTextToEdit("Starting Instance 4")
            $PID[3] = Run("D:\XVM05\Entwicklung_FilterTest_Multi_ToFileV001.exe 4", @ScriptDir, Default, 3)

        Case $btnPause01
            AddTextToEdit("Send Pause to Instance 1")
            StdinWrite($PID[0], "Pause")
        Case $btnPause02
            StdinWrite($PID[1], "Pause")
        Case $btnPause03
            StdinWrite($PID[2], "Pause")
        Case $btnPause04
            StdinWrite($PID[3], "Pause")

        Case $tbnStop01
            StdinWrite($PID[0], "Stop")
        Case $tbnStop02
            StdinWrite($PID[1], "Stop")
        Case $tbnStop03
            StdinWrite($PID[2], "Stop")
        Case $tbnStop04
            StdinWrite($PID[3], "Stop")

        Case $btnPauseAll
            AddTextToEdit(@CRLF & "************Pause All not yet implemented**************" & @CRLF)
        Case $btnStopAll
            AddTextToEdit(@CRLF & "************Stop All not yet implemented***************" & @CRLF)
    EndSwitch
EndFunc   ;==>CheckGuiMsg

Func CheckClientMessages()
    For $i = 0 To 3
        FileWrite($logfile, @HOUR & ":" & @MIN & ":" & @SEC & "> " & $i & @CRLF)
        Local $a = TimerInit()
            $p = $PID[$i]
            $streamRead = StdoutRead($p)
            If $streamRead <> "" Then
                Switch $streamRead
                    Case StringInStr($streamRead, "Focus Needed") > 0
                        If $FocusAvailable Then
                            $FocusAvailable = False
                            StdinWrite($p, "Focus Granted")
                        Else
                        EndIf
                    Case StringInStr($streamRead, "Release Focus") > 0
                        StdinWrite($p, "Release Focus Received")
                        $FocusAvailable = True
                    Case Else
                EndSwitch
            EndIf
        FileWrite($logfile, @HOUR & ":" & @MIN & ":" & @SEC & "> " & $i & "    " & round(TimerDiff($a),2) & @CRLF)

    Next
EndFunc   ;==>CheckClientMessages


Func AddTextToEdit($text)
    If $previousEditMsg <> $text Then
        $previousEditMsg = $text
        GUICtrlSetData($Edit1, GUICtrlRead($Edit1) & @YEAR & "." & @MON & "." & @MDAY & " - " & @HOUR & ":" & @MIN & ":" & @SEC & "> " & $text & @CRLF)
        _GUICtrlEdit_Scroll($Edit1, $SB_SCROLLCARET)
    EndIf

EndFunc   ;==>AddTextToEdit

My issue now is that the mechanism with with StdoutRead and StdinWrite is not efficient at all. The more instances I start the slower it gets. This is not just a bit slower (like a fraction of a second), but to the degree that the front end is not responding at all any longer (with 3 instances handling).

So my questions are:

1.       Is there a flaw in my implementation with StdoutRead and StdinWrite? (note that all works fine with 1 and also (slower) with 2 instances running) but actually breaks down with 3 instances running.

2.       Can I optimize the currently used implementation so that I can control 30+ instances?

3.       What other implementation do you see suitable for this approach?

a.       I have already tried it with communication through files but observed that this is not sufficiently reliable with multiple instances.

b.       Is Named Pipes a more performant approach (I am a bit scared of the effort to learn and implement this)

c.       Any other method?

 

Many thanks in advance

-dubi



Share this post


Link to post
Share on other sites



1. don't see any obvious mistakes (but haven't looked carefully)

2. probably not the way to go.

3. You could make your central script a TCP server, and the other mpdules hook up as TCP clients. NamedPipes can be a bit confusing to set up. Alternatively, MailSlots are super simple. Or you could give my Pool environment (link in my sig) a spin. The keyword to search the forums with is IPC (inter-process communication). Beware that your anti-virus software may interfere with various IPC communication channels, so may have to be explicitly enabled.

Share this post


Link to post
Share on other sites

Thank you RTFC!

I have done more testing and have now detected that the EditControl is the culprit of the delay. I was sending quite a number of messages to this control which slowed things down so significantly that eventually nothing worked any longer. I wanted to use this as a logging window for some messages during testing. The moment I removed the control everything works perfectly again.

23 hours ago, dubi said:

$Edit1 = GUICtrlCreateEdit("", 8, 720, 473, 497)

So for the moment there is no need to implement another means of IPC.
Thanks to all that were reading :-)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0

  • Similar Content

    • OGA
      By OGA
      Hi, I'm new.
      Anyways, I'm using the RunBinary.au3 script by trancexx and I want to re-direct the STDOUT of the "child process" back to the autoit script that launches it. I'm attempting to do so using named pipes. If its possible to use StdoutRead instead of namedpipes please let me know. I'm just unsure of how to provide a handle of the childs STDOUT stream to that function. Though DllCall("kernel32.dll", "ptr", "GetStdHandle", "dword", "STD_OUTPUT_HANDLE") seems to get the handle?
      Please excuse any foolish mistakes because I'm new to STDOUT, runbinary and namedpipes. Here's the parts of the code I'm trying to use that are relevent:
      ;~~~Firstly I think I need to make a pipe that's inheritable.. which I may have done wrong Local $_SECURITY_ATTRIBUTES = DllStructCreate("dword Length;" & _ "int lpSecurityDescriptor;" & _ "bool InheritHandle;") ;***Not positive if bool works correctly here? DLLStructSetData($_SECURITY_ATTRIBUTES, "Length", DllStructGetSize($_SECURITY_ATTRIBUTES)) DLLStructSetData($_SECURITY_ATTRIBUTES, "lpSecurityDescriptor", 0) ;***This sets default state; "If the value of this member is NULL, the object is assigned the default security descriptor associated with the access token of the calling process." but I'm unsure if this is what I should use DLLStructSetData($_SECURITY_ATTRIBUTES, "InheritHandle", true);***True = Inheritable(but again I'm not positive the bool works correctly?) Global $hNamedPipe = _NamedPipes_CreateNamedPipe("\\.\pipe\poopp", _;Name 2, _;Direction: 2=both ;I only need 1 direction but I'm just using this for testing 1, _;Flags: 1=no extra instances of pipe are allowed to run 0, _;Security: No ACL Security 0, _;Type: 0=byte 0, _;ReadType: 0=byte 1, _;Wait: 0=Block(wait) 1=No block(no wait) 1, _;Max Instances of pipe allowed 4096, _;out size 4096, _;in size 9000, _;timeout DllStructGetPtr($_SECURITY_ATTRIBUTES));Default=0 which wouldn't make the handle inheritable ;~~~Next I would need to set the STARTUPINFO of the process ;code used by trancexx for the _STARTUPINFO Global $tSTARTUPINFO = DllStructCreate("dword cbSize;" & _ "ptr Reserved;" & _ "ptr Desktop;" & _ "ptr Title;" & _ "dword X;" & _ "dword Y;" & _ "dword XSize;" & _ "dword YSize;" & _ "dword XCountChars;" & _ "dword YCountChars;" & _ "dword FillAttribute;" & _ "dword Flags;" & _ "word ShowWindow;" & _ "word Reserved2;" & _ "ptr Reserved2;" & _ "ptr hStdInput;" & _ "ptr hStdOutput;" & _ "ptr hStdError") ;Attempting to set the values for namedpipe redirection DllStructSetData($tSTARTUPINFO, "Flags", 0x00000100) ;***Flag = STARTF_USESTDHANDLES (I think I set it correctly?) DllStructSetData($tSTARTUPINFO, "hStdOutput", $hNamedPipe) ;***Currently setting the output handle to the SERVER end of the NamePipe I'm creating (which I'm pretty sure is wrong but idk how to use the Client End) ;~~~code used by trancexx for CreateProcess Global $aCall = DllCall("kernel32.dll", "bool", "CreateProcessW", _ "wstr", $sExeModule, _ "wstr", $sCommandLine, _ "ptr", 0, _ "ptr", 0, _ "bool", true, _ ;***changed to inherit handles (not positive I did so correctly) was int 0 before "dword", 4, _ ; CREATE_SUSPENDED ; <- this is essential "ptr", 0, _ "ptr", 0, _ "ptr", DllStructGetPtr($tSTARTUPINFO), _ "ptr", DllStructGetPtr($tPROCESS_INFORMATION)) ;~~~~~Code used in a loop to try to see if anything is being written into the pipe If _IsPressed(35, $hDLL) Then Local $pipeData = _NamedPipes_PeekNamedPipe($hNamedPipe) If @Error Then MsgBox(1,"PipeData Error",@Error & " | " & $pipeData) Else Local $r = _ArrayDisplay($pipeData) If @Error Then MsgBox(1,"Array Error",@Error & " | " & $pipeData) EndIf EndIf  
      I'm not using this exact code cause I changed it around some for the post. I'm mainly wondering how to correctly use the client end of the name pipe? I also had some values I wasn't sure if I set correctly because I don't have experience with com objects. And It seems the process launched needs to be the child?.. Can the process started through the autoitscript can be considered the child process and the script the parent process?
       
      Guides I'm using for this:
      https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx
      https://support.microsoft.com/en-us/help/190351/how-to-spawn-console-processes-with-redirected-standard-handles
    • careca
      By careca
      #include <Constants.au3> ListLetter() Func ListLetter() ;$DSK = Run("cmd", '', '', $STDIN_CHILD + $STDOUT_CHILD) $DSK = Run(@ComSpec & " /c diskpart.exe start", '', '', $STDIN_CHILD + $STDOUT_CHILD) ConsoleWrite('$DSK - '& $DSK&' - Error - '& @error &' - '&@MSEC&@CRLF) Sleep(2000) $Read = StdoutRead($DSK, True, False) ConsoleWrite(' - '& @error &' - '&@MSEC&@CRLF) ConsoleWrite('StdoutRead ' &$Read&' - '&@MSEC&@CRLF) EndFunc ;==>ListLetter So, this works with normal console "cmd" why does it error with diskpart? how can i make it read from diskpart?
    • Grosminet
      By Grosminet
      Hi,
      I'm blocked on a strange issue concerning the use of '_WinAPI_ReadProcessMemory' to retrieve one 'String' between 2 cooperating applications based on the IPC method using a private 'Windows Message' handler (thanks to '_WinAPI_RegisterWindowMessage').
      Let's me explain what happens:
      1) - From a small GUI 'ipc-sender' application, the user can type any string (like 'abcde') and click a 'Send Data' button to exchange this info with another small 'ipc-receiver' application. the coding is done in such way ( '_DumpStruct()' method) that a trace of the data sent is dumped in an edit viewer inside the GUI: see the 'ipc-sender' script source below -->
      #Region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_Outfile=ipc_sender.exe #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** #include <GUIConstantsEx.au3> #include <SendMessage.au3> #include <MsgBoxConstants.au3> #include <WinAPI.au3> #include <WinAPISys.au3> #include <ProcessConstants.au3> #include <FontConstants.au3> #include <GuiEdit.au3> #include <ScrollBarsConstants.au3> #include <Array.au3> ; Author : Grosminet Global Const $WM_IPC_PRIVATE_Grosminet = _WinAPI_RegisterWindowMessage('ipc_sender_to_receiver') Global Const $sAPP_me = "ipc_sender" Global Const $sAPP_other = "ipc_receiver" Global $guiw = 1000, $guih = 300, $guix = (@desktopwidth - $guiw - 50), $guiy = $guih + 150, $sp = 10, $x = $sp, $y = $sp, $w, $hbut = 28, $h Global $hParentGUI, $hSendBut, $hlocalPID, $hSendEdit, $hRecEdit Global $debug = true, $info, $PIDAppMe, $hOtherProcess ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo $hParentGui = GUICreate($sAPP_me, $guiw, $guih, $guix, $guiy) $w = ($guiw - 3*$sp) / 4 $h = ($guih - 3* $sp) / 2 $hSendBut = GUICtrlCreateButton("Send data", $x, $y, $w, $hbut) $y += $hbut + $sp $hlocalPID = GUIctrlCreateLabel("PID=", $x, $y, $w, $h) $x += $w + $sp $y = $sp $hSendEdit = GUIctrlCreateEdit("abcde", $x, $y, 3* $w, $h) $x = $sp $y += $h + $sp $hRecEdit = GUIctrlCreateEdit("", $x, $y, 4* $w, $h) GUICtrlSetFont(-1, 9, $FW_NORMAL, Default, "Courier New") ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo GUISetState(@SW_SHOW, $hParentGui) GUICtrlSetData($hlocalPID, "PID= " & @AutoItPID) ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo ; Get the RECEIVER application 'process handle' ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo Opt("WinTitleMatchMode", 1) $hOtherProcess = WinGetHandle($sAPP_other) if @error then MsgBox($MB_SYSTEMMODAL, "ERROR", "Unable to retrieve handle of " & $sAPP_other & ", error= " & @error) exit endif $info = " Receiver application --> " & $sAPP_other & " - Handle= " & $hOtherProcess & @crlf _ShowInfo($info) ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE _myExit() Case $hSendBut _SendDATA_to_X() EndSwitch WEnd ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo Func _SendDATA_to_X() Local $sValue_To_Send = GUICtrlRead($hSendEdit) Local $iStringSize = StringLen($sValue_To_Send) + 1 local $TagInfoStruct = "struct;wchar buf[" & $iStringSize & "];endstruct" Local $tValue_To_Send = DllStructCreate($TagInfoStruct) DllStructSetData($tValue_To_Send, "buf", $sValue_To_Send) ; Local $pValue_To_Send = DllStructGetPtr($tValue_To_Send) Local $iSizeStruct = DllStructGetSize($tValue_To_Send) $info = '_SendDATA_to_X(): Pointer to text= ' & $pValue_To_Send & " - Size of text= " & $iStringSize & " - Size of structure= " & $iSizeStruct & @CRLF _ShowInfo($info) $info = _DumpStruct($pValue_To_Send, $iSizeStruct) _ShowInfo($info) ; local $ret = _WinAPI_PostMessage($hOtherProcess, $WM_IPC_PRIVATE_Grosminet, $pValue_To_Send, $iSizeStruct) If not $ret Then MsgBox($MB_SYSTEMMODAL, "ERROR", "_SendDATA_to_X(): " & $sAPP_me & " --> _WinAPI_PostMessage error= " & _WinAPI_GetLastError()) else Local $sData_Sent = StringLeft(DllStructGetData($tValue_To_Send, "buf"), $iStringSize) $info = '................: --> Data sent = ' & $sData_Sent & @CRLF _ShowInfo($info) endif $pValue_To_Send = 0 $tValue_To_Send = 0 EndFunc ;==>_SendDATA_to_X ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo Func _ShowInfo($msg) if $debug then ConsoleWrite($msg) GUICtrlSetData($hRecEdit, GUICtrlRead($hRecEdit) & $msg) Local $iEnd = StringLen(GUICtrlRead($hRecEdit)) _GUICtrlEdit_SetSel($hRecEdit, $iEnd, $iEnd) _GUICtrlEdit_Scroll($hRecEdit, $SB_SCROLLCARET) Endfunc ; _ShowInfo ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo Func _myExit() GUIDelete() exit Endfunc ; _myExit ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo Func _DumpStruct($p_STRUCT, $iSizeStruct) ; $iSizeStruct = the size of the struct in bytes (DllStructGetSize) Local $TagStructDump = "struct;align;byte[" & $iSizeStruct & "];endstruct" Local $t_Struct = DllStructCreate($TagStructDump, $p_STRUCT) Local $i Local $structInfo = "" _ConsoleWriteInfo($structInfo, "Structure size: " & $iSizeStruct & " byte(s):" & @crlf) for $i = 0 to $iSizeStruct - 1 _ConsoleWriteInfo($structInfo, hex(DllStructGetData($t_Struct, 1, $i), 2) & " ") if (Mod($i+1, 8) = 0) then _ConsoleWriteInfo($structInfo, @CRLF) Endif Next _ConsoleWriteInfo($structInfo, @CRLF) return $structInfo EndFunc ; _DumpStruct ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo Func _ConsoleWriteInfo(ByRef $msg, $txt) $msg &= $txt EndFunc ; _ConsoleWriteInfo 2) - From a small GUI 'ipc-receiver' application, the user can check the values of data received thanks to the same '_DumpStruct()' method: --> see the 'ipc-receiver' script :
      #Region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_Outfile=ipc_receiver.exe #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** #include <Array.au3> #include <GUIConstantsEx.au3> #include <SendMessage.au3> #include <MsgBoxConstants.au3> #include <WinAPI.au3> #include <WinAPISys.au3> #include <ProcessConstants.au3> #include <FontConstants.au3> #include <GuiEdit.au3> #include <ScrollBarsConstants.au3> #include <WinAPIDiag.au3> ; Author : Grosminet Global Const $WM_IPC_PRIVATE_Grosminet = _WinAPI_RegisterWindowMessage('ipc_sender_to_receiver') Global Const $sAPP_me = "ipc_receiver" Global Const $sAPP_other = "ipc_sender" Global Const $sSenderEXE = @scriptdir & "\" & $sAPP_other & ".exe" Global $guiw = 1000, $guih = 300, $guix = (@desktopwidth - $guiw - 50), $guiy = 100, $sp = 10, $x = $sp, $y = $sp, $w, $hbut = 28, $h Global $hParentGUI, $hlocalPID, $hRecEdit Global $debug = true, $info, $hProcessOther, $PIDAppMe, $PIDAppOther, $iRead, $aret ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo $hParentGui = GUICreate($sAPP_me, $guiw, $guih, $guix, $guiy) $w = ($guiw - 2*$sp) $hlocalPID = GUIctrlCreateLabel("PID=", $x, $y, $w, $hbut) $y += $hbut + $sp $h = ($guih - $y - $sp) $hRecEdit = GUIctrlCreateEdit("", $x, $y, $w, $h) GUICtrlSetFont(-1, 9, $FW_NORMAL, Default, "Courier New") ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo GUIRegisterMsg($WM_IPC_PRIVATE_Grosminet, 'WM_FROM_APP') GUISetState(@SW_SHOW, $hParentGui) GUICtrlSetData($hlocalPID, "PID= " & @AutoItPID) ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo ; Get the SENDER application 'pid' ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo $PIDAppOther = ShellExecute($sSenderEXE) if $PIDAppOther = -1 then MsgBox($MB_SYSTEMMODAL, "ERROR", "Unable to start " & $sAPP_other & " --> error= " & @error) exit Endif sleep(500) $info = "Ready to receive ! Please send a text ..." & @CRLF _ShowInfo($info) ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo ; Get the SENDER application 'process handle' ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo $hProcessOther = _WinAPI_OpenProcess($PROCESS_VM_READ, False, $PIDAppOther) if @error Then $info = "_WinAPI_OpenProcess() error: " & @error & @crlf _ShowInfo($info) exit endif ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE _myExit() EndSwitch WEnd ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo Func WM_FROM_APP($hWnd, $iMsg, $wParam, $lParam) $info = "..... METHOD 1: _WinAPI_CreateBuffer ....." & @crlf _ShowInfo($info) _Method_1($wParam, $lParam) ; $info = "..... METHOD 2: DllStructCreate .........." & @crlf _ShowInfo($info) _Method_2($wParam, $lParam) EndFunc ;==>WM_FROM_APP ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo Func _Method_1($wParam, $lParam) Local $iStrucSize_SENT = Int($lParam) Local $pBuffer = _WinAPI_CreateBuffer($iStrucSize_SENT) $aret = _WinAPI_ReadProcessMemory($hProcessOther, $wParam, $pBuffer, $iStrucSize_SENT, $iRead) ; $info = _DumpStruct($pBuffer, $iStrucSize_SENT) _ShowInfo($info) _ShowInfo(_WinAPI_GetString($pBuffer) & @crlf & "--------------------------------" & @crlf) _WinAPI_FreeMemory($pBuffer) EndFunc ; _Method_1 ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo Func _Method_2($wParam, $lParam) Local $iStrucSize_SENT = Int($lParam) local $TagInfoStruct = "struct;align;byte buf[" & $iStrucSize_SENT & "];endstruct" Local $tbuffer = DllStructCreate($TagInfoStruct) Local $iSizeStruct = DllStructGetSize($tbuffer) Local $pBuffer = DllStructGetPtr($tbuffer) $aret = _WinAPI_ReadProcessMemory($hProcessOther, $wParam, $pBuffer, $iStrucSize_SENT, $iRead) ; $info = _DumpStruct($pBuffer, $iStrucSize_SENT) _ShowInfo($info) _ShowInfo(_WinAPI_GetString($pBuffer) & @crlf & "--------------------------------" & @crlf) $pBuffer = 0 EndFunc ; _Method_2 ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo Func _ShowInfo($msg) if $debug then ConsoleWrite($msg) GUICtrlSetData($hRecEdit, GUICtrlRead($hRecEdit) & $msg) Local $iEnd = StringLen(GUICtrlRead($hRecEdit)) _GUICtrlEdit_SetSel($hRecEdit, $iEnd, $iEnd) _GUICtrlEdit_Scroll($hRecEdit, $SB_SCROLLCARET) Endfunc ; _ShowInfo ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo Func _myExit() _WinAPI_CloseHandle($hProcessOther) ProcessClose($PIDAppOther) GUIDelete() exit Endfunc ; _myExit ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo Func _DumpStruct($p_STRUCT, $iSizeStruct) ; $iSizeStruct = the size of the struct in bytes (DllStructGetSize) Local $TagStructDump = "struct;align;byte[" & $iSizeStruct & "];endstruct" Local $t_Struct = DllStructCreate($TagStructDump, $p_STRUCT) Local $i Local $structInfo = "" _ConsoleWriteInfo($structInfo, "Structure size: " & $iSizeStruct & " byte(s):" & @crlf) for $i = 0 to $iSizeStruct - 1 _ConsoleWriteInfo($structInfo, hex(DllStructGetData($t_Struct, 1, $i), 2) & " ") if (Mod($i+1, 8) = 0) then _ConsoleWriteInfo($structInfo, @CRLF) Endif Next _ConsoleWriteInfo($structInfo, @CRLF) return $structInfo EndFunc ; _DumpStruct ; ooooooooooooooooooooooooooooooooooooooooooooooooooooooo Func _ConsoleWriteInfo(ByRef $msg, $txt) $msg &= $txt EndFunc ; _ConsoleWriteInfo The ISSUE : systematically, the 3 first bytes received are 'corrupted' !!! ???
      NOTE: You must repeat several times sending the same string to check that  bytes sent" and "bytes received"  are equal EXCEPT the 3 first ones !
      I'm quite sure that my code is somewhere wrong ! BUT I'm not able to discover myself WHERE ! I have tried to use 2 methods to read and save the external memory bytes (using the '_WinAPI_CreateBuffer' function, and the 'DllStructCreate' function) --> BOTH give me back the same issue.
      --> So I suspect that my understanding of the '_WinAPI_ReadProcessMemory' function is maybe wrong and I do not correctly call this API.
      ??? Is it correct if I say, [according the MSDN 's ReadProcessMemory explanation or the #include <WinAPI.au3> library code of this function] :
      - the base address of memory to be read is the pointer received from my private WM handler --> i.e. $wParam (regarding my script receiver code)
      - the buffer pointer where to save bytes read (starting from $wParam) is the pointer created using '_WinAPI_CreateBuffer' or 'DllStructCreate + DllStructGetPtr' functions
      - the number of bytes to be read is the information provided by the $lParam variable (regarding my script receiver code)
      - AND of course, the external memory base-address will only be readable if the 'ipc-sender' application handler is correctly declared ($hProcessOther = _WinAPI_OpenProcess($PROCESS_VM_READ, False, $PIDAppOther)).
      There is probably other methods to share strings between cooperating applications, and surely more simple and elegant ones, BUT I'm focusing on these scripts where in fact the types of data to share are not limited to the 'String' type, but could concern any kind of structure.
      Any advice or help to explain me what happens would be welcome.
      Great Thanks in advance for your time passed to help me...
      Alain.
      These are my environment characteristics:
      AutoIT : 3.3.14.2
      OS: Windows 7 Home Premium Service Pack 1 / 7601
       
       
      ipc_receiver.au3
      ipc_sender.au3
    • nill
      By nill
      $user = "root" $password ="" $host ="5.0.0.1" $port ="22" $puty_exe = @ScriptDir & "\putty.exe"; putty salve local folder script $command = Run(@comspec & " /C "&$puty_exe&" -ssh -l "&$user&" "&$host&" p "&$port&" -pw "&$password,@ScriptDir, @SW_HIDE, 1) While 1 $data = StdoutRead($command) ConsoleWrite($data&@CRLF) If @error Then ExitLoop Wend Why StdoutRead cant read what putty.exe write in console ?
    • Jeemo
      By Jeemo
      Hello AutoIt community,
      I'm working on a project where I need to copy the bits of a file to a child process's StdIn stream. It's almost complete, but I have one final snag. 
      The script uses DLLStructCreate to create a 64K buffer that holds the file's binary contents in memory. It iterates through the contents of the desired file in 64K segments, first pulling the bits into the buffer, then dumping the contents of the buffer out to the child process's StdIn stream. The child process then ultimately reconstitutes the file on a remote system.
      The child process does build the file in the target directory, but the problem is that the reconstituted file is always slightly larger than the original because it's always a multiple of 64K (when you look at the file properties, the size is always identical to the "size on disk" value). Storage consumption is not a concern since it's technically not occupying more space than the original, but file integrity is a concern. Not every file shows any signs of corruption when opening it, but some of the reconstituted files are completely useless because of this.
      I know that the problem lies within the last iteration of reading the source file; for example, if there are only 24K bits remaining to be read, that data gets stored in the DLLStruct along with 40K of zeros to fill the entire 64K buffer. When the file is reconstituted, this padding of zeroes is unfortunately also included. So my challenge is to try to figure out how to ignore these trailing zeroes while reading the final <64K bits of the file, or at least only send part of the contents of the DLLStruct buffer to StdIn. Does anyone know how to go about doing this?
      Thanks in advance,
      Jeemo