Jump to content

Recommended Posts

JohnWIlling

IPC_IO.AU3

I am in the need for a simple synchronous Client/Server communication.  I found several examples and references to various kinds of Inter-Process Communications such as TCP, Named Pipes, Mail Slots, Shared Memory, Memory Mapped Files, and simple Files.  I wanted to see what the best solutions would be.  I began developing a library and slowly began adding each of the IPC methods and ended up with a library with a very simple synchronous “ASCII” API where the application can choose which method to use at startup.

For the Server side, a Server app must initialize communication by calling:

Func InitConnection($cType = $cDefaultType, $ResourceName = "", $bBlock = $cDefaultBlocking, $fSleepFunc = "", $iBufSize = $DEFAULT_BUFSIZE)

The optional arguments allow the app to specify the connection type (such as: $cNamedPipe, $cFile, $cTCP, $cSharedMem, $cMailSlot), a value for the resource name (such as the file, named pipe name, TCP port number, etc.), the communication buffer size, and a callback function for when the “read” is waiting for data.

A “File Descriptor” is returned and must be used in the future API calls.

The Server side must then call:

Func StartConnection($iFD)

This call waits for a Client to connect.  The Server then calls:

                Func ReadData($iFD, ByRef $sData)

To read a Request from the Client and then calls:

                Func WriteData($iFD, ByRef $sData)

To send the reply back to the Client.

When communication with the Client is done, the Server app will call:

Func StopConnection($iFD)

When the Server app is done with the communications it will call:

Func EndConnection($iFD)

 

For the Client side, a Client app must open the communication by calling:

Func OpenConnection($cType = $cDefaultType, $ResourceName = "", $bBlock = $cDefaultBlocking, $fSleepFunc = "", $iBufSize = $DEFAULT_BUFSIZE)

The optional arguments allow the app to specify the connection type (such as: $cNamedPipe, $cFile, $cTCP, $cSharedMem, $cMailSlot), a value for the resource name (such as the file, named pipe name, TCP port number, etc.), the communication buffer size, and a callback function for when the “read” is waiting for data.

A “File Descriptor” is returned and must be used in the future API calls.

The Client side then send a request to the Server app by calling:

                Func WriteData($iFD, ByRef $sData)

To read a Response from the Server by calling:

                Func ReadData($iFD, ByRef $sData)

To end the connection to the Server by calling:

Func CloseConnection($iFD)

 

Within the IPC_IO.AU3 library, each IPC method is ether:

·         “stream” based where data is read/written by calling _WinAPI_ReadFile/TCPRecv or _WinAPI_WriteFile/ TCPSend

·         “direct” based for Shared memory where the Client reads the data directly from the Server App’s memory and the Server directly reads the Client App’s memory

In processing a request, the “ReadData” process starts by checking if data is ready to be read by calling the routine: “ReadStart”, then it reads in the size of the request by calling “ReadSize”, it then reads in the Ascii Request by calling “ReadBuffer”, then the sequence is completed by calling “ReadEnd”. 

The Write Process follows the same sequence with “WriteData” calling “WriteStart”, “WriteSize”, “WriteBuffer”, “WriteEnd”.

 

Results

My testing showed that the performance of sending and receiving of a 10k file took:

·         "Shared Memory" was the fastest, at 0.007468 Sec

·         “Named Pipes” at 0.015954

·         “Mail Slots” at 0.016427

·         “File Based” at 0.270287

·         “TCP” at 0.994884

 

IPC_IO.au3

Client.au3

Server.au3

  • Thanks 1

Share this post


Link to post
Share on other sites
argumentum
7 hours ago, JohnWIlling said:

I am in the need for a simple synchronous Client/Server communication.

me too. Tried but the UDF gave me errors

>Running AU3Check (3.3.14.2)  from:C:\Program Files (x86)\AutoIt3  input:G:\au3s\ipc io\IPC_IO.au3
"G:\au3s\ipc io\IPC_IO.au3"(307,123) : error: _WinAPI_GetLastErrorMessage() called with wrong number of args.
    $sMessage &= " Err:" & _WinAPI_GetLastError($tError, $tExtended) & " (" & _WinAPI_GetLastErrorMessage($tError, $tExtended)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
"C:\Program Files (x86)\AutoIt3\Include\WinAPI.au3"(1526,35) : REF: definition of _WinAPI_GetLastErrorMessage().
Func _WinAPI_GetLastErrorMessage()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
"G:\au3s\ipc io\IPC_IO.au3"(1542,90) : error: _WinAPI_CreateFileEx(): undefined function.
    $CArray[$iFD][$C_InFD] = _WinAPI_CreateFileEx($InFile, $OPEN_EXISTING, $iAccess, $iShare)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
G:\au3s\ipc io\IPC_IO.au3 - 2 error(s), 0 warning(s)

 

Share this post


Link to post
Share on other sites
JohnWIlling

I used the latest version of AutoIT V3.3.14.3 which has several newer constants and APIs defined.

 

  • Thanks 1

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

  • Similar Content

    • JRSmile
      By JRSmile
      Hi,
      just for reference.
      you can control the streamlabs OBS client via Named Pipes like so:
       
      Local $sMessage = '{"jsonrpc": "2.0","id": 1,"method": "toggleRecording","params": {"resource": "StreamingService","args": []}}' $handle = FileOpen("\\.\pipe\slobs", 2) FileWriteLine($handle, $sMessage) FileClose($handle)  
      the api can be found here: https://stream-labs.github.io/streamlabs-obs-api-docs/docs/index.html
    • argumentum
      By argumentum
      I was in need of an IPC (Interprocess communication) between system, admin and user levels, and ended up writing this UDF to suit my wantings.
      Hope you find it useful too.
      Works from WinXP/Server2003 to the now current Win10/Server2016.
      It communicates between any mix of x32, x64, Admin, User.

      In the zip file, there is the UDF and an example: FMIPC(v0.2018.04.04).zip
      Special thanks to @RTFC for the help in the support forum   
    • argumentum
      By argumentum
      There is this topic on Examples about IPC. My question is: What is the best IPC to work with between a script running as SYSTEM level, User level, and Administrator level to interact with each other ? 
      Thanks
    • dubi
      By dubi
      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





    • 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
×