; #Include <OnWin>   ; 好的!但不幸的是,它不适用于已编译的 exe。
#Persistent

; 示例:计算器存在后,3秒后关闭
OnWin("Exist", "计算器", Func("C"))
Sleep, 1000
Run, calc
Loop
    ToolTip 持续运算测试
return
 
C(this) {
  static window
  event := this.Event, window := this.WinTitle
  MsgBox, Event: %event%`nWindow: %window%
  OnWin("Close", window, Func("X"))
  SetTimer, close, -3000
  return
close:
  WinClose, %window%
  return
}
 
X(this) {
  event := this.Event, window := this.WinTitle
  MsgBox, Event: %event%`nWindow: %window%
  SetTimer, exit, -1 ; allow function to return
  return
exit:
  ExitApp
}

; https://www.autohotkey.com/boards/viewtopic.php?f=6&t=6463

/**
 * Function: OnWin
 *     Specifies a function to call when the specified window event for the
 *     specified window occurs.
 * Version:
 *     v1.1.00.02
 * License:
 *     WTFPL [http://wtfpl.net/]
 * Requirements:
 *     Latest stable version of AutoHotkey
 * Syntax:
 *     OnWin( Event, WinTitle, Callback [, ItemId := "" ] )
 * Parameter(s):
 *     Event           [in] - Window event to monitor. Valid values are: Exist,
 *                            Close, Show, Hide, Active, NotActive, Minimize,
 *                            Maximize.
 *     WinTitle        [in] - see http://ahkscript.org/docs/misc/WinTitle.htm
 *     Callback        [in] - "Function object" - see http://ahkscript.org/docs/objects/Functor.htm
 *     ItemId     [in, opt] - User-defined ID to associate with this particular
 *                            window event monitor.
 * Remarks:
 *     - Script must be #Include-ed(manually or automatically) and must not be
 *       copy-pasted into the main script.
 *     - OnWin() uses A_TitleMatchMode
 * Links:
 *     GitHub      - http://goo.gl/JfzFTh
 *     Forum Topic - http://goo.gl/sMufTt
 */
OnWin(args*)
{
  return __OnWinEvent(args*)
}
/**
 * Function: OnWin_Ex
 *     Sames as OnWin() above, however, a child process is spawned to perform the
 *     monitoring.
 * Syntax:
 *     OnWin_Ex( Event, WinTitle, Callback [, ItemId := "", ProcessName := "default" ] )
 * Parameter(s):
 *     Event             [in] - Same as that of OnWin()'s
 *     WinTitle          [in] - Same as that of OnWin()'s
 *     Callback          [in] - Same as that of OnWin()'s
 *     ItemId       [in, opt] - Same as that of OnWin()'s
 *     ProcessName  [in, opt] - User-defined ID to associate with this particular
 *                              child process. Subsequent call(s) to OnWin_Ex()
 *                              will push the window event monitor info into the
 *                              queue of this particular process unless a different
 *                              name/ID is specified.
 */
OnWin_Ex(args*)
{
  return __OnWinEvent(args*)
}
/**
 * Function: OnWin_Stop
 *     Disables monitoring for the particular window event monitor info item
 *     specified by the 'ItemId' parameter.
 * Syntax:
 *     OnWin_Stop( ItemId [, ProcessName ] )
 * Parameter(s):
 *     ItemId            [in] - a previously defined ItemId - see OnWin/OnWin_Ex
 *                              If explicitly blank(""), all window event monitor
 *                              are disabled.
 *     ProcessName  [in, opt] - a previously defined ProcessName - see OnWin_Ex
 */
OnWin_Stop(id, name*)
{
  return __OnWinEvent(id, name*)
}

; PRIVATE
__OnWinEvent(args*)
{
  static InProcess   := new __OnWinEvent.Watcher
  static SubProcesss := new __OnWinEvent.Server

  static level := A_AhkVersion<"2" ? -2 : -1
  command := Exception("", level).What ; prevent user from calling A_ThisFunc directly, overkill??
  if (command == "OnWin")
    return %InProcess%(args*)

  else if (command == "OnWin_Ex")
    return %SubProcesss%(args*)

  else if (command == "OnWin_Stop")
  {
    if ObjHasKey(args, 2)
      SubProcesss.Clients[args[2]].Stop(args[1])
    else
      InProcess.Stop(args[1])
  }
}

