多进程共享函数通信

支持编译后,自己调用自身实现多进程

20220113更新1.2版【之前的版本有变量不共享的BUG】

/*
═════════════════════════════════════════════════════════════════
                            多进程共享函数、连锁关闭并通信

                         关联进程变量 := new Process("标签名称") 新建一个关联进程, 可传参
                         关联进程变量 := ""         清空这个 "关联进程变量" 来关闭对应的进程

     语法参考AHK_H对线程控制的用法,并新增相互通信和进程间互相控制的功能。支持编译运行
  新进程默认隐藏托盘图标和禁用热键来避免冲突,恢复热键和控制的方法参考以下示例的 F3 和 F4
═════════════════════════════════════════════════════════════════
*/
; #Include <ExecProcess>

; 受函数封闭性的影响,只能在脚本头部自行判断是否加载新进程,让新进程正常运行
if (A_Args[10]="演示新进程代码载入")
    Goto % A_Args[10]

Process1 := new Process("演示新进程代码载入", , , ,"value4") ; 带传参的开启新进程

Gosub 演示新进程代码载入 ; 复用共享标签和函数演示
Return

演示新进程代码载入:
Gui -MinimizeBox -MaximizeBox +AlwaysOnTop
Gui Add, Edit, w300 R2 v通信显示 g同步发送
Gui Show, % "x850 w330 y" ((A_Args[4]="Value4")?500:400), % ((A_Args[4]="Value4")?"F2新":"P主") "进程 - 互相通信同步演示,请输入文字"

Loop {
    Sleep 80
    MouseGetPos, x, y
    ToolTip % ((A_Args[4]="Value4")?"F2-新":"P-主") "进程持续运算演示-" A_Index, x+10, y-((A_Args[4]="Value4")?30:70)
}
Return

同步发送:
GuiControlGet, 获取编辑框内容,, 通信显示
if (A_Args[4]="Value4")
    ahkPostFunction( , "Gui同步更新", 获取编辑框内容, "通信显示") ; 第一个参数留空或者填"Parent"是给主进程发送信息
  else
    ahkPostFunction("演示新进程代码载入", "Gui同步更新", 获取编辑框内容, "通信显示")
Return

Gui同步更新(Value, ControlID) {
    GuiControl,, %ControlID%, %Value%
}

GuiClose:
; if (A_Args[9]="NewProcess")
    ExitApp
Return

MyLabel:
Menu Tray, Icon
; MsgBox, , , % "var变量值:" var, 2
Return

F1::
; ahkAssign("演示新进程代码载入", "var", "123456") ; 给新进程的变量赋值
ahkLabel("演示新进程代码载入", "MyLabel",1) ; 让新进程跳转至指定标签,并等待其完成才返回
if (onoff := !onoff)
    ahkPause("演示新进程代码载入") ; 暂停指定进程
  else
    ahkPause("演示新进程代码载入", "Off")
Return

; F2键做新进程的开、关、一键开关的演示【只有主进程端能够新建进程】
F2::
; Process1 := new Process("演示新进程代码载入") ; 不带传参的新建进程
Process1 := ""
; ProcessOnOff:=(Toggle:=!Toggle) ? new Process("演示新进程代码载入",,,,"Value4") : ""
Return

; ========== F3和F4为弥补措施, 按F3后F2结束新进程将无法启用, 需要按F4恢复给主进程才行 ==========

; 开启关联进程的热键,启用后会屏蔽主进程对应热键。屏蔽后,需要F4为主进程恢复热键
F3::ahkHotkey("演示新进程代码载入","Esc|F2|F3") ; 用 | 号分隔来添加多个热键

; ahkHotkey只能屏蔽日常大部分热键和组合键,会有组合键疏漏。若不了解, 则不推荐对新进程开启热键
F4::ahkRecoveryHotkey("演示新进程代码载入") ; 恢复主进程热键

Esc::ExitApp

