【问题标题】:AutoHotkey - Perform action based on clicked menu itemAutoHotkey - 根据单击的菜单项执行操作
【发布时间】:2017-01-06 08:01:35
【问题描述】:

我在 AHK 论坛、谷歌和这里查看了堆栈溢出,但找不到任何方法来解决这个问题,所以如果你能解决这个问题,请提前感谢 BIG TIME。我在示例中使用记事本,但我正在为复杂的销售点程序编写此脚本。

我想做一些与this 几乎完全一样的事情,但使用如下所述的应用程序菜单而不是按钮。

我想要一个脚本,如果单击某个菜单项,它会执行一个操作。这是否由菜单位置、标签或其他方式决定并不重要。但是,光标位置可能不适合我的需要。

一个简单的例子是在记事本中用鼠标选择 File-Open 时打开一个 MsgBox。

我已经实现了一些技术,使我能够在左键单击后从大多数控件对象(按钮、下拉菜单等)中提取变量,但是当我使用应用程序菜单时(例如,在记事本中:文件-save 或 file-open) 它返回一个空白字符串。例如:

#z::
  MouseGetPos,,,,Ctrl,2
  ControlGetText, Text,,ahk_id %Ctrl%  
  MouseClick, left
  Sleep 2500
  MsgBox, %Text%

(请注意,我使用 windows-z 以便在每次左键单击后不返回 MsgBox。)

如果在记事本的“文件另存为”对话框中按下取消按钮,将返回“取消”,但如果我在从应用程序菜单(文件、编辑、格式等)中选择任何内容时激活它,则不会返回任何内容

我知道 AHK 可以访问这些菜单,因为您可以执行以下操作,从记事本菜单中激活“文件-保存”:

WinMenuSelectItem, Untitled - Notepad,File,Save

但是当我使用 window spy 时,我看到的是菜单后面的对象,而不是菜单中实际存在的字符串。

这些菜单是否真的分开了我需要切换到活动才能阅读的窗口?还是我在尝试做一些不可能的事情?

提前非常感谢,这让我发疯了!