class __OnWinEvent ; namespace
{
  class Callable ; base object for custom "Function" objects
  {
    __Call(method, args*)
    {
      if IsObject(method)
        return this.Call(method, args*)
      else if (method == "")
        return this.Call(args*)
    }
  }
  
  class Watcher extends __OnWinEvent.Callable
  {
    Queue := []
    IsRunning := false
    Call(args*)
    {
      ObjPush(this.Queue, info := new __OnWinEvent.Info(args*))
      
      if !this.IsRunning
        this.Start()
    }

    Watch()
    {
      Loop, % ObjLength(this.Queue)
      {
        if this.Queue[1].Assert()
          ObjRemoveAt(this.Queue, 1)
        else
          ObjPush(this.Queue, ObjRemoveAt(this.Queue, 1))
      }

      if !this.IsRunning := ObjLength(this.Queue) ; update 'IsRunning' property
        this.Stop()
    }

    Start()
    {
      this.SetTimer(25)
      this.IsRunning := true
    }

    Stop(args*)
    {
      this.SetTimer("Off")

      if HasId := ObjLength(args)
      {
        id := args[1]
        if (id == "") && (len := ObjLength(this.Queue))
          ObjRemoveAt(this.Queue, 1, len)

        Loop, % ObjLength(this.Queue)
          if (this.Queue[A_Index].Id = id) ? ObjRemoveAt(this.Queue, A_Index) : 0
            break
      }

      this.SetTimer(HasId ? "On" : "Delete")
    }

    SetTimer(arg3)
    {
      static number := "number"
      if arg3 is %number%
      {
        if !timer := this.Timer
          timer := this.Timer := ObjBindMethod(this, "Watch")
        SetTimer, %timer%, %arg3%
      }
      
      else if (arg3 = "On" || arg3 = "Off")
      {
        timer := this.Timer
        SetTimer, %timer%, %arg3%
      }
      
      else if (arg3 = "Delete")
      {
        if timer := ObjDelete(this, "Timer")
          SetTimer, %timer%, Delete
      }
    }
  }

  class WatcherEx extends __OnWinEvent.Watcher
  {
    Host := "" ; value assigned by host
    Name := "" ; value assigned by host
    __Delete()
    {
      ExitApp
    }

    Stop(args*)
    {
      base.Stop(args*)
      
      if !ObjLength(args) ; usually called from the timer routine when the queue is empty
      {
        ; there are no longer any window events to monitor. Remove object
        ; reference from the host script's clients list, __Delete should be
        ; triggered automatically.
        this.Host.Ptr.Clients.Delete(this.Name)
        this.Host := ""
      }
    }
  }

  class Server extends __OnWinEvent.Callable
  {
    Clients := {}
    Call(event, WinTitle, CbProc, id:="", name:="default")
    {
      if !IsObject(this.Clients[name])
      {
        client := new __OnWinEvent.Client

        static q := Chr(34), quot := Func("Format").Bind("{1}{2}{1}", q)
        code := Format("
        ( LTrim Join`r`n Comments
        ComObjActive({1}).Proxy := new __OnWinEvent.WatcherEx
        return
        #NoTrayIcon
        #Persistent
        #Include {2}
        )"
        , quot.Call(client.Guid["Str"]), A_LineFile)

        try
        {
          WshExec := ComObjCreate("WScript.Shell").Exec(quot.Call(A_AhkPath) . " /ErrorStdOut *")
          WshExec.StdIn.Write(code)
          WshExec.StdIn.Close()
          while (WshExec.Status == 0) && !client.Proxy
            Sleep, 10
        }
        finally
          client.Revoke()

        client.Proxy.Host := new this.Ref(this) ; weak reference, allows 2-way communication
        client.Proxy.Name := name
        this.Clients[name] := client.Proxy
        client := ""
      }

      remote := this.Clients[name]
      return %remote%(event, WinTitle, CbProc, id, A_TitleMatchMode)
    }

    class Ref
    {
      __New(self)
      {
        this.__Ptr := &self
      }