; =============== 以下是多进程共享函数通信的类和函数 ===============

Class Process {  ; By dbgba    Thank FeiYue
    ; 关联进程变量 := new Process("标签名称") 新建一个关联进程,重复新建该进程变量可重启此进程。最多可传递8组参数
    __New(LabelOrFunc, Arg1:="", Arg2:="", Arg3:="", Arg4:="", Arg5:="", Arg6:="", Arg7:="", Arg8:="") {
        if (A_Args[9]="NewProcess")
            Return
        if A_IsCompiled
            Run "%A_ScriptFullPath%" /f "%Arg1%" "%Arg2%" "%Arg3%" "%Arg4%" "%Arg5%" "%Arg6%" "%Arg7%" "%Arg8%" "NewProcess" "%LabelOrFunc%",,, pid
         else
            Run "%A_AhkPath%" /f "%A_ScriptFullPath%" "%Arg1%" "%Arg2%" "%Arg3%" "%Arg4%" "%Arg5%" "%Arg6%" "%Arg7%" "%Arg8%" "NewProcess" "%LabelOrFunc%",,, pid
        this.pid:=pid
    }

    ; 关联进程变量 := "",清空这个“进程变量”来关闭对应的进程
    __Delete() {
        DetectHiddenWindows On   ; Logging Out Script
        PostMessage, 0x111, 65307,,, % A_ScriptFullPath " ahk_pid " this.pid
        Process Close, % this.pid
    }

    ; 为了新进程同步退出,所以新建进程会占用异步窗口切换监控钩子
    ShellEvent(wParam, lParam) {
        DetectHiddenWindows On
        IfWinNotExist <<ExecThreadParent>> ahk_class AutoHotkeyGUI
            ExitApp
    }

    Send(StringToSend, Label:="Parent", wParam:=0) {
        SetBatchLines % (bch:=A_BatchLines) ? "-1" : "-1"
        VarSetCapacity(CopyDataStruct, 3*A_PtrSize, 0)
        , NumPut((StrLen(StringToSend) + 1) * (A_IsUnicode ? 2 : 1), CopyDataStruct, A_PtrSize)
        , NumPut(&StringToSend, CopyDataStruct, 2*A_PtrSize)
        DetectHiddenWindows On
        WinGet, NewPID, PID, <<ExecThread%Label%>> ahk_class AutoHotkeyGUI
        SendMessage, 0x4a, wParam, &CopyDataStruct,, ahk_pid %NewPID% ahk_class AutoHotkey
        SetBatchLines %bch%
        Return ErrorLevel
    }

} ; // Class End


