Что нового

Перечислить все контекстные меню по hWnd формы

Webarion

Осваивающий
Сообщения
143
Репутация
24
Друзья, нужна помощь. Нужно перечислить все контекстные меню формы, зная только hWnd окна.
Нужно узнать $FormContext, $FormContext2, только по $hForm. Ниже скрипт только для примера. Наличие переменных $FormContext.. не нужно брать в расчёт, так как на деле, UDF будет неизвестно, какие разработчик создаст меню, но их нужно перечислить, чтобы в последствии выполнить сервисное действие:
Код:
#include <ButtonConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <Array.au3>

$hForm = GUICreate("Form", 315, 125, 302, 218)

$FormContext = GUICtrlCreateContextMenu()
$MenuItem1 = GUICtrlCreateMenuItem("MenuItem1", $FormContext)
$MenuItem2 = GUICtrlCreateMenuItem("MenuItem2", $FormContext)
$MenuItem3 = GUICtrlCreateMenuItem("MenuItem3", $FormContext)
$Button1 = GUICtrlCreateButton("Button1", 8, 8, 73, 25)

$FormContext2 = GUICtrlCreateContextMenu($Button1)
$MenuItem1 = GUICtrlCreateMenuItem("MenuItem4", $FormContext2)
$MenuItem2 = GUICtrlCreateMenuItem("MenuItem5", $FormContext2)
$MenuItem3 = GUICtrlCreateMenuItem("MenuItem6", $FormContext2)

GUISetState(@SW_SHOW)


$aContextMenu = _EnumFormContext($hForm) ; ???

_ArrayDisplay($aContextMenu)

While 1
    $nMsg = GUIGetMsg()
    Switch $nMsg
        Case $GUI_EVENT_CLOSE
            Exit

    EndSwitch
WEnd

Сообщение автоматически объединено:

Пока остановился на WM_INITMENU, WM_INITMENUPOPUP. Не совсем то, что нужно, но под задачу пойдёт. Придётся обрабатывать на лету. Если у кого-то будут мысли, на счёт перечисления КМ, делитесь! С удовольствием посмотрел бы на реализацию.
 
Последнее редактирование:

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Пока остановился на WM_INITMENU, WM_INITMENUPOPUP
Я так понимаю это для своей же программы?
Тогда не вижу в этом смысла, ведь и так известно сколько и какие там меню созданы.
Если для чужой программы, то скорее всего придётся копаться в её ресурсах.
 
Автор
W

Webarion

Осваивающий
Сообщения
143
Репутация
24
Я так понимаю это для своей же программы?
Тогда не вижу в этом смысла, ведь и так известно сколько и какие там меню созданы.
Если для чужой программы, то скорее всего придётся копаться в её ресурсах.
Сервис, для разработчика, который создаёт свою программу в AutoIt. Есть необходимость максимально охватить автоматический перебор элементов окна. С контекстным меню, как раз загвоздка, пока WM_INITMENUPOPUP спасает в этом плане, но это работа на лету, что выходит из той логики, которую мне бы хотелось реализовать.
Можно конечно, предложить прописать ID ручками, но уже не так элегантно получится, как хотелось бы. Это крайний вариант.
 
Последнее редактирование:

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Как то так:

Код:
#include <ButtonConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <WinAPIDlg.au3>
#include <Array.au3>
#include <GUIMenu.au3>

$hForm = GUICreate("Form", 315, 125, 302, 218)

$FormContext = GUICtrlCreateContextMenu()
$MenuItem1 = GUICtrlCreateMenuItem("MenuItem1", $FormContext)
$MenuItem2 = GUICtrlCreateMenuItem("MenuItem2", $FormContext)
$MenuItem3 = GUICtrlCreateMenuItem("MenuItem3", $FormContext)
$Button1 = GUICtrlCreateButton("Button1", 8, 8, 73, 25)

$FormContext2 = GUICtrlCreateContextMenu($Button1)
$MenuItem1 = GUICtrlCreateMenuItem("MenuItem4", $FormContext2)
$MenuItem2 = GUICtrlCreateMenuItem("MenuItem5", $FormContext2)
$MenuItem3 = GUICtrlCreateMenuItem("MenuItem6", $FormContext2)

$aMenus = _GUICtrlMenu_EnumMenus($hForm)

$hFormContext = GUICtrlGetHandle($FormContext)
$hFormContext2 = GUICtrlGetHandle($FormContext2)