【问题讨论】:

    标签: variables menu click autohotkey


    【解决方案1】:

    从菜单中获取信息并不容易,至少不是使用 autohotkeys 内置命令,您需要使用 DllCall 来获取所需的信息。

    我给你做了一个例子,可以在 win7 64b 上为我使用记事本

    我在示例中所做的是查找记事本,如果它不存在则运行它然后获取记事本窗口的 ID (hWnd)

    当您按下 ctrl + LButton 时,使用记事本窗口 ID 对 GetMenu 的 DllCall 用于获取菜单句柄。

    之后,更多的 DllCall 用于获取 GetMenuItemCount、GetSubMenu 和 GetMenuItemInfo 等内容。

    它的作用是让我们一个接一个地遍历菜单项并获取有关它们的信息,在此示例中,我们获取项目的状态,特别是高亮状态。

    当找到具有突出显示状态的项目时,脚本会存储该项目的索引并中断循环。

    由于这只是一个示例,它所做的只是显示带有索引号的字符串消息并发送 LButton 按下

    示例:

    ifWinNotExist, ahk_class Notepad
    {
        Run, notepad.exe
        WinWait, ahk_class Notepad
    }
    WinGet, hWnd, ID, 
    
    
    ^Lbutton::
    
    hMenu :=DllCall("GetMenu", "Uint", hWnd)
    
    loop % (count := DllCall("GetMenuItemCount", "ptr", hMenu))
    {
        menu_index := A_index
    
        ; Note that menu item positions are zero-based.
        hSubMenu := DllCall("GetSubMenu", "Uint", hMenu, "int", (A_index-1))
    
        ; Set the capacity of mii to sizeof(MENUITEMINFO)
        VarSetCapacity(mii, A_PtrSize=8 ? (Size:=80) : (Size:=48), 0)
        ; Set the cbSize field to sizeof(MENUITEMINFO)
        NumPut(Size, mii, 0)
    
        ; Set the mask to whatever you want to retrieve.
        ; In this case I set it to MIIM_STATE=1.
        NumPut(1, mii, 4)
    
        loop % (subCount := DllCall("GetMenuItemCount", "ptr", hSubMenu))
        {
            Sub_index := A_index
    
            ; Note that menu item positions are zero-based.
            DllCall("GetMenuItemInfo", "UInt", hSubMenu, "UInt", (A_index-1), "UInt", 1, "UInt", &mii)
    
            ; Get the state field out of the struct.
            fState := NumGet(mii, 12)
    
            if (fState & 0x80) ; 0x80 MFS_HILITE | 0x8 MFS_CHECKED
            {
                MenuString := "Found in top #" menu_index " Sub pos #" Sub_index
                break 2
            }
        }
    }
    
    if (MenuString)
    {
        Tooltip % MenuString
    }
    else
    {
        tooltip no menu item highlighted
    }
    send {Lbutton}
    return
    

    我希望这可以帮助您了解要执行的操作所需的操作,其他可能要查看的 DllCall 是 MenuItemFromPoint、GetMenuString。

    这可能需要一些时间才能正确完成,但是对于我在此示例中展示的 Dllcall,我希望您可以在论坛上找到其他示例,说明如何使用它们与 autohotkey。

    【讨论】:

      【解决方案2】:
      • 这个 AutoHotkey 脚本应该可以满足您的要求。
      • 它已经在记事本 (Windows 7) 上进行了测试,应该可以很容易地适应其他使用标准上下文菜单的程序,这些程序仍然在绝大多数软件中使用。尽管许多程序使用自定义菜单栏,但上下文菜单通常仍然是标准上下文菜单。
      • 我提供了两个脚本,一个将显示工具提示并阻止在按下 任何 菜单项时触发菜单项,这有助于理解和诊断目的。 如果其文本字符串以“打开”开头,第二个脚本将显示工具提示并阻止触发任何菜单项。
      • 注意:记事本菜单项的文本是 Open... Ctrl+O (Open...[tab]Ctrl+O) 而不仅仅是“打开”。

      -

      ;==================================================
      
      ;tested on Notepad (Windows 7)
      ;block any clicks on any menu items when notepad is the active window
      ;note: to bypass this and click an item anyway, use ctrl+click
      
      ;note: requires Acc.ahk library in AutoHotkey\Lib folder
      ;https://github.com/Drugoy/Autohotkey-scripts-.ahk/blob/master/Libraries/Acc.ahk
      ;on right of screen right-click Raw, Save target as...
      
      #IfWinActive, ahk_class Notepad
      ;LButton::
      CoordMode, Mouse, Screen
      MouseGetPos, , , hWnd
      WinGetClass, vWinClass, ahk_id %hWnd%
      
      if vWinClass in #32768
      {
      vCount++ ;your code here
      ToolTip blocked %vCount%, 500, 200 ;your code here
      Return
      }
      
      SendInput {LButton Down}
      KeyWait, LButton
      
      MouseGetPos, vPosX, vPosY, hWnd
      WinGetClass, vWinClass, ahk_id %hWnd%
      
      if vWinClass in #32768
      {
      ControlSend, Dummy, {Click, 0, 0}, ahk_class Notepad
      
      vCount++ ;your code here
      ToolTip blocked %vCount%, 500, 200 ;your code here
      Return
      }
      
      SendInput {LButton Up}
      Return
      #IfWinActive
      
      ;==================================================
      
      ;tested on Notepad (Windows 7)
      ;block any clicks on the 'open' menu item when notepad is the active window
      ;note: to bypass this and click an item anyway, use ctrl+click
      
      ;note: requires Acc.ahk library in AutoHotkey\Lib folder
      ;https://github.com/Drugoy/Autohotkey-scripts-.ahk/blob/master/Libraries/Acc.ahk
      ;on right of screen right-click Raw, Save target as...
      
      #IfWinActive, ahk_class Notepad
      LButton::
      CoordMode, Mouse, Screen
      MouseGetPos, , , hWnd
      WinGetClass, vWinClass, ahk_id %hWnd%
      
      if vWinClass in #32768
      {
          vItemText := ""
          oAcc := Acc_Get("Object", "1", 0, "ahk_id " hWnd)
          Loop, % oAcc.accChildCount
          if (oAcc.accState(A_Index) & 0x80) ;MF_HILITE := 0x80
          if (1, vItemText := oAcc.accName(A_Index))
          break
      
          if (SubStr(vItemText, 1, 4) = "Open")
          {
          vCount++ ;your code here
          ToolTip blocked %vCount%, 500, 200 ;your code here
          Return
          }
      }
      
      SendInput {LButton Down}
      KeyWait, LButton
      
      MouseGetPos, vPosX, vPosY, hWnd
      WinGetClass, vWinClass, ahk_id %hWnd%
      
      if vWinClass in #32768
      {
      ControlSend, Dummy, {Click, 0, 0}, ahk_class Notepad
      
          vItemText := ""
          oAcc := Acc_Get("Object", "1", 0, "ahk_id " hWnd)
          Loop, % oAcc.accChildCount
          if (oAcc.accState(A_Index) & 0x80) ;MF_HILITE := 0x80
          if (1, vItemText := oAcc.accName(A_Index))
          break
      
          if (SubStr(vItemText, 1, 4) = "Open")
          {
          vCount++ ;your code here
          ToolTip blocked %vCount%, 500, 200 ;your code here
          Return
          }
      }
      
      SendInput {LButton Up}
      Return
      #IfWinActive
      
      ;==================================================
      

      注意:
      下面两个链接中贴出的代码示例,实现了相关目标。
      Is it possible to catch the close button and minimize the window instead? AutoHotKey
      AutoHotKey: Run code on Window Event (Close)

      注意:
      函数JEE_MenuIsActive,如下链接,可用于检查是否 菜单栏/系统菜单栏处于活动状态 为了区分标题栏菜单和右键单击上下文菜单。
      GUI 命令:完成重新思考 - AutoHotkey 社区
      https://autohotkey.com/boards/viewtopic.php?f=5&t=25893

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-10-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-13
        相关资源
        最近更新 更多