; 接收字符串后保存在变量名为"CopyOfData",以供调用
ExecThreadReceive_WM_COPYDATA(wParam, lParam) {
    CopyOfData := StrGet(NumGet(lParam + 2*A_PtrSize))
    Switch wParam
    {    ; wParam is the Thread class communication number
      case "1" : ahkThreadPostFunction(CopyOfData)
      case "2" : ahkThreadPostFunction(CopyOfData,1)
      case "3" :
        LabelName := StrReplace(SubStr(CopyOfData, 1, 50)," ")
        if !IsLabel(LabelName)
            Return False
        Gosub %LabelName%
      case "4" :
        LabelName := StrReplace(SubStr(CopyOfData, 1, 50)," ")
        if !IsLabel(LabelName)
            Return False
        ahkThreadPostLabel(LabelName)
      case "5" : ExecThreadvarName := StrReplace(SubStr(CopyOfData, 1, 50)," "), %ExecThreadvarName% := SubStr(CopyOfData, 51)
      case "6" : Global ExecThreadVarGetvarRtnVar := CopyOfData
      case "7" :
        ExecThreadVarNameRtn := StrReplace(SubStr(CopyOfData, 1, 50)," ")
        , Process.Send(%ExecThreadVarNameRtn%, , 6)
      case "8" : Process.Send(A_IsPaused, , 6)
      case "9" :
        SetBatchLines % (bch:=A_BatchLines) ? "-1" : "-1"
        Critical
        Suspend Off
        s:="||Home|End|Ins|Del|PgUp|PgDn|Left|Right|Up|Down|NumpadEnter|"
        Loop 254
            k:=GetKeyName(Format("VK{:X}",A_Index)), s.=InStr(s,"|" k "|") ? "" : k "|"
        For k,v in { Escape:"Esc", Control:"Ctrl", Backspace:"BS" }
            s:=StrReplace(s, k, v)
        s:=Trim(RegExReplace(s,"\|+","|"), "|")
        Loop, Parse, s, |
        { ; Ability to disable most hotkeys and combinations
            Hotkey %A_LoopField%, Off, UseErrorLevel
            Hotkey ~%A_LoopField%, Off, UseErrorLevel
            Hotkey ^%A_LoopField%, Off, UseErrorLevel
            Hotkey #%A_LoopField%, Off, UseErrorLevel
            Hotkey !%A_LoopField%, Off, UseErrorLevel
            Hotkey +%A_LoopField%, Off, UseErrorLevel
            Hotkey ^!%A_LoopField%, Off, UseErrorLevel
            Hotkey ^+%A_LoopField%, Off, UseErrorLevel
            Hotkey ^#%A_LoopField%, Off, UseErrorLevel
        }
        For k,v in StrSplit(CopyOfData, "|")
            Hotkey %v%, On
        Critical Off
        SetBatchLines %bch%
    }
    Return True
}

ScriptStart() {
    Static init:=ScriptStart()
    #NoTrayIcon
    SetBatchLines % (bch:=A_BatchLines) ? "-1" : "-1"
    OnMessage(0x4a, "ExecThreadReceive_WM_COPYDATA")
    if (A_Args[9]!="NewProcess") {
        Prev_DetectHiddenWindows=%A_DetectHiddenWindows%
        DetectHiddenWindows On
        PostMessage, 0x111, 65307,,, <<ExecThreadParent>> ahk_class AutoHotkeyGUI
        DetectHiddenWindows %Prev_DetectHiddenWindows%
        Menu Tray, Icon
        Gui Gui_Label_Gui: Show, Hide, <<ExecThreadParent>>
        SetBatchLines %bch%
        Return
    }
    Gui Gui_Label_Gui: Show, Hide, % "<<ExecThread" A_Args[10] ">>"
    Suspend On ; 屏蔽新进程的热键,来避免冲突
    DllCall("RegisterShellHookWindow", "Ptr", A_ScriptHwnd)
    , OnMessage(DllCall("RegisterWindowMessage", "Str", "ShellHook"), "Process.ShellEvent")
    SetBatchLines %bch%
}


; 让进程调用函数【等待函数执行完毕才返回】ahkFunction("演示新进程代码载入", "MyFunc", "Hello World!")
ahkFunction(ThreadLabel:="Parent", FuncName:="", Arg1:="", Arg2:="", Arg3:="", Arg4:="", Arg5:="", Arg6:="", Arg7:="", Arg8:="", Arg9:="", Arg10:="") {
    L := "ExecThreadFuncNameLabelArg", FuncNameArgs := Format("{:-50}", FuncName) . L "11" Arg1 L "31" L "12" Arg2 L "32" L "13" Arg3 L "33" L "14" Arg4 L "34" L "15" Arg5 L "35" L "16" Arg6 L "36" L "17" Arg7 L "37" L "18" Arg8 L "38" L "19" Arg9 L "39" L "20" Arg10 L "40End"
    , Rtn := Process.Send(FuncNameArgs, ThreadLabel, 1)
    Return Rtn
}