ConsoleWrite($FormContext & ' = ' & $aMenus[1][0] & '? ' & ($FormContext = $aMenus[1][0]) & @CRLF)
ConsoleWrite($FormContext2 & ' = ' & $aMenus[2][0] & '? ' & ($FormContext2 = $aMenus[2][0]) & @CRLF)

ConsoleWrite($hFormContext & ' = ' & $aMenus[1][1] & '? ' & ($hFormContext = $aMenus[1][1]) & @CRLF)
ConsoleWrite($hFormContext2 & ' = ' & $aMenus[2][1] & '? ' & ($hFormContext2 = $aMenus[2][1]) & @CRLF)

_ArrayDisplay($aMenus)
GUISetState(@SW_SHOW)

While 1
    $nMsg = GUIGetMsg()
  
    Switch $nMsg
        Case $GUI_EVENT_CLOSE
            Exit
    EndSwitch
WEnd

Func _GUICtrlMenu_EnumMenus($hWnd)
    GUICtrlCreateLabel('', -100, -100)
  
    Local $iLastCtrlID = _WinAPI_GetDlgCtrlID(GUICtrlGetHandle(-1))
    Local $aMenus[1][2] = [[0]]
  
    For $iID = 3 To $iLastCtrlID - 1
        If _GUICtrlMenu_IsMenu(GUICtrlGetHandle($iID)) Then
            $aMenus[0][0] += 1
            ReDim $aMenus[$aMenus[0][0] + 1][2]
          
            $aMenus[$aMenus[0][0]][0] = $iID
            $aMenus[$aMenus[0][0]][1] = GUICtrlGetHandle($iID)
        EndIf
    Next
  
    Return $aMenus
EndFunc


Но это только для меню, созданных с помощью нативных функции.
 
Автор
W

Webarion

Осваивающий
Сообщения
143
Репутация
24
CreatoR, ты гений! Ларчик то, просто открывался)
Решение интересное. Благодарю тебя.
 
Последнее редактирование:

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Немного переделал, на самом деле $hWnd не нужен, т.к нет способа проверить принадлежность к родительскому окну (контекстное меню создаётся как независимое окно).
Также убрал не нужное использование _WinAPI_GetDlgCtrlID, ведь GUICtrlCreateLabel и есть ID последнего элемента.

Код:
Func _GUICtrlMenu_EnumMenus()
    Local $iLastCtrlID = GUICtrlCreateLabel('', -100, -100)
    Local $hCtrl, $aMenus[1][2] = [[0]]
    
    For $iID = 3 To $iLastCtrlID - 1
        $hCtrl = GUICtrlGetHandle($iID)
        
        If _GUICtrlMenu_IsMenu($hCtrl) Then
            $aMenus[0][0] += 1
            ReDim $aMenus[$aMenus[0][0] + 1][2]
            
            $aMenus[$aMenus[0][0]][0] = $iID
            $aMenus[$aMenus[0][0]][1] = $hCtrl
        EndIf
    Next
    
    GUICtrlDelete($iLastCtrlID)
    Return SetError(($aMenus[0][0] = 0), 0, $aMenus)
EndFunc
 
Автор
W

Webarion

Осваивающий
Сообщения
143
Репутация
24
Понял идею. Очень круто и просто. И все созданные ID получить можно, помимо контекстного меню. А я то пытался использовать _WinAPI_EnumChildWindows, но контекстное меню это не возвращало.
Сообщение автоматически объединено:

Правда, это не сработает, если будет удалён хотя бы один из контролов, $iLastCtrlID будет иметь значение первого удалённого. Тогда все остальные не будут прочтены.
Сообщение автоматически объединено:

Грубый способ вычисления последнего элемента, при любых удалённых:
Открыт, для более элегантных предложений)
Код:
#include <ButtonConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <WinAPIDlg.au3>
#include <Array.au3>
#include <GUIMenu.au3>


$hForm = GUICreate("Form", 595, 125, 302, 218)

$FormContext = GUICtrlCreateContextMenu()
$MenuItem1 = GUICtrlCreateMenuItem("MenuItem1", $FormContext)
$MenuItem2 = GUICtrlCreateMenuItem("MenuItem2", $FormContext)
$MenuItem3 = GUICtrlCreateMenuItem("MenuItem3", $FormContext)
$Button1 = GUICtrlCreateButton("Button1", 8, 8, 73, 25)

$FormContext2 = GUICtrlCreateContextMenu($Button1)
$MenuItem1 = GUICtrlCreateMenuItem("MenuItem4", $FormContext2)
$MenuItem2 = GUICtrlCreateMenuItem("MenuItem5", $FormContext2)
$MenuItem3 = GUICtrlCreateMenuItem("MenuItem6", $FormContext2)

GUISetState(@SW_SHOW)

; добавляем много кнопок и контекстных меню к ним
For $n = 2 To 30
    $ButtonN = GUICtrlCreateButton($n, 8+(($n-2)*20), 35, 20, 20)
    $FormContextN = GUICtrlCreateContextMenu($ButtonN)
    $MenuItem1 = GUICtrlCreateMenuItem("MenuItem" & ($n + 5), $ButtonN)
Next

; удаляем некоторые элементы
For $n = 30 To 70
    GUICtrlDelete($n)
Next

$aMenus = _GUICtrlMenu_EnumMenus()

$hFormContext = GUICtrlGetHandle($FormContext)
$hFormContext2 = GUICtrlGetHandle($FormContext2)

ConsoleWrite($FormContext & ' = ' & $aMenus[1][0] & '? ' & ($FormContext = $aMenus[1][0]) & @CRLF)
ConsoleWrite($FormContext2 & ' = ' & $aMenus[2][0] & '? ' & ($FormContext2 = $aMenus[2][0]) & @CRLF)

ConsoleWrite($hFormContext & ' = ' & $aMenus[1][1] & '? ' & ($hFormContext = $aMenus[1][1]) & @CRLF)
ConsoleWrite($hFormContext2 & ' = ' & $aMenus[2][1] & '? ' & ($hFormContext2 = $aMenus[2][1]) & @CRLF)

_ArrayDisplay($aMenus)


While 1
    $nMsg = GUIGetMsg()

    Switch $nMsg
        Case $GUI_EVENT_CLOSE
            Exit
    EndSwitch
WEnd

Func _GUICtrlMenu_EnumMenus()

    ; вычисляем последний элемент и анализируем время вычисления
    $iT = TimerInit()
    For $iLastCtrlID=9999 To 3
        If GUICtrlGetHandle($iLastCtrlID) Then ExitLoop
    Next
    ConsoleWrite('Время вычисления последнего элемента: ' & TimerDiff($iT) & @CRLF)
    ;

    Local $hCtrl, $aMenus[1][2] = [[0]]

    For $iID = 3 To $iLastCtrlID
        $hCtrl = GUICtrlGetHandle($iID)

        If _GUICtrlMenu_IsMenu($hCtrl) Then
            $aMenus[0][0] += 1
            ReDim $aMenus[$aMenus[0][0] + 1][2]

            $aMenus[$aMenus[0][0]][0] = $iID
            $aMenus[$aMenus[0][0]][1] = $hCtrl
        EndIf
    Next

    Return SetError(($aMenus[0][0] = 0), 0, $aMenus)
EndFunc   ;==>_GUICtrlMenu_EnumMenus
 
Последнее редактирование:

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
это не сработает, если будет удалён хотя бы один из контролов
Да, этот момент я забыл.
Грубый способ вычисления последнего элемента
Только наверное корректно было бы так:
Код:
...
    For $iLastCtrlID = 65532 To 3 Step -1
        If GUICtrlGetHandle($iLastCtrlID) Then
            ExitLoop
        EndIf
    Next
    
    For $iID = 3 To $iLastCtrlID

т.к макс. число элементов в AutoIt это 65532.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Ещё корректнее наверное будет так:
Код:
Func _GUICtrlMenu_EnumMenus()
    Local $iLabel = GUICtrlCreateLabel('', -100, -100)
    Local $iLastCtrlID = $iLabel
    Local $hCtrl, $aMenus[1][2] = [[0]]
   
    For $iID = $iLastCtrlID + 1 To 65532
        If Not GUICtrlGetHandle($iID) Then
            $iLastCtrlID = $iID - 1
            ExitLoop
        EndIf
    Next
   
    For $iID = 3 To $iLastCtrlID
        $hCtrl = GUICtrlGetHandle($iID)
       
        If _GUICtrlMenu_IsMenu($hCtrl) Then
            $aMenus[0][0] += 1
            ReDim $aMenus[$aMenus[0][0] + 1][2]
           
            $aMenus[$aMenus[0][0]][0] = $iID
            $aMenus[$aMenus[0][0]][1] = $hCtrl
        EndIf
    Next
   
    GUICtrlDelete($iLabel)
    Return SetError(($aMenus[0][0] = 0), 0, $aMenus)
EndFunc


Идея в том, чтобы перебирать все элементы начиная от первого найденного (удалён) и до последнего, пока он не является элементом, это будет быстрее чем перебирать все элементы подряд.

Хотя нет, тут могут попасть следующие элементы которые удалены.
 
Последнее редактирование:
Верх