RTFC

Pool - Post Office for Organised Labour

7 posts in this topic

#1 ·  Posted (edited)

Pool provides multiple AutoIt processes with an infrastructure for exchanging messages, data, and remote-control instructions, on a single machine or on a local area network. It is modelled partly on HTCondor, but also incorporates user-defined messages (UDM), LANchat, mails, data Container shipping management, and remote power control and remote execution. That last feature means one process can send lines of AutoIt script to another process, and run it there (including batch control and synchronisation). Consider it my take on inter-process communication (IPC).
 
Pool incorporates many contributions (a few are included in the bundle, other tiny snippets are part of the main script), from other forum members without whom this project simply would never even have been conceivable. In particular, I hereby want to acknowledge the excellent work of Zatorg, wraithdu, W0uter, Ward, ValeryVal, spudw2k, trancexx, This-is-Me, Nomad, Nine, Manadar, LazyCat, Lakes, Kip, Kealper, Jos, Greencan, FredAI, evilertoaster, dragan, Chimp, and AdamUL. Some codes merely helped to clarify my novice understanding of various IPC issues, others have been gratefully integrated. Three contributors need special mention: Kip & Zatorg for event-driven TCP, and trancexx for MailSlot; together these form the backbone of everything else. The only reason I still dare to publish this under my own name is because I did add over ten thousand lines of code myself. Nevertheless, Pool may serve as a showcase for how various members' example scripts can be combined into a comprehensive environment. Many, many thanks to everyone involved! :thumbsup:
 
Pool is designed to be simple to use. Only a few functions are public, and of those, you really only need to study these four control UDFs:
_Pool_Send_Command(): Pool's Command & Control Centre (see list of commands below)
_Pool_Send_UserDefinedMsg(): define your own outgoing msgs
_Pool_Receive_UserDefinedMsg(): process your own incoming msgs
_Pool_Send_ExeQcall(): send one or more AutoIt instructions to target's Exe-Queue (ExeQ)
 
and these ones for data transfers:
_Pool_Container_Create(): associate shipping Container with data
_Pool_Container_Destroy(): release Container memory and all associated Sharing relations
_Pool_Container_CreateShare(): associate destination with Container
_Pool_Container_DestroyShare(): dissolve sharing association for destination
 
To get an idea of what _Pool_Send_Command() can do, here's the list of commands:
 

 

* Pool Control

"Ping" ; test presence, determine Pool-Msg turnaround time
"StandBy" ; defer TODOlist/ExeQ processing
"Proceed" ; resume TODOlist/ExeQ processing
"SyncWait" ; PARAM=syncID; Standby until SYNC_RESUME with same syncID received
"SyncResume" ; PARAM=syncID; Standby until SYNC_RESUME with same syncID received
"Identify" ; tell msg recipient to mail its own PML entry to msg sender
"Uptime" ; return uptime
"Mute" ; PARAM=T/F; T: defer outgoing messages; F: resume
"LeaveNow" ; leave Pool immediately; Exit unless $_POOL_STATUS_STAYALIVE = True
"LeaveWhenDone" ; leave Pool once all current tasks are completed (no reverse transfers afterwards)
"CountMembers" ; update various PML-based membership counters
"ListLocalMembers" ; return entire PML
"AddMembers" ; PARAM=number; tell MGR to start N more default processes locally
"AddMaxMembers" ; tell MGR to start new default Pool processes up to the number of CPU cores
"RemoveMember" ; tell TARGET process(!) to tell all COLLECTORS

* Power-related

"MachineWakeUp" ; triggers Wake-on-LAN magicpacket (untested)
"MachineStandBy" ; place target's machine in StandBy mode
"MachineSuspend" ; place target's machine in Hibernation
"MachineShutDown" ; power down target's machine (target calls Pool_Closedown too)
"MachineReboot" ; reboot target's machine (target calls Pool_Closedown too)

* MailSlot-related (call _Pool_Send_Mail() to send your own, user-defined mails)

"MailTest" ; send a Mail (with double echo)
"Scan4MailSlots" ; detect new MailSlots on localhost
"DestroyMailSlot" ; close MailSlot
"CreateMailSlot" ; create a new MailSlot

* ExeQ-related (call _Pool_Send_ExeQcall() to parse a single cmd or multi-line batch job)

"ClearExeQ" ; release all ExeQ contents
"IamYourMaster" ; for BDSM lovers
"IamYourSlave" ; ditto
"IamNotYourSlave" ; for the rest of us
"ExeQReportState" ; returns IPTR/ExeQ size, stored in PML
"ExeQReportBatch" ; returns target's current batch markers, stored in $_POOL_STATUS_EXEQ_BATCH_START/END
"ExeQIgnoreErrors" ; PARAM = T/F; break ExeQ running on @error
"ExeQBatchStart" ; use this ONLY before supplying batch contents one line at a time
"ExeQBatchEnd" ; use this ONLY after supplying batch contents one line at a time
"Run" ; start/resume execution until end of ExeQueue/currently defined batch
"Break" ; interrupt batch execution a.s.a.p.
"RunNext" ; execute the contents of $_Pool_ExeQueue[$_POOL_STATUS_EXEQ_IPTR]
"RunBatch" ; execute all lines of a defined ExeQ batch
"RunFrom" ; execute from the specified ExeQ line to the end
"RunPrev" ; execute last-previously executed line again
"RunAll" ; (re)run from first line of ExeQ to the end
"SetExeQptr" ; PARAM low word = IPTR (ExeQ instruction pointer)