; 让进程调用函数【不等待函数执行完毕返回】ahkPostFunction("演示新进程代码载入", "MyFunc", "Hello World!")
ahkPostFunction(ThreadLabel:="Parent", FuncName:="", Arg1:="", Arg2:="", Arg3:="", Arg4:="", Arg5:="", Arg6:="", Arg7:="", Arg8:="", Arg9:="", Arg10:="") {
    L := "ExecThreadFuncNameLabelArg", FuncNameArgs := Format("{:-50}", FuncName) . L "11" Arg1 L "31" L "12" Arg2 L "32" L "13" Arg3 L "33" L "14" Arg4 L "34" L "15" Arg5 L "35" L "16" Arg6 L "36" L "17" Arg7 L "37" L "18" Arg8 L "38" L "19" Arg9 L "39" L "20" Arg10 L "40End"
    , Rtn := Process.Send(FuncNameArgs, ThreadLabel, 2)
    Return Rtn
}

; 让进程跳转至指定标签 ahkLabel("演示新进程代码载入", "MyLabel")
ahkLabel(ThreadLabel:="Parent", LabelName:="", DoNotWait:=0) {
    if DoNotWait
        Rtn := Process.Send(Format("{:-50}", LabelName), ThreadLabel, 3)
      else
        Rtn := Process.Send(Format("{:-50}", LabelName), ThreadLabel, 4)
    Return Rtn
}

; 给进程的变量赋值:ahkAssign("演示新进程代码载入", "var", "123456")
ahkAssign(ThreadLabel:="Parent", VarName:="", Value:="") {
    Rtn := Process.Send(Format("{:-50}", VarName) Value, ThreadLabel, 5)
    Return Rtn
}

; 返回进程中变量的内容:ahkGetvar("演示新进程代码载入","var")
ahkGetvar(ThreadLabel:="Parent", VarName:="") {
    Global ExecThreadVarGetvarRtnVar
    Process.Send(Format("{:-50}", VarName), ThreadLabel, 7)
    Return ExecThreadVarGetvarRtnVar
}

; 查看新进程运行状态:MsgBox % ahkReady("演示新进程代码载入")
ahkReady(ThreadLabel) {
    DetectHiddenWindows On
    Rtn := WinExist("<<ExecThread" ThreadLabel ">> ahk_class AutoHotkeyGUI") ? 1 : 0
    Return Rtn
}

; 暂停指定进程:ahkPause("演示新进程代码载入", "Off")
ahkPause(ThreadLabel, OnOff:="On") {
    Global ExecThreadVarGetvarRtnVar
    Process.Send("", ThreadLabel, 8) ; Return ExecThreadVarGetvarRtnVar
    DetectHiddenWindows On
    if (ExecThreadVarGetvarRtnVar=1) && (OnOff="Off")
        PostMessage, 0x111, 65306,,, <<ExecThread%ThreadLabel%>> ahk_class AutoHotkeyGUI
      else if (ExecThreadVarGetvarRtnVar=0) && (OnOff="On")
        PostMessage, 0x111, 65306,,, <<ExecThread%ThreadLabel%>> ahk_class AutoHotkeyGUI
}

; 启用指定进程的热键:ahkHotkey("演示新进程代码载入","Esc|F2|^1")
ahkHotkey(ThreadLabel, Hotkey) {
    if (ThreadLabel!="")
        For k,v in StrSplit(Hotkey, "|")
            Hotkey, %v%, Off
    ahkRecoveryHotkey(ThreadLabel, Hotkey)
    , Process.Send(Hotkey, ThreadLabel, 9)
}

; 记录与恢复主进程热键:ahkRecoveryHotkey("演示新进程代码载入")
ahkRecoveryHotkey(ThreadLabel, Hotkey:="") {
    Static Boolean
    if (A_Args[9]="NewProcess")
        Return
    if !Boolean
        Boolean:=[]
    if (Hotkey="") {
        For k,v in StrSplit(Boolean[ThreadLabel], "|")
            Hotkey %v%, On
        Return Boolean[ThreadLabel]
    }
    Boolean[ThreadLabel] := Hotkey
    Return Boolean[ThreadLabel]
}