      Ptr[] ; allows client script(s) to retrieve a reference to the host object
      {
        get {
          if (pThis := this.__Ptr) && (NumGet(pThis + 0) == NumGet(&this))
            return Object(pThis)
        }
      }
    }

    __Delete() ; triggers on main script's exit
    {
      ; stop remote client script(s) if any
      for i, client in this.Clients
        client.SetTimer("Delete") ; force stop and remove any extra object references(timer's BoundFunc)
      this.Clients := "" ; should trigger client script(s) __Delete
    }
  }

  class Client
  {
    __New()
    {
      HR := DllCall("oleaut32\RegisterActiveObject", "Ptr", &this, "Ptr", this.Guid["Ptr"], "UInt", 0, "UInt*", hReg, "UInt")
      if (HR < 0)
        throw Exception("RegisterActiveObject() error", -1, Format("HRESULT: 0x{:x}", HR))
      this.__Handle := hReg
    }

    __Delete()
    {
      this.Revoke()
    }

    Revoke()
    {
      if hReg := this.__Handle
        DllCall("oleaut32\RevokeActiveObject", "UInt", hReg, "Ptr", 0)
      this.__Handle := 0
    }

    Guid[arg]
    {
      get {
        if !pGuid := ObjGetAddress(this, "_Guid")
        {
          ObjSetCapacity(this, "_Guid", 94)
          pGuid := ObjGetAddress(this, "_Guid")
          if ( DllCall("ole32\CoCreateGuid", "Ptr", pGuid) != 0 )
            throw Exception("Failed to create GUID", -1, Format("<at {1:p}>", pGuid))
          DllCall("ole32\StringFromGUID2", "Ptr", pGuid, "Ptr", pGuid + 16, "Int", 39)
        }
        return (arg="Ptr") ? pGuid : (arg="Str") ? StrGet(pGuid + 16, "UTF-16") : ""
      }
    }
  }

  class Info
  {
    __New(event, WinTitle, CbProc, id:="", tmm:=0) ; tmm used internally for client scripts
    {
      this.Event := event
      this.WinTitle := WinTitle
      this.Callback := CbProc
      this.TitleMatchMode := tmm ? tmm : A_TitleMatchMode
      if (id != "")
        this.Id := id
    }

    Assert()
    {
      event := this.Event
      WinTitle := this.WinTitle
      prev_TMM := A_TitleMatchMode
      this_TMM := this.TitleMatchMode
        SetTitleMatchMode, %this_TMM%

      prev_DWH := A_DetectHiddenWindows
      DetectHiddenWindows, On

      if (event = "Exist")
        r := WinExist(WinTitle)

      else if (event = "Close")
        r := !WinExist(WinTitle)
      
      else if (event = "Active")
        r := WinActive(WinTitle)

      else if (event = "NotActive")
        r := !WinActive(WinTitle)
      
      else if (event = "Show") || (event = "Hide" && hWnd := WinExist(WinTitle))
      {
        DetectHiddenWindows, Off
        r := (event="Show") ? WinExist(WinTitle) : !WinExist(WinTitle)
      }
      
      else if (event = "Minimize" || event = "Maximize")
      {
        static WINDOWPLACEMENT
        if !VarSetCapacity(WINDOWPLACEMENT)
          VarSetCapacity(WINDOWPLACEMENT, 44, 0), NumPut(44, WINDOWPLACEMENT, 0, "UInt") ; sizeof(WINDOWPLACEMENT)
        
        hWnd := WinExist(WinTitle)
        showCmd := event="Minimize" ? 2 : 3
        DllCall("GetWindowPlacement", "Ptr", hWnd, "Ptr", &WINDOWPLACEMENT)
        r := NumGet(WINDOWPLACEMENT, 8, "UInt") == showCmd
      }

      
      SetTitleMatchMode, %prev_TMM%
      DetectHiddenWindows %prev_DHW%
      
      if r
        this.Callback.Call(this)
      return r
    }
  }
}

 

声明:站内资源为整理优化好的代码上传分享与学习研究,如果是开源代码基本都会标明出处,方便大家扩展学习路径。请不要恶意搬运,破坏站长辛苦整理维护的劳动成果。本站为爱好者分享站点,所有内容不作为商业行为。如若本站上传内容侵犯了原著者的合法权益,请联系我们进行删除下架。