* Container-related (requires prior creation of data Container and at least one Share)

"RequestContainerID" ; ask local MGR to provide IDs before creating Containers (much faster)
"LoadContainer" ; PARAM = structID; tell CLN to load specified Container with associated data, but do not send it (yet)
"LoadSendContainer" ; PARAM = structID; tell CLN to load specified Container with associated data, and send it
; if targetID = Creator: send out to all defined shares
; if targetID = sharer: return data to Creator
"SendContainer" ; PARAM = structID; tell CLN to send specified Container without reloading it first
; if targetID = Creator: send out to all defined shares
; if targetID = sharer: return data to Creator
"SendContainerAsMail"; PARAM = structID; tell CLN to send specified TINY Container via MailSlot, without reloading it first
"LoadSendContainerAsMail" ; PARAM = structID; tell CLN to reload and send specified TINY Container via MailSlot
"PrepContainerTtransfers" ; PARAM = structID; tell CLN to (re)activate all Shares for specified Container (sets all Share Transfers as "pending")
"DestroyContainer" ; PARAM = structID; release specified Container only
"ReleaseAllContainers" ; release all Containers (variables themselves remain intact)

* TCP/UDP-related

"NewPort" ; PARAM=new port; assign new port for TCP/UDP (requires both protocols to be currently disabled)
"TCPconnect" ; for MGRs/testing only; NB this would be impossible to receive without the MailSlot alternative
"TCPdisconnect" ; for MGRs/testing only
"TCPmultiMsgDelay" ; PARAM = delay in ms; adjust target's $_POOL_STATUS_TCP_MULTIMSG_DELAY
"WMmultiMsgDelay" ; PARAM = delay in ms; adjust target's $_POOL_STATUS_WMULTIMSG_DELAY
"UDPconnect" ; for MGRs/testing only
"UDPdisconnect" ; for MGRs/testing only
"UDPbaseDelay" ; PARAM = delay in ms; adjust target's $_POOL_STATUS_UDP_BASEDELAY
"UDPstepDelay" ; PARAM = delay in ms; adjust target's $_POOL_STATUS_UDP_STEPDELAY
"UseTCPforDataTransfers" ; switch to TCP packets per target MGR for shipping Containers
"UseUDPforDataTransfers" ; switch to UDP packet broadcasting for shipping Containers


 
Pool is big. It has got many dozens of fixed and dynamic user-defined settings to create specific applications with (see #Region Globals). Please take the time to read the extensive Remarks section at the top of the main script (Pool.au3), including the warnings and limitations. Furthermore, I've provided two example environments in the subdir Examples in the bundle: CLNtest (messages and remote execution) and CLNmaster + CLNslave (data Container shipping). If you run these on a single machine, you'll need to start PostOffice_Solo; on multiple machines, one should start first with PostOffice_Server, the rest with PostOffice_Client, once the Server is initialised. See the readme.txt in the Examples subdir for additional help. Another example (multi-processing a la Condor) is shipped with release 2.4+ of Eigen4AutoIt.

Since this is an Example script, you can find a number of interesting design ideas in Pool:

 

 

* Containers can be retrieved without ever having been sent out first

* TODO list processing can defer tasks with task-specific retry-expiry

* the three main Pool 2D arrays (PML, PSL, PSC) are accessed by column Name, without having to know the column index (like a database)

* you can switch between TCP (host-targeted) and UDP (broadcast) for data transfers

* handling of subnet masks other than 255.255.255.0 (look for _CIDR_PREFIX)

* using a hexstring for checksum bitflags (see _Pool_PrepChecksum, _Pool_SetChecksum, etc)

* TCP socket buffer + event processing can handle stacked and partial messages

* resend data slice msgs use slice multiplexing with data-dependent granularity

 

 Pool.v0.7.7z Pool bundle, second beta release, version 0.7

:construction:   IMPORTANT CAVEAT  :construction:

This is an experimental beta release. Some parts of Pool have never been tested, others only in the simplest possible setting (the largest network I ever tested comprised one desktop and three laptops (one with busted radio) on a crappy Wifi router; I've never tested it on a cabled network). The (W)LAN part still has many issues and may not be able to handle more than a few machines. There's no callstack error handler. Event-driven TCP remains glitchy, and some of my MailSlots keep malfunctioning. I also lack access to Windows 8.* test environments.

In addition, many issues you may encounter will be due to specific timings/event sequencing (which makes it hard to debug) and/or your specific infrastructure. That means that, most likely, I cannot recreate your Pool bugs (even with your scripts), so I cannot fix them either. Therefore, I will not be offering bug support for users at this point; there's still far too much I need to fix in my own environment to worry about yours, I'm sorry (also, I've been working on this for four months flat, and need a break from it). However, I am of course open to suggestions, remarks, criticism, and kind words (preferably the latter ;) ).

Finally, I will be exceptionally busy with work for the foreseeable future, so it may take me more time than in the past to respond to your posts.

Have fun playing in the Pool.

Edited by RTFC
3 people like this

Share this post


Link to post
Share on other sites



Always the useful, RTFC you rule!!!

1 person likes this

AutoIt.4.Life Clubrooms - Life is like a Donut (secret key)

Spoiler

My contributions to the AutoIt Community

Some messages & Apologizes:

If I hurt you, Please accept my apologies, I never (regardless of the situation) mean to hurt anybody!!!

Also, I am very busy with my project so I will appear in the last row of the online list, if you want to contact me: Email@TheDcoder.xyz

Or you can have a nice chat with me in freenode, I use the same nick on freenode too!

3fHNZJ.gif

PLEASE JOIN ##AutoIt AND HELP THE IRC AUTOIT COMMUNITY!

Share this post


Link to post
Share on other sites

High five! :thumbsup:


AutoIt.4.Life Clubrooms - Life is like a Donut (secret key)

Spoiler

My contributions to the AutoIt Community

Some messages & Apologizes:

If I hurt you, Please accept my apologies, I never (regardless of the situation) mean to hurt anybody!!!

Also, I am very busy with my project so I will appear in the last row of the online list, if you want to contact me: Email@TheDcoder.xyz

Or you can have a nice chat with me in freenode, I use the same nick on freenode too!

3fHNZJ.gif

PLEASE JOIN ##AutoIt AND HELP THE IRC AUTOIT COMMUNITY!

Share this post


Link to post
Share on other sites

I was given the impression of late that Pool.au3 looks too complicated to engage with, so I thought I'd post one of the bundled examples, to show that it's actually really simple to use.

A Pool script consists of a few steps:

  1. define what type of Pool entity the script is going to be (a Post Office, client, master, slave...)
  2. call _Pool_StartUp()
  3. perform pool actions with _Pool_Send_Command ( $targetID, <pool command>, see full listing in Pool.au3 Remarks section)
  4. call _Pool_CloseDown()

Per machine, you need one Post Office script (already running), and as many Pool clients as you like.

Furthermore- if you wish to send data from one script to another (on the same machine or on a network), you additionally need to:

  1. create a shipping container for it (with _Pool_Container_Create());
  2. define a "Share" relationship for the Container, that is, tell Pool where it needs to go (with _Pool_Container_CreateShare()); and
  3. load the Container with data and send it, with _Pool_Send_Command ( $containerID, "LoadSendContainer")

That's basically it.

 

So here's a simple Pool client script (included in the bundle) that communicates with other Pool clients (start at least two instances of this), and which can send and remotely execute AutoIt calls at the destination. Below it you'll find a Post Office script that handles all comms between clients on a machine (with thanks to Melba23 for auxiliary function _MemoWrite).

; POOL Messaging & ExeQueue Example
;
; Run one instance of PostOffice.exe per machine, and two or more instances of this script
;
;____________________________________________________________________________

#NoTrayIcon
#include "Pool.au3"

; set attributes (fixed)
$_POOL_ATTRIB_CLASS         ="Any"  ; cannot be empty; should match Pool MGR's Class
$_POOL_ATTRIB_OPERATOR      ="CLN"  ; CLN = main app/master/slave/collaborator/parent/child...
$_POOL_ATTRIB_SHAREDATA     =False  ; not tested here
$_POOL_ATTRIB_COLLECTOR     =True       ; keep PML up to date (for master/parent/collaborator)
$_POOL_ATTRIB_LAUNCH_MGR    =True       ; T: allow starting a new MGR instance ourselves
$_POOL_ATTRIB_KEY               ="<replace this string with your own encryption key in your apps>"
$_POOL_ATTRIB_MGR_DEFAULT   ="PostOffice_Solo.exe"  ; default MGR to launch if none found locally

; NB use these if testing on a (W)LAN (NB use one _Server, rest _Client)
;$_POOL_ATTRIB_MGR_DEFAULT  ="PostOffice_Server.exe"    ; default MGR to launch if none found locally
;$_POOL_ATTRIB_MGR_DEFAULT  ="PostOffice_Client.exe"    ; default MGR to launch if none found locally

; disable relevant timeouts
$_POOL_ATTRIB_TIMEOUT_CLN_SOLO=-1       ; positive value = msec without MGR before leaving Pool
$_POOL_ATTRIB_TIMEOUT_INACTIVE=-1       ; positive value = msec without msg/mail/TODOlist before leaving Pool
$_POOL_STATUS_TIMEOUT_UNCONDITIONAL=-1  ; positive value = msec before leaving Pool, regardless of state

; set status vars (dynamic)
$_POOL_STATUS_TESTING           =True       ; write events to handler
$_POOL_STATUS_CHAT_ENABLED      =True       ; quack, quack, quack...
$_POOL_STATUS_CHAT_ALIAS        ="CHAT " & @ScriptName & " / " & @AutoItPID & " says: "
$_POOL_STATUS_EXEQ_ENABLED      =True       ; T: allow other CLNs to send and Execute ANY AutoIt call here
$_POOL_STATUS_EXEQ_ENCRYPTED    =True       ; strongly recommended for general use
$_POOL_STATUS_IGNORE_ERRORS     =False  ; F: break ExeQ execution upon errors
$_POOL_STATUS_CHAT_INCOMING_UDF =_MemoWrite ; for this demo; write your own handler
$_POOL_STATUS_TESTING_UDF       =_MemoWrite ; for this demo; write your own handler
$_POOL_STATUS_STAYALIVE         =False  ; F: terminate when leaving Pool
$_POOL_STATUS_BEEP              =True       ; make some noise when pinged

; join Pool
_Pool_StartUp()

; variables/data for testing
$thisVarContained123=123                ; to be changed at destination by testbatch below
$targetID   =   $_POOL_BROADCAST        ; 0xffff = broadcast to all CLNs
$chatmsg    =   "Blah blah blah..." ; NB max.size=424 chars for long-distance chatmsgs (unencrypted)
$testCall   =   "Msgbox(0,'Test Call from PID " & @AutoItPID & " on " & @ComputerName & "','This MsgBox appears courtesy of ExeQ, running on PID: ' & @AutoItPID,5)"
$testBatch  =   "Beep(200,50)" & @CR & _
                    "Assign('thisVarContained123',456,2)" & @CR & _
                    "Beep(400,50)" & @CR & _
                    "Beep(800,50)" & @CR & _
                    "Beep(1600,50)" & @CR & _   ; should really be using asynchronous beep, because Beep() is a blocking function
                    "Beep(3200,50)" & @CR & _   ; but we get away with it here because it's only 50 ms per beep
                    "Run('Notepad.exe')" & @CR & _  ; never use RunWait in ExeQ!
                    "_TestUDF()"

; display status msgs and some action buttons
Global $hGUI=GUICreate("Msg/Mail/ExeQ Test CLN PID: " & @AutoItPID, 400, 410)
Global $hEdit=GUICtrlCreateEdit("Please wait for Post Office to connect..."  & @CRLF & @CRLF, 119, 10, 276, 390, BitOR($WS_VSCROLL,$ES_READONLY))
GUICtrlSetFont($hEdit, 9, 400, 0, "Courier New")

$buttonwidth=100
$ButtonPing         =GUICtrlCreateButton("Ping", 10, 10, $buttonwidth, 25)
GUICtrlSetState(-1,$GUI_DISABLE)
$ButtonStandby      =GUICtrlCreateButton("Standby", 10, 40, $buttonwidth, 25)
GUICtrlSetState(-1,$GUI_DISABLE)
$ButtonProceed      =GUICtrlCreateButton("Proceed", 10, 70, $buttonwidth, 25)
GUICtrlSetState(-1,$GUI_DISABLE)
$ButtonMailTest =GUICtrlCreateButton("Mail Test", 10, 100, $buttonwidth, 25)
GUICtrlSetState(-1,$GUI_DISABLE)
$ButtonChat         =GUICtrlCreateButton("Send Chat Msg", 10, 130, $buttonwidth, 25)
GUICtrlSetState(-1,$GUI_DISABLE)
$ButtonExeQline =GUICtrlCreateButton("Send ExeQ Line", 10, 160, $buttonwidth, 25)
GUICtrlSetState(-1,$GUI_DISABLE)
$ButtonExeQbatch    =GUICtrlCreateButton("Send ExeQ Batch", 10, 190, $buttonwidth, 25)
GUICtrlSetState(-1,$GUI_DISABLE)
$ButtonRunNext      =GUICtrlCreateButton("Run next line", 10, 220, $buttonwidth, 25)
GUICtrlSetState(-1,$GUI_DISABLE)
$ButtonRunLast      =GUICtrlCreateButton("Rerun last Line", 10, 250, $buttonwidth, 25)
GUICtrlSetState(-1,$GUI_DISABLE)
$ButtonRunBatch =GUICtrlCreateButton("Run Batch", 10, 280, $buttonwidth, 25)
GUICtrlSetState(-1,$GUI_DISABLE)
$ButtonExeQclear    =GUICtrlCreateButton("Clear ExeQ", 10, 310, $buttonwidth, 25)
GUICtrlSetState(-1,$GUI_DISABLE)

; always enabled
$ButtonShowPML      =GUICtrlCreateButton("Show PML", 10, 340, $buttonwidth, 25)
$ButtonQuit         =GUICtrlCreateButton("Quit", 10, 370, $buttonwidth, 25)

; GUI presets
$firsttime=True
$linesent=False
$batchsent=False
$ExeQcalled=False
$buttonsEnabled=False

GUISetState(@SW_SHOW,$hGUI)
While True
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE,$ButtonQuit
         ExitLoop

        Case $ButtonShowPML ; PML = Pool Member List
            _ArrayDisplay($_Pool_MemberList,"Pool Member List")

        ; remaining controls are enabled only if at least two CLNs are registered
        Case $ButtonPing
            _Pool_Send_Command($targetID,"Ping")

        Case $ButtonStandby
            _Pool_Send_Command($targetID,"StandBy")

        Case $ButtonProceed
            _Pool_Send_Command($targetID,"Proceed")

        Case $ButtonMailTest
            _Pool_Send_Command($targetID,"MailTest")

        Case $ButtonChat
            _Pool_Send_ChatMsg($targetID,$chatmsg)

        Case $ButtonExeQline
            _MemoWrite("Please be patient; this may take a few seconds to arrive at the destination(s)..." & @CRLF)
            _Pool_Send_ExeQcall($targetID,$testCall)
            $linesent=True
            $buttonsEnabled=False

        Case $ButtonExeQBatch
            _MemoWrite("Please be patient; this may take a few seconds to arrive at the destination(s)..." & @CRLF)
            _Pool_Send_ExeQcall($targetID,$testBatch)
            $batchsent=True
            $buttonsEnabled=False

        Case $ButtonRunNext
            _Pool_Send_Command($targetID,"RunNext")
            $ExeQcalled=True
            $buttonsEnabled=False

        Case $ButtonRunLast
            _Pool_Send_Command($targetID,"RepeatLast")

        Case $ButtonRunBatch
            _Pool_Send_Command($targetID,"RunBatch")
            $ExeQcalled=True
            $buttonsEnabled=False

        Case $ButtonExeQclear
            _Pool_Send_Command($targetID,"ExeQClear")
            $linesent=False
            $batchsent=False
            $ExeQcalled=False
            $buttonsEnabled=False
    EndSwitch

    ; multiple clients + registered by MGR = buttons enabled
    If $_POOL_STATUS_ID>0 And $_POOL_STATUS_TOTAL_ALLCLIENTS>0 Then
        If $buttonsEnabled=False Then
            GUICtrlSetState($ButtonPing     , $GUI_ENABLE)
            GUICtrlSetState($ButtonStandby  , $GUI_ENABLE)
            GUICtrlSetState($ButtonProceed  , $GUI_ENABLE)
            GUICtrlSetState($ButtonMailTest , $GUI_ENABLE)
            GUICtrlSetState($ButtonChat     , $GUI_ENABLE)
            GUICtrlSetState($ButtonExeQline , $GUI_ENABLE)
            GUICtrlSetState($ButtonExeQbatch    , $GUI_ENABLE)
            GUICtrlSetState($ButtonExeQclear    , $GUI_ENABLE)

            If $batchsent=True Then
                GUICtrlSetState($ButtonRunBatch,$GUI_ENABLE)
            Else
                GUICtrlSetState($ButtonRunBatch,$GUI_DISABLE)
            EndIf

            If $batchsent=True Or $linesent=True Then
                GUICtrlSetState($ButtonRunNext,$GUI_ENABLE)
            Else
                GUICtrlSetState($ButtonRunNext, $GUI_DISABLE)
            EndIf

            If $ExeQcalled=True Then
                GUICtrlSetState($ButtonRunLast,$GUI_ENABLE)
            Else
                GUICtrlSetState($ButtonRunLast,$GUI_DISABLE)
            EndIf

            $buttonsEnabled=True
            If $firsttime=True Then
                _MemoWrite("Ready for testing." & @CRLF)
                If Mod($_POOL_STATUS_ID,2)=0 Then
                    WinMove($hGUI,"",5,5,Default,Default,3)
                Else
                    WinMove($hGUI,"",@DesktopWidth-405,5, Default,Default,3)
                EndIf
                $firsttime=False
            EndIf
        EndIf
    Else
        If $buttonsEnabled=True Then
            GUICtrlSetState($ButtonPing     , $GUI_DISABLE)
            GUICtrlSetState($ButtonStandby  , $GUI_DISABLE)
            GUICtrlSetState($ButtonProceed  , $GUI_DISABLE)
            GUICtrlSetState($ButtonMailTest , $GUI_DISABLE)
            GUICtrlSetState($ButtonChat     , $GUI_DISABLE)
            GUICtrlSetState($ButtonExeQbatch    , $GUI_DISABLE)
            GUICtrlSetState($ButtonExeQclear    , $GUI_DISABLE)
            GUICtrlSetState($ButtonExeQline , $GUI_DISABLE)
            GUICtrlSetState($ButtonRunBatch , $GUI_DISABLE)
            GUICtrlSetState($ButtonRunNext  , $GUI_DISABLE)
            GUICtrlSetState($ButtonRunLast  , $GUI_DISABLE)
            $buttonsEnabled=False
        EndIf
    EndIf
WEnd
GUIDelete($hGUI)

_Pool_CloseDown()



; http://www.autoitscript.com/forum/topic/110948-add-text-to-edit-box-and-scroll-it-down/, post #2; Thanks, Melba23!
Func _MemoWrite($sMessage)
; display status updates in GUI

    ConsoleWrite($sMessage)

    If Not IsDeclared("hEdit") Then Return

   Local $iEnd = StringLen(GUICtrlRead($hEdit))
   _GUICtrlEdit_SetSel($hEdit, $iEnd, $iEnd)
    _GUICtrlEdit_Scroll($hEdit, $SB_SCROLLCARET)
    GUICtrlSetData($hEdit,$sMessage, 1)

EndFunc

Func _TestUDF()

        _MemoWrite("_TestUDF was called" & @CRLF & _
        "Updated $thisVarContained123 = " & $thisVarContained123 & @CRLF)

EndFunc

 

And the Post Office script:

; DO NOT USE THIS SCRIPT IN A (W)LAN POOL, but ONLY on a single machine

#NoTrayIcon
#include "Pool.au3"

; set some attributes (permanent)
$_POOL_ATTRIB_CLASS             ="Any"  ; cannot be empty; should match CLNs' Class in this Pool
$_POOL_ATTRIB_OPERATOR          ="MGR"  ; MGR = your friendly local Post Office
$_POOL_ATTRIB_SHAREDATA         =True   ; T: handle incoming/outgoing vars, arrays, structs
$_POOL_ATTRIB_LAUNCH_SERVER     =False  ; boolean, T: start new instance of Server if original one dies
$_POOL_ATTRIB_LAUNCH_CLN        =False  ; enable this for grid computing functionality

; disable relevant timeouts
$_POOL_ATTRIB_TIMEOUT_UNCONDITIONAL =-1 ; positive value = always Exit when time's up (in msec)
$_POOL_ATTRIB_TIMEOUT_SERVER_SOLO   =-1 ; positive value = msec without any TCP clients (MGRs on other machines) before closedown
$_POOL_ATTRIB_TIMEOUT_MGR_SOLO      =-1 ; positive value = msec without local CLNs before closedown
$_POOL_ATTRIB_TIMEOUT_INACTIVE      =-1 ; positive value = msec without msg/mail/TCP/UDP/TODOlist before closedown

; set some status variables
$_POOL_STATUS_MGR_SOLO          =True   ; T: no LAN support (much faster); use this if all your CLNs run on a single machine
$_POOL_STATUS_SERVER_CESTMOI    =False  ; T: start as TCP server (required only on LAN)
$_POOL_STATUS_TCP_ENABLED       =False  ; required only for event-driven LAN msgs
$_POOL_STATUS_UDP_ENABLED       =False  ; required only for LAN data Sharing

; set Pool-related status vars
$_POOL_STATUS_EXEQ_ENABLED      =False  ; no point enabling this for a MGR (plus potential security risk)
$_POOL_STATUS_CHAT_ENABLED      =False  ; no point enabling this for a MGR (no user)
$_POOL_STATUS_STAYALIVE         =False  ; F: terminate when leaving Pool
$_POOL_STATUS_TESTING           =True

; Intialisation may take several minutes on a large LAN,
;   but because we here set $_POOL_STATUS_MGR_SOLO=True, no LAN scan is performed.

; initialise
_Pool_StartUp()

If @Compiled Then
    ; Everything a MGR does is periodic or event-driven.
    While True
        Sleep(10000)
    WEnd

Else    ; user interface for testing
    $hGUI=GUICreate("MGR", 160, 160)

    ; define some buttons
    $ButtonShowPML      =GUICtrlCreateButton("Show PML", 20, 10, 115, 25)
    $ButtonShowPSL      =GUICtrlCreateButton("Show PSL", 20, 40, 115, 25)
    $ButtonShowPSC      =GUICtrlCreateButton("Show PSC", 20, 70, 115, 25)
    $ButtonQuit         =GUICtrlCreateButton("Quit", 20, 120, 115, 25)

    WinMove($hGUI,"",Default,5)
    GUISetState(@SW_SHOW)
    While True
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE,$ButtonQuit
                ExitLoop

            Case $ButtonShowPML ; PML = Pool Member List
                _ArrayDisplay($_Pool_MemberList,"Pool Member List (PML)")

            Case $ButtonShowPSL ; PSL = Pool Struct List (list of Containers)
                _ArrayDisplay($_Pool_Structlist,"Pool Container List")

            Case $ButtonShowPSC ; PSC = Pool Shared Containers List
                _ArrayDisplay($_Pool_SharedContainers,"Pool Shares List")
        EndSwitch
    WEnd
EndIf

_Pool_CloseDown()
Exit

 

The master/slave example scripts in the bundle show how to parse variables, arrays, strings, structs, etc. between scripts.

Hope it helps.:closedeyes:

PS this project is still in developmental limbo, so the example scripts and Remarks still constitute my only support for this, sorry.:(

1 person likes this

Share this post


Link to post
Share on other sites

it fails and without support I'll find myself writing one from scratch based on your code.

---------------------------
AutoIt Error
---------------------------
Line 4490  (File "G:\au3s\Pool.v0.6\Pool\Pool.au3"):
$_Pool_MemberList[$memberindex][_Pool_GetPMLcol("Mail Errors")]=0
$_Pool_MemberList[^ ERROR
Error: Variable used without being declared.
---------------------------
OK   
---------------------------

=(

Share this post


Link to post
Share on other sites

#7 ·  Posted (edited)

Happy New Year, argumentum.:D

I've got to hand it to you, you're good at finding bugs in my code.:> You could earn good money with that kind of skill, you know? And because it's a special day today, and I'm feeling exceptionally generous, I've uploaded a bug-fixed beta version 0,7, (to be honest, it was a trivial oversight on my part, patched in 10 seconds).

 

Edited by RTFC
1 person likes this

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

    • 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
    • mLipok
      By mLipok
      I looked at ScriptControl.
      I wonder if using this, can I do IPC solution in AutoIt. I also wonder whether some extent, ScirptControl can be compared to the use of LUA.  
      Below is what I have read so far.
      http://msdn.microsoft.com/en-us/library/windows/desktop/aa365574(v=vs.85).aspx
      http://support.microsoft.com/kb/184739/EN-US
      http://msdn.microsoft.com/en-us/library/aa227413(v=vs.60).aspx
      http://www.microsoft.com/en-us/download/details.aspx?id=1949
      '?do=embed' frameborder='0' data-embedContent>>
      '?do=embed' frameborder='0' data-embedContent>>
      '?do=embed' frameborder='0' data-embedContent>>
       
      btw.
      Please, share with other interesting links that according to you is worth reading on the subject (links not directly related to the AutoIt).
    • timmy2
      By timmy2
      The following is a script from Eukalyptus that plays an audio file and displays an FFT Visualization. Let's call it the "Child".
      My goal is to be able to close it AND to send volume-level change commands from a Parent program (script farther below). I'm trying to use WM_CopyData for IPC but it's not working for me. Part of my problem is I'm unsure about what's needed to initialize WM_CopyData in the Parent program -- if this is even necessary.  I realize I could use the MailSlot UDF for this but would like to give WM_CopyData a go first, based on Guinness's recommendation. Once I get WM_CopyData working I might even try to use it to close the Child program.
      Other issues I'm unsure about are:
      1. can I send decimal values via WM_CopyData_Send() ?  (BASS_SetVolume() needs values between 0 and 1)
      2. at the receiving end in the Child am I using the proper variable type and format for BASS_SetVolume()?
       
      "Child" (must be compiled and named "FFT-Viz-WM_CopyData" so Parent can run it)
      #AutoIt3Wrapper_UseX64=n #include "Bass.au3" #include "BassExt.au3" #include <GDIPlus.au3> #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include 'WM_COPYDATA.au3' Opt("MustDeclareVars", 1) _WM_COPYDATA_SetID('MyUniqueID') ; set ID for WM_CopyData Global $sFile = FileOpenDialog("Open...", "..\audiofiles", "playable formats (*.MP3;*.MP2;*.MP1;*.OGG;*.WAV;*.AIFF;*.AIF)") If @error Or Not FileExists($sFile) Then Exit Global $iWidth = 522 Global $iHeight = 170 Global $hGui = GUICreate("FFT", $iWidth, $iHeight, -1, -1, BitOR($WS_SYSMENU, $WS_POPUP)) GUISetOnEvent($GUI_EVENT_CLOSE, "_EXIT") _GDIPlus_Startup() Global $hGraphics = _GDIPlus_GraphicsCreateFromHWND($hGui) Global $hBmp_Buffer = _GDIPlus_BitmapCreateFromScan0($iWidth, $iHeight) Global $hGfx_Buffer = _GDIPlus_ImageGetGraphicsContext($hBmp_Buffer) _GDIPlus_GraphicsSetSmoothingMode($hGfx_Buffer, 2) Global $hBrush = _CreateFFTBrush(5, 5, $iWidth - 10, $iHeight - 10) GUIRegisterMsg($WM_PAINT, "WM_PAINT") GUISetState() _BASS_Startup() _BASS_EXT_Startup() _BASS_Init(0, -1, 44100, 0, "") Global $hStream = _BASS_StreamCreateFile(False, $sFile, 0, 0, $BASS_SAMPLE_FLOAT) Global $aFFT = _BASS_EXT_CreateFFT(128, 5, 5, $iWidth - 10, $iHeight - 10, 1, True) _BASS_ChannelPlay($hStream, True) Local $iControlID = _WM_COPYDATA_Start($hGui) ; Start the communication process. Global $iTimer = TimerInit() While _BASS_ChannelIsActive($hStream) If TimerDiff($iTimer) > 20 Then Switch GuiGetMsg() Case -3 ; allows Parent program to use WinClose to close this program. ExitLoop Case $iControlID ; if the Parent program sends a volume change value via WM_CopyData Local $volumelevel = _WM_COPYDATA_GetData() _BASS_SetVolume($volumelevel) Beep(1000, 100) ; for testing purposes to see if this Case ever gets triggered EndSwitch $iTimer = TimerInit() _GDIPlus_GraphicsClear($hGfx_Buffer, 0xFF110022) _BASS_EXT_ChannelGetFFT($hStream, $aFFT, 2) If Not @error Then DllCall($ghGDIPDll, "int", "GdipFillPolygon", "handle", $hGfx_Buffer, "handle", $hBrush, "ptr", $aFFT[0], "int", $aFFT[1], "int", "FillModeAlternate") _GDIPlus_GraphicsDrawImage($hGraphics, $hBmp_Buffer, 0, 0) EndIf WEnd _Exit() Func _CreateFFTBrush($iX, $iY, $iWidth, $iHeight) Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iWidth, $iHeight) Local $hContext = _GDIPlus_ImageGetGraphicsContext($hBitmap) Local $hBrush_FFT = _GDIPlus_LineBrushCreate(0, 0, 0, $iHeight, 0, 0, 3) Local $aColors[5][2] = [[4]] $aColors[1][0] = 0xFFFF0000 $aColors[1][1] = 0 $aColors[2][0] = 0xFFFFAA00 $aColors[2][1] = 0.25 $aColors[3][0] = 0xFF00AAFF $aColors[3][1] = 0.5 $aColors[4][0] = 0xFF00AAFF $aColors[4][1] = 1 _GDIPlus_LineBrushSetPresetBlend($hBrush_FFT, $aColors) Local $hBrush_LED = _GDIPlus_LineBrushCreate(0, 0, 0, 4, 0xAA000000, 0x00000000, 0) _GDIPlus_GraphicsFillRect($hContext, 0, 0, $iWidth, $iHeight, $hBrush_FFT) _GDIPlus_GraphicsFillRect($hContext, 0, 0, $iWidth, $iHeight, $hBrush_LED) _GDIPlus_BrushDispose($hBrush_LED) _GDIPlus_BrushDispose($hBrush_FFT) _GDIPlus_GraphicsDispose($hContext) Local $hBrush = _GDIPlus_TextureCreate($hBitmap) _GDIPlus_BitmapDispose($hBitmap) DllCall($ghGDIPDll, "uint", "GdipTranslateTextureTransform", "hwnd", $hBrush, "float", $iX, "float", $iY, "int", 0) Return $hBrush EndFunc ;==>_CreateFFTBrush Func WM_PAINT($hWnd, $uMsgm, $wParam, $lParam) _GDIPlus_GraphicsDrawImage($hGraphics, $hBmp_Buffer, 0, 0) Return $GUI_RUNDEFMSG EndFunc ;==>WM_PAINT ; #FUNCTION# ==================================================================================================================== ; Author ........: Authenticity ; Modified.......: UEZ ; =============================================================================================================================== Func _GDIPlus_BitmapCreateFromScan0($iWidth, $iHeight, $iPixelFormat = $GDIP_PXF32ARGB, $iStride = 0, $pScan0 = 0) Local $aResult = DllCall($ghGDIPDll, "uint", "GdipCreateBitmapFromScan0", "int", $iWidth, "int", $iHeight, "int", $iStride, "int", $iPixelFormat, "ptr", $pScan0, "handle*", 0) If @error Then Return SetError(@error, @extended, 0) If $aResult[0] Then Return SetError(10, $aResult[0], 0) Return $aResult[6] EndFunc ;==>_GDIPlus_BitmapCreateFromScan0 ; #FUNCTION# ==================================================================================================================== ; Author ........: Authenticity ; Modified.......: UEZ ; =============================================================================================================================== Func _GDIPlus_LineBrushCreate($nX1, $nY1, $nX2, $nY2, $iARGBClr1, $iARGBClr2, $iWrapMode = 0) Local $tPointF1, $tPointF2, $aResult $tPointF1 = DllStructCreate("float;float") $tPointF2 = DllStructCreate("float;float") DllStructSetData($tPointF1, 1, $nX1) DllStructSetData($tPointF1, 2, $nY1) DllStructSetData($tPointF2, 1, $nX2) DllStructSetData($tPointF2, 2, $nY2) $aResult = DllCall($ghGDIPDll, "int", "GdipCreateLineBrush", "struct*", $tPointF1, "struct*", $tPointF2, "uint", $iARGBClr1, "uint", $iARGBClr2, "int", $iWrapMode, "handle*", 0) If @error Then Return SetError(@error, @extended, 0) If $aResult[0] Then Return SetError(10, $aResult[0], 0) Return $aResult[6] EndFunc ;==>_GDIPlus_LineBrushCreate ; #FUNCTION# ==================================================================================================================== ; Author ........: Authenticity ; Modified.......: UEZ ; =============================================================================================================================== Func _GDIPlus_LineBrushSetPresetBlend($hLineGradientBrush, $aInterpolations) Local $iI, $iCount, $tColors, $tPositions, $aResult $iCount = $aInterpolations[0][0] $tColors = DllStructCreate("uint[" & $iCount & "]") $tPositions = DllStructCreate("float[" & $iCount & "]") For $iI = 1 To $iCount DllStructSetData($tColors, 1, $aInterpolations[$iI][0], $iI) DllStructSetData($tPositions, 1, $aInterpolations[$iI][1], $iI) Next $aResult = DllCall($ghGDIPDll, "int", "GdipSetLinePresetBlend", "handle", $hLineGradientBrush, "struct*", $tColors, "struct*", $tPositions, "int", $iCount) If @error Then Return SetError(@error, @extended, False) If $aResult[0] Then Return SetError(10, $aResult[0], False) Return True EndFunc ;==>_GDIPlus_LineBrushSetPresetBlend ; #FUNCTION# ==================================================================================================================== ; Author ........: Authenticity ; Modified.......: UEZ ; =============================================================================================================================== Func _GDIPlus_TextureCreate($hImage, $iWrapMode = 0) Local $aResult = DllCall($ghGDIPDll, "int", "GdipCreateTexture", "handle", $hImage, "int", $iWrapMode, "handle*", 0) If @error Then Return SetError(@error, @extended, 0) If $aResult[0] Then Return SetError(10, $aResult[0], 0) Return $aResult[3] EndFunc ;==>_GDIPlus_TextureCreate Func _Exit() _BASS_StreamFree($hStream) _BASS_Free() _WM_COPYDATA_Shutdown() _GDIPlus_BrushDispose($hBrush) _GDIPlus_GraphicsDispose($hGfx_Buffer) _GDIPlus_BitmapDispose($hBmp_Buffer) _GDIPlus_GraphicsDispose($hGraphics) _GDIPlus_Shutdown() Exit EndFunc ;==>_Exit And here's the Parent program, which you can run from SciTE.
      #include "ExtMsgBox.au3" #Include <WinAPIEx.au3> #include 'WM_COPYDATA.au3' ;_WM_COPYDATA_SetID('MyUniqueIDC') ; NOT SURE IF I NEED THIS ;Local $iControlID = _WM_COPYDATA_Start(Default) ; Start the communication process. NOT SURE IF I NEED THIS $wPID = run ("FFT-Viz-WM_CopyData.exe") sleep(2000) _ExtMsgBox(0,0,"","press enter to reduce volume",0,1,1) _WM_COPYDATA_Send(.2) ;can WM_CopyData send values like this or must they be strings? _ExtMsgBox(0,0,"","press enter to raise volume",0,1,1) _WM_COPYDATA_Send(1) _ExtMsgBox(0,0,"","press enter to exit",0,1,1) $aData = _WinAPI_EnumProcessWindows ($wPID) ConsoleWrite($aData[1][0] & @LF) Sleep(2000) $result = WinClose($aData[1][0]) ConsoleWrite ("winclose issued! Result = " & $result & @LF) sleep (2000) ;_WM_COPYDATA_Shutdown() ;NOT SURE IF I NEED THIS Exit