; 临时新建进程【依赖AHK解释器】ahkExec("Loop{`nSleep 80`nToolTip test-%A_Index%`n}")
; 结束临时进程:ahkExec("")
ahkExec(NewCode:="", flag="Default") {
    if A_AhkPath {
        SetBatchLines % (bch:=A_BatchLines) ? "-1" : "-1"
        Critical
        DetectHiddenWindows On
        WinGet, NewPID, PID, <<ExecNew%flag%>> ahk_class AutoHotkeyGUI
        Process Close, % NewPID
        add=`nflag=<<ExecNew%flag%>>`n
        (%
        #NoTrayIcon
        Gui Gui_ExecFlag_Gui: Show, Hide, %flag%
        DllCall("RegisterShellHookWindow", "Ptr", A_ScriptHwnd)
        , OnMessage(DllCall("RegisterWindowMessage", "Str", "ShellHook"), "ShellEvent")
        ShellEvent() {
            DetectHiddenWindows On
            IfWinNotExist <<ExecThreadParent>> ahk_class AutoHotkeyGUI
                ExitApp
         }
        )
        NewCode:=add "`n" NewCode "`nExitApp"
        , exec := ComObjCreate("WScript.Shell").Exec(A_AhkPath " /ErrorStdOut /f *")
        , exec.StdIn.Write(NewCode)
        , exec.StdIn.Close()
        Critical Off
        SetBatchLines %bch%
        Return True
    }
    Return False
}

; 读取ahk脚本来新建临时新进程【依赖AHK解释器】ahkExecFile("NewScript.ahk")
ahkExecFile(FilePath:="", flag:="Default") {
    SplitPath, FilePath,,,,, drive
    if drive=
        FilePath := A_ScriptDir "\" FilePath
    FileRead, FileReadVar, %FilePath%
    ahkExec(FileReadVar, flag)
    Return True
}

ahkThreadPostFunction(CopyOfData, Synchronous:=0) {
    Global ExecThreadFunctionName := StrReplace(SubStr(CopyOfData, 1, 50)," "), ExecThreadFunctionArgs := []
    Loop 10
        ExecThreadFunctionArgs[A_Index] := RegExReplace(CopyOfData, "(^.+ExecThreadFuncNameLabelArg" A_Index+10 ")(.*)(ExecThreadFuncNameLabelArg" A_Index+30 ".+)", "$2")
    if !IsFunc(ExecThreadFunctionName)
        Return
    if Synchronous
        SetTimer ThreadPostFunctionSetTimer, -1
      else
        Goto ThreadPostFunctionSetTimer
    Return

    ThreadPostFunctionSetTimer:
    %ExecThreadFunctionName%(ExecThreadFunctionArgs*)
    Return
}

ahkThreadPostLabel(CopyOfData) {
    Global ExecThreadLabelName := CopyOfData
    if !IsLabel(ExecThreadLabelName)
        Return
    SetTimer ThreadPostLabelSetTimer, -1
    Return

    ThreadPostLabelSetTimer:
    Gosub %ExecThreadLabelName%
    Return
}

给TA打赏
共{{data.count}}人
人已打赏
AHKV1

右键双击,以非默认应用打开文件

2021-12-9 17:14:50

AHKV1

AHKv1文件或目录监控〔搬运〕

2021-12-10 12:55:28

5 条回复 A文章作者 M管理员
  1. 山重水复疑无路

    我是不是可以理解成,Global声明的全局变量,只在单个进程里有用?

    • dbgba

      对的,大致就是简易的操控其它进程。操控其它进程的方法是通过进程之间的通信在操作

  2. 蜜獾哥
    蜜獾哥给您打赏了¥5
  3. 1河许人
    1河许人给您打赏了¥2
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索