Re: AHK: Замена "Window Spy"
Никак.
Вы не вошли. Пожалуйста, войдите или зарегистрируйтесь.
Страницы Назад 1 … 15 16 17 18 19 Далее
Чтобы отправить ответ, вы должны войти или зарегистрироваться
Никак.
Malcev
А какой тогда смысл, если нет возможности использовать данные из этого Spy?
user7256, этот инструмент не для программного использования. С его помощью вы можете визуально ознакомиться со структурой окна, и скопировать какие-либо данные в буфер обмена.
Плохо. Большинство - хочет строить свою логику на основе выдаваемых данных, а не просто скопировать в буфер.
Почему плохо? Просто эта программа не для этого. Вы можете посмотреть в коде, какие методы используются, и применять их отдельно. Либо спросить здесь, если чего-то не понимаете.
user7256
Какой смысл использовать огромный кусок кода для своих мелких поделок? Не легче разобраться как оно устроено и сделать свою версию?
Библиотека Acc настолько сложна и забагованна, что без обертки пользоваться ей невозможно.
То тут, то там возникают ошибки.
Яркий тому пример - протухшая библиотека AccV2.ahk на гитхабе.
Хорошие программисты всегда стремились к понижению сложности.
Для уменьшения затрат на программирование практикуется code reuse, разбивка на модули и много других техник.
У вас ничего этого нет. Все в кучу на 8 тыс строк на 280 кб.
Вы предлагаете выдирать отдельные функции из вашего скрипта для использования в моих скриптах.
Но логичнее было бы один раз создать библиотеку Acc.ahk, и подключать к вашему AhkSpy.ahk где была бы только рисовалка формы с выводом данных в нее.
Это и было бы настоящим code reuse и разбивкой на модули на практике.
Так чтобы другие могли ей пользоваться, а не только трое программистов на свете, участвовавших в разработке.
Вы потратили столько времени на скрипт, это похвально.
Но ваш код очень быстро придет в негодность как только исчезнет желание его поддерживать.
И все сотни часов разработки окажутся потрачены зря, как у того самого юзера sancarn на гите.
Вот я выдрал все процедуры и хочу получить адрес бар из firefox с помощью GetBrowserURL()
Вот что получаю в результате:
ComObjError(false)
Global WS_EX_APPWINDOW := 0x40000, WS_CHILDWINDOW := 0x40000000, WS_EX_LAYERED := 0x80000
, WS_EX_TRANSPARENT := 0x20, WS_POPUP := 0x80000000, WS_EX_NOACTIVATE := 0x8000000
#NoEnv
#UseHook
#KeyHistory 0
SetBatchLines, -1
ListLines, Off
DetectHiddenWindows, On
CoordMode, Pixel
CoordMode, Menu
oOther.anchor := {}, oOther.CurrentProcessId := DllCall("GetCurrentProcessId")
; If MemoryAnchor
; {
; If oOther.anchor["Win_text"] := IniRead("Win_Anchor")
; oOther.anchor["Win"] := 1
; If oOther.anchor["Control_text"] := IniRead("Control_Anchor")
; oOther.anchor["Control"] := 1
; }
SeDebugPrivilege()
oBody := oDoc.body
oJScript := oDoc.Script
oJScript.WordWrap := WordWrap
oJScript.MoveTitles := MoveTitles
ComObjConnect(oDoc, Events)
url := GetBrowserURL()
OutputDebug, url: %url%
GetBrowserURL()
{
WinGet, hw, ID, ahk_exe firefox.exe
AccAccFocus(hw, name, value, role, irole)
OutputDebug, value: %value%
return value
}
SeDebugPrivilege() {
Static PROCESS_QUERY_INFORMATION := 0x400, TOKEN_ADJUST_PRIVILEGES := 0x20, SE_PRIVILEGE_ENABLED := 0x2
hProc := DllCall("OpenProcess", UInt, PROCESS_QUERY_INFORMATION, Int, false, UInt, oOther.CurrentProcessId, Ptr)
DllCall("Advapi32\OpenProcessToken", Ptr, hProc, UInt, TOKEN_ADJUST_PRIVILEGES, PtrP, token)
DllCall("Advapi32\LookupPrivilegeValue", Ptr, 0, Str, "SeDebugPrivilege", Int64P, luid)
VarSetCapacity(TOKEN_PRIVILEGES, 16, 0)
NumPut(1, TOKEN_PRIVILEGES, "UInt")
NumPut(luid, TOKEN_PRIVILEGES, 4, "Int64")
NumPut(SE_PRIVILEGE_ENABLED, TOKEN_PRIVILEGES, 12, "UInt")
DllCall("Advapi32\AdjustTokenPrivileges", Ptr, token, Int, false, Ptr, &TOKEN_PRIVILEGES, UInt, 0, Ptr, 0, Ptr, 0)
res := A_LastError
DllCall("CloseHandle", Ptr, token)
DllCall("CloseHandle", Ptr, hProc)
Return res ;; в случае удачи 0
}
AccWindowFromObject(pacc) {
If DllCall("oleacc\WindowFromAccessibleObject", "Ptr", IsObject(pacc) ? ComObjValue(pacc) : pacc, "Ptr*", hWnd) = 0
Return hWnd
}
;; http://forum.script-coding.com/viewtopic.php?pid=130762#p130762
AccAccFocus(hWnd, byref name, byref value, byref role, byref irole) {
Acc := Acc_ObjectFromWindow(hWnd)
While IsObject(Acc.accFocus)
Acc := Acc.accFocus
try
{
child := Acc.accFocus
name := Acc.accName(child)
value := Acc.accValue(child)
role := AccRole(Acc, child)
irole := Acc.accRole(0)
}
}
AccRole(Acc, ChildId=0) {
Return ComObjType(Acc, "Name") = "IAccessible" ? AccGetRoleText(Acc.accRole(ChildId)) : ""
}
AccGetRoleText(nRole) {
nSize := DllCall("oleacc\GetRoleText", "UInt", nRole, "Ptr", 0, "UInt", 0)
VarSetCapacity(sRole, (A_IsUnicode?2:1)*nSize)
DllCall("oleacc\GetRoleText", "UInt", nRole, "str", sRole, "UInt", nSize+1)
Return sRole
}
AccGetStateText(nState) {
nSize := DllCall("oleacc\GetStateText", "UInt", nState, "Ptr", 0, "UInt", 0)
VarSetCapacity(sState, (A_IsUnicode?2:1)*nSize)
DllCall("oleacc\GetStateText", "UInt", nState, "str", sState, "UInt", nSize+1)
Return sState
}
Acc_GetPath(byref arr) {
Static DesktopHwnd := DllCall("User32.dll\GetDesktopWindow", "ptr")
If oPubObj.Acc.CLOAKED
Return 0
Acc := Object(oPubObj.Acc.AccObj)
arr := []
While Hwnd := Acc_WindowFromObject(Parent := Acc_Parent(Acc)) {
If (DesktopHwnd != Hwnd)
t1 := GetEnumIndex(Acc)
If t1 = -1
Return arr := ""
If (PrHwnd != "" && Hwnd != PrHwnd)
{
PrHwnd := Format("0x{:06X}", PrHwnd)
WinGetClass, WinClass, ahk_id %PrHwnd%
WinGet, ProcessName, ProcessName, ahk_id %PrHwnd%
arr.InsertAt(1, {Hwnd: PrHwnd, Path: SubStr(t2, 1, -1), WinClass: WinClass, ProcessName: ProcessName})
}
if (t1 = "" || Hwnd = DesktopHwnd)
break
t2 := t1 "." t2
PrHwnd := Hwnd
Acc := Parent
}
Return arr.Count()
}
GetEnumIndex(Acc) {
If oPubObj.Acc.CLOAKED
Return -1
For Each, child in Acc_Children(Acc_Parent(Acc))
{
if CompareAcc(child, Acc)
return A_Index
}
}
CompareAcc(Acc1, Acc2) {
if IsObject(Acc1) && IsObject(Acc2)
&& (Acc_Location(Acc1) = Acc_Location(Acc2))
&& (Acc1.accDefaultAction(0) = Acc2.accDefaultAction(0))
&& (Acc1.accDescription(0) = Acc2.accDescription(0))
&& (Acc1.accHelp(0) = Acc2.accHelp(0))
&& (Acc1.accKeyboardShortcut(0) = Acc.accKeyboardShortcut(0))
&& (Acc1.accChildCount = Acc2.accChildCount)
&& (Acc1.accName(0) = Acc2.accName(0))
&& (Acc1.accRole(0) = Acc2.accRole(0))
&& (Acc1.accState(0) = Acc2.accState(0))
&& (Acc1.accValue(0) = Acc2.accValue(0))
return 1
}
Acc_Children(Acc) {
if ComObjType(Acc, "Name") != "IAccessible"
return
else
{
cChildren := Acc.accChildCount, Children := []
if DllCall("oleacc\AccessibleChildren"
, "Ptr", ComObjValue(Acc)
, "Int", 0, "Int", cChildren
, "Ptr", VarSetCapacity(varChildren, cChildren * (8 + 2 * A_PtrSize), 0) * 0 + &varChildren
, "Int*", cChildren) = 0
{
Loop %cChildren%
i := (A_Index - 1) * (A_PtrSize * 2 + 8) + 8
, child := NumGet(varChildren, i)
, Children.Insert(NumGet(varChildren, i - 8) = 9 ? Acc_Query(child) : child)
, NumGet(varChildren, i - 8) = 9 ? ObjRelease(child) : ""
return Children.MaxIndex() ? Children : ""
}
}
}
AccGetLocation(Acc, ChildId=0) {
Static x := 0, y := 0, w := 0, h := 0
try Acc.accLocation(ComObj(0x4003,&x), ComObj(0x4003,&y), ComObj(0x4003,&w), ComObj(0x4003,&h), ChildId)
AccCoord[1]:=NumGet(x,0,"int"), AccCoord[2]:=NumGet(y,0,"int"), AccCoord[3]:=NumGet(w,0,"int"), AccCoord[4]:=NumGet(h,0,"int")
}
Acc_Location(Acc, ChildId=0) {
Static x := 0, y := 0, w := 0, h := 0
try Acc.accLocation(ComObj(0x4003,&x), ComObj(0x4003,&y), ComObj(0x4003,&w), ComObj(0x4003,&h), ChildId)
return "x" NumGet(x, 0, "int") " y" NumGet(y, 0, "int") " w" NumGet(w, 0, "int") " h" NumGet(h, 0, "int")
}
Acc_ObjectFromPoint(ByRef _idChild_ = "", x = "", y = "") {
If DllCall("oleacc\AccessibleObjectFromPoint", "Int64", x==""||y==""?0*DllCall("GetCursorPos","Int64*",pt)+pt:x&0xFFFFFFFF|y<<32
, "Ptr*", pacc, "Ptr", VarSetCapacity(varChild,8+2*A_PtrSize,0)*0+&varChild)=0
Return ComObjEnwrap(9,pacc,1), _idChild_:=NumGet(varChild,8,"UInt")
}
Acc_ObjectFromWindow(hWnd, idObject = 0) {
If DllCall("oleacc\AccessibleObjectFromWindow", "Ptr", hWnd, "UInt", idObject&=0xFFFFFFFF
, "Ptr", -VarSetCapacity(IID,16)+NumPut(idObject==0xFFFFFFF0?0x46000000000000C0:0x719B3800AA000C81
,NumPut(idObject==0xFFFFFFF0?0x0000000000020400:0x11CF3C3D618736E0,IID,"Int64"),"Int64"), "Ptr*", pacc)=0
Return ComObjEnwrap(9,pacc,1)
}
Acc_WindowFromObject(pacc) {
If DllCall("oleacc\WindowFromAccessibleObject", "Ptr", IsObject(pacc) ? ComObjValue(pacc) : pacc, "Ptr*", hWnd)=0
Return hWnd
}
Acc_Parent(Acc) {
try parent := Acc.accParent
return parent ? Acc_Query(parent) : ""
}
Acc_Query(Acc) {
try return ComObj(9, ComObjQuery(Acc, "{618736e0-3c3d-11cf-810c-00aa00389b71}"), 1)
}
Acc_Error(p="") {
static setting:=0
return p=""?setting:setting:=p
}
Acc_Role(Acc, ChildId=0) {
try return ComObjType(Acc,"Name")="IAccessible"?Acc_GetRoleText(Acc.accRole(ChildId)):"invalid object"
}
Acc_GetRoleText(nRole)
{
nSize := DllCall("oleacc\GetRoleText", "Uint", nRole, "Ptr", 0, "Uint", 0)
VarSetCapacity(sRole, (A_IsUnicode?2:1)*nSize)
DllCall("oleacc\GetRoleText", "Uint", nRole, "str", sRole, "Uint", nSize+1)
Return sRole
}
Acc_ChildrenByRole(Acc, Role) {
if ComObjType(Acc,"Name")!="IAccessible"
ErrorLevel := "Invalid IAccessible Object"
else {
cChildren:=Acc.accChildCount, Children:=[]
if DllCall("oleacc\AccessibleChildren", "Ptr",ComObjValue(Acc), "Int",0, "Int",cChildren, "Ptr",VarSetCapacity(varChildren,cChildren*(8+2*A_PtrSize),0)*0+&varChildren, "Int*",cChildren)=0 {
Loop %cChildren% {
i:=(A_Index-1)*(A_PtrSize*2+8)+8, child:=NumGet(varChildren,i)
if NumGet(varChildren,i-8)=9
AccChild:=Acc_Query(child), ObjRelease(child), Acc_Role(AccChild)=Role?Children.Insert(AccChild):
else
Acc_Role(Acc, child)=Role?Children.Insert(child):
}
return Children.MaxIndex()?Children:, ErrorLevel:=0
} else
ErrorLevel := "AccessibleChildren DllCall Failed"
}
if Acc_Error()
throw Exception(ErrorLevel,-1)
}
Acc_Get(Cmd, ChildPath="", ChildID=0, WinTitle="", WinText="", ExcludeTitle="", ExcludeText="") {
static properties := {Action:"DefaultAction", DoAction:"DoDefaultAction", Keyboard:"KeyboardShortcut"}
AccObj := IsObject(WinTitle)? WinTitle
: Acc_ObjectFromWindow( WinExist(WinTitle, WinText, ExcludeTitle, ExcludeText), 0 )
if ComObjType(AccObj, "Name") != "IAccessible"
ErrorLevel := "Could not access an IAccessible Object"
else {
StringReplace, ChildPath, ChildPath, _, %A_Space%, All
AccError:=Acc_Error(), Acc_Error(true)
Loop Parse, ChildPath, ., %A_Space%
try {
if A_LoopField is digit
Children:=Acc_Children(AccObj), m2:=A_LoopField ; mimic "m2" output in else-statement
else
RegExMatch(A_LoopField, "(\D*)(\d*)", m), Children:=Acc_ChildrenByRole(AccObj, m1), m2:=(m2?m2:1)
if Not Children.HasKey(m2)
throw
AccObj := Children[m2]
} catch {
ErrorLevel:="Cannot access ChildPath Item #" A_Index " -> " A_LoopField, Acc_Error(AccError)
if Acc_Error()
throw Exception("Cannot access ChildPath Item", -1, "Item #" A_Index " -> " A_LoopField)
return ; Acc_Error
}
Acc_Error(AccError)
StringReplace, Cmd, Cmd, %A_Space%, , All
properties.HasKey(Cmd)? Cmd:=properties[Cmd]:
try {
if (Cmd = "Location")
AccObj.accLocation(ComObj(0x4003,&x:=0), ComObj(0x4003,&y:=0), ComObj(0x4003,&w:=0), ComObj(0x4003,&h:=0), ChildId)
, ret_val := "x" NumGet(x,0,"int") " y" NumGet(y,0,"int") " w" NumGet(w,0,"int") " h" NumGet(h,0,"int")
else if (Cmd = "Object")
ret_val := AccObj
else if Cmd in Role,State
ret_val := Acc_%Cmd%(AccObj, ChildID+0)
else if Cmd in ChildCount,Selection,Focus
ret_val := AccObj["acc" Cmd]
else
ret_val := AccObj["acc" Cmd](ChildID+0)
} catch {
ErrorLevel := """" Cmd """ Cmd Not Implemented"
if Acc_Error()
throw Exception("Cmd Not Implemented", -1, Cmd)
return
}
return ret_val, ErrorLevel:=0
}
if Acc_Error()
throw Exception(ErrorLevel,-1)
}
AccUnderMouse(WinID, ByRef child) {
Static h
If Not h
h := DllCall("LoadLibrary", "Str", "oleacc", "Ptr")
If DllCall("oleacc\AccessibleObjectFromPoint"
, "Int64", 0 * DllCall("GetCursorPos", "Int64*", pt) + pt, "Ptr*", pacc
, "Ptr", VarSetCapacity(varChild, 8 + 2 * A_PtrSize, 0) * 0 + &varChild) = 0
Acc := ComObjEnwrap(9, pacc, 1)
If IsObject(Acc)
Return Acc, child := NumGet(varChild, 8, "UInt")
}
Библиотека Acc настолько сложна и забагованна, что без обертки пользоваться ей невозможно.
Пока я багов не обнаружил и по сравнению с UIA ничуть не сложна.
user7256, думаю, не стоит в этой теме обсуждать неудачные попытки использования выдранного наугад кода. Эта тема предназначена для обсуждения AhkSpy. Создайте свою тему со внятным заголовком, напишите, что пытались сделать, задайте конкретный вопрос.
serzh82saratov, у меня последняя версия не подсвечивает acc-элементы на семёрке, на десятке работает.
У меня нет семёрки, а координаты и размер правильно определяет?
Отбой, я просто не включил режим Control.
Такой вопрос на гитхабе задали:
Is it possible to retrieve the caret position in edit fields? I know this is possible in standard Win32 control edits, but I wonder if UIA allows this.
тоже интересно.
Через UIA нельзя.
Только через MSAA.
У тебя есть что то работающее?
Хотя через uia можно наверное так:
https://docs.microsoft.com/en-us/window … caretrange
Работающего нету. Но на нашем форуме через iaccessible это обсуждалось.
IUIAutomationTextPattern2 тоже смотрел, нигде не нашёл примеров.
на нашем форуме
Ссылки нет?
IUIAutomationTextPattern2 тоже смотрел, нигде не нашёл примеров.
Не думаю, что в этом будет толк, так как контрол должен поддерживать text pattern, а это далеко не всегда.
Ссылки нет?
Но если уж так хочется потестировать text pattern, то можешь добавить его в класс от jethrow, как я сделал тут:
https://www.autohotkey.com/boards/viewtopic.php?t=69627
Либо самому вызывать, как я сделал тут:
https://www.autohotkey.com/boards/viewt … p?p=329332
Это работает http://forum.script-coding.com/viewtopi … 940#p97940, нашёл сразу.
как я сделал тут
Так сразу не сообразишь, какой именно там код?
Я даже не знаю какой из способов мне проще адаптировать к UIASub в AhkSpy.
f11::
if !hVolume
{
ControlGet, hVolume, hwnd,, ToolbarWindow323, ahk_class Shell_TrayWnd
IUIAutomation := ComObjCreate(CLSID_CUIAutomation := "{ff48dba4-60ef-4201-aa87-54103eef594e}", IID_IUIAutomation := "{30cbe57d-d9d0-452a-ab13-7ac5ac4825ee}")
DllCall(NumGet(NumGet(IUIAutomation+0)+6*A_PtrSize), "ptr", IUIAutomation, "ptr", hVolume, "ptr*", tray) ; IUIAutomation::ElementFromHandle
VOLUME_GUID := DllCall("oleaut32\SysAllocString", "str", "{7820AE73-23E3-4229-82C1-E41CB67D5B9C}", "ptr")
VarSetCapacity(variant, 8+A_PtrSize*2, 0)
NumPut(VT_BSTR := 8, variant, 0, "ushort")
NumPut(VOLUME_GUID, variant, 8, "ptr")
if (A_PtrSize = 4)
DllCall(NumGet(NumGet(IUIAutomation+0)+23*A_PtrSize), "ptr", IUIAutomation, "int", UIA_AutomationIdPropertyId := 30011, "int64", NumGet(variant, 0, "int64"), "int64", NumGet(variant, 8, "int64"), "ptr*", condition) ; IUIAutomation::CreatePropertyCondition
else
DllCall(NumGet(NumGet(IUIAutomation+0)+23*A_PtrSize), "ptr", IUIAutomation, "int", UIA_AutomationIdPropertyId := 30011, "ptr", &variant, "ptr*", condition) ; IUIAutomation::CreatePropertyCondition
DllCall("oleaut32\SysFreeString", "ptr", VOLUME_GUID)
}
DllCall(NumGet(NumGet(tray+0)+5*A_PtrSize), "ptr", tray, "int", TreeScope_Descendants := 0x4, "ptr", condition, "ptr*", element) ; IUIAutomationElement::FindFirst
DllCall(NumGet(NumGet(element+0)+16*A_PtrSize), "ptr", element, "int", UIA_InvokePatternId := 10000, "ptr*", invoke) ; IUIAutomationElement::GetCurrentPattern
DllCall(NumGet(NumGet(invoke+0)+3*A_PtrSize), "ptr", invoke) ; IInvokeProvider::Invoke
ObjRelease(element)
ObjRelease(invoke)
Ладно, долго разбираться, асс пойдёт.
Ну и правильно.
Например в адресной строке у фаерфокса положение каретки через UIA не получить, так как textpattrern не поддерживается.
А через ACC получается без проблем.
Думаю не очень правильно, или ты считаешь что не может быть такого, когда UIA работает а MSAA нет.
Да, и как ты узнал что UIA не работает, в inspect не нашёл такого.
или ты считаешь что не может быть такого, когда UIA работает а MSAA нет.
Утверждать такого не буду, но я проверил на пару программах и пока таких случаев не обнаружил.
В inspect надо смотреть IsTextPattern2Available: true/false.
Спасибо.
Добавил в 4.70.
Accessible > Caret position relative.
Looking forward to the retrieval of the caret position index in the text field.
Теперь такое хотят.
Ты можешь открыть донат и выполнять подобные хотелки за пожертвования.
Malcev
Следующая версия будет вся напичкана рекламой того, что может реализовать автор программы. Придётся искать Adblock.ahk для использования.
Зачем напичкана?
Я бы сделал обычное стандартное меню у программы и в меню help прописал бы donate.
Как это сделано, например, у WinSCP.
Вопрос сам по себе интересный. Так понимаю в MSAA этого нет, в IUIAutomationTextPattern2 не знаю.
4.71 добавил в Buttton тест ControlSend из определённого перед этим контрола, если такого нет то окна.
Вопрос сам по себе интересный
Ну если уж такой интересный, то так вызывается.
ЗЫ Освобождать ресурсы не интересно (лень).
f11::
IUIAutomation := ComObjCreate(CLSID_CUIAutomation8 := "{e22ad333-b25f-460c-83d0-0581107395c9}", IID_IUIAutomation := "{30cbe57d-d9d0-452a-ab13-7ac5ac4825ee}")
DllCall(NumGet(NumGet(IUIAutomation+0)+7*A_PtrSize), "ptr", IUIAutomation, "int64", 0*DllCall("GetCursorPos","Int64*",pt)+pt, "ptr*", ElementFromPoint) ; IUIAutomation::ElementFromPoint
DllCall(NumGet(NumGet(ElementFromPoint+0)+16*A_PtrSize), "ptr", ElementFromPoint, "int", UIA_TextPattern2Id := 10024, "ptr*", ITextProvider2) ; IUIAutomationElement::GetCurrentPattern
DllCall(NumGet(NumGet(ITextProvider2+0)+7*A_PtrSize), "ptr", ITextProvider2, "ptr*", ITextRangeProvider_DocumentRange) ; ITextProvider2::get_DocumentRange
DllCall(NumGet(NumGet(ITextProvider2+0)+10*A_PtrSize), "ptr", ITextProvider2, "int*", isActive, "ptr*", ITextRangeProvider_CaretRange) ; ITextProvider2::GetCaretRange
DllCall(NumGet(NumGet(ITextRangeProvider_CaretRange+0)+5*A_PtrSize), "ptr", ITextRangeProvider_CaretRange, "int", TextPatternRangeEndpoint_Start := 0, "ptr", ITextRangeProvider_DocumentRange, "int", TextPatternRangeEndpoint_Start := 0, "uint*", caretPos) ; ITextRangeProvider2::CompareEndpoints
msgbox % caretPos
Пока ничего не понятно, но жутко интересно. Можешь ещё раз вкратце объяснить, где ты берёшь смещения и Id.
ITextProvider2 это не IUIAutomationTextPattern2?
На первый взгляд действительно бесполезная штука, показывает только в обычном блокноте. Если isActive = 0, то нет смысла пробовать другие интерфейсы UIA?
Я взял код отсюда:
https://stackoverflow.com/questions/613 … on-not-the
Создал IUIAutomation2 интерфейс:
IUIAutomation := ComObjCreate(CLSID_CUIAutomation8 := "{e22ad333-b25f-460c-83d0-0581107395c9}", IID_IUIAutomation := "{30cbe57d-d9d0-452a-ab13-7ac5ac4825ee}")
https://docs.microsoft.com/en-us/previo … 6(v=vs.85)
После чего взял код получения элемента под курсором мыши
DllCall(NumGet(NumGet(IUIAutomation+0)+7*A_PtrSize), "ptr", IUIAutomation, "int64", 0*DllCall("GetCursorPos","Int64*",pt)+pt, "ptr*", ElementFromPoint) ; IUIAutomation::ElementFromPoint
отсюда
https://github.com/jethrow/UIA_Interfac … ce.ahk#L72
После чего нам надо получить нужный нам паттерн элемента через IUIAutomationElement::GetCurrentPattern
DllCall(NumGet(NumGet(ElementFromPoint+0)+16*A_PtrSize), "ptr", ElementFromPoint, "int", UIA_TextPattern2Id := 10024, "ptr*", ITextProvider2) ; IUIAutomationElement::GetCurrentPattern
Заходишь по этой ссылке, смотришь, что надо вписывать:
https://docs.microsoft.com/en-us/window … entpattern
PATTERNID
The identifier of the control pattern. For a list of control pattern IDs, see Control Pattern Identifiers.
Там видишь, что UIA_TextPattern2Id = 10024.
Дальше идешь в самый низ метода и видишь что интерфейс расписан в
Header uiautomationclient.h (include UIAutomation.h)
Гуглишь его - выходишь на гитхаб
https://github.com/tpn/winsdk-10/blob/m … onClient.h
Вбиваешь там в поиск GetCurrentPattern.
Получаешь
https://github.com/tpn/winsdk-10/blob/m … nt.h#L1766
После чего считаешь какой он идет по счету.
Получаешь 16.
И так далее.
ITextProvider2 это не IUIAutomationTextPattern2?
Не, это разные интерфейсы. Нам правильней использовать IUIAutomationTextPattern2.
Это я названия попутал.
На первый взгляд действительно бесполезная штука, показывает только в обычном блокноте. Если isActive = 0, то нет смысла пробовать другие интерфейсы UIA?
Не понял, а при чем тут isActive = 0?
При isActive = 0 в блокноте всё-равно показывает.
Спасибо, всё понятно, тебе надо в педагоги.
Не понял, а при чем тут isActive = 0?
Спутал её с проверкой наличия у элемента TextPattern.
Предлагаю всегда вставлять в Acc Do Default Action, так как оно может быть прописано, даже когда не определено.
если свойство DefaultAction у элемента не определено, то применив к этому элементу метод DoDefaultAction в результате может дать нажатие на этот элемент.
Не совсем правильно.
DoDefaultAction убираем.
Если возвращает пустоту, но ошибки нету, значит пишем в Action
[null] ▪ Execute
Если же возвращает ошибку, то пишем в defaultaction
[Error: hr=0x80020003 - Member not found.] без Execute
Malcev
Забыл там уже всё, можешь кусок кода показать?
Это убираем:
id='acc_DoDefaultAction2'>
А вот сюда
id='acc_DoDefaultAction'>
вставляем если нету ошибки и ничему не равно, то
[null] ▪ Execute
или если есть ошибка, то
[Error: hr=0x80020003 - Member not found.] без Execute.
[Error: hr=0x0001 - Does not have a default action.] без Execute.
[Error: hr=0x80070057 - An argument is not valid.] без Execute.
То есть проверяем, что возвращает IAccessible::get_accDefaultAction.
https://docs.microsoft.com/en-us/window … aultaction
Посмотрю обязательно, сейчас время нет.
Баг в проверке acc пути.
На броузере vivaldi при дефалтных установках путь для получения адресной строки должен быть таким
4.1.2.1.1.1.1.1.2.1.2.3.1.1
AhkSpy же выводит
4.1.1.1.2.1.2.3.1.1
Так же если есть желание, то можно добавить нахождение пути в буквенной форме через role properties.
Обнаружил баг в функции Acc_ObjectFromWindow.
Если ей предоставить хендл окна в uint64, типа такого hwnd := 0xffffffff80400b6c, то iaccessible интерфейс не возвращается.
Лень копаться в математике функции, решил добавить просто:
varsetcapacity(var, 8, 0)
numput(hwnd, var, 0, "uint64")
hwnd := numget(var, 0, "uint")
Acc_ObjectFromWindow(hWnd, idObject = -4)
{
Acc_Init()
varsetcapacity(var, 8, 0)
numput(hwnd, var, 0, "uint64")
hwnd := numget(var, 0, "uint")
If DllCall("oleacc\AccessibleObjectFromWindow", "Ptr", hWnd, "UInt", idObject&=0xFFFFFFFF, "Ptr", -VarSetCapacity(IID,16)+NumPut(idObject==0xFFFFFFF0?0x46000000000000C0:0x719B3800AA000C81,NumPut(idObject==0xFFFFFFF0?0x0000000000020400:0x11CF3C3D618736E0,IID,"Int64"),"Int64"), "Ptr*", pacc)=0
Return ComObjEnwrap(9,pacc,1)
}
Кстати, надо бы проверить, может достаточно hwnd, как uint указывать в dllcall.
Проверил - если просто uint указать не помогает.
Если ей предоставить хендл окна в uint64, типа такого hwnd := 0xffffffff80400b6c
Насколько я знаю, хэндлы окон не превышают UInt.
Написал в баг-репорт.
https://www.autohotkey.com/boards/viewt … 79#p402579
Я так понял, lexikos не хочет исправлять это в исходном коде.
Причина, лично мне, непонятна.
Я тоже не понял, какие-то проблемы с обратной совместимостью.
Игнорирует.
Наверное лень переписывать.
Когда система начинает давать такие хендлы окнам, то автохотки превращается в один сплошной баг.
Например ahk_spy не запускается, так как там видно в гуи прописано обращение к дочерним и родительскому окну, хендлы которых не распознаются.
В общем грустно.
Что делать, придётся использовать костыли.
Malcev, не понимаю чего-то очевидного. Вот такой код для Скайпа, если Скайп только что запущен
$F1::
SendMessage, WM_GETOBJECT := 0x003D, 0, 1, Chrome_RenderWidgetHostHWND1, A
Acc := Acc_ObjectFromPoint(child)
MsgBox, % Acc.accRole(child)
Return
всегда возвращает 15 (ROLE_SYSTEM_DOCUMENT). Что нужно сделать, чтобы начал определять дочерние элементы?
Судя по всему надо как-то так делать.
if !init
{
SendMessage, WM_GETOBJECT := 0x003D, 0, 1, Chrome_RenderWidgetHostHWND1, A
Acc := Acc_ObjectFromPoint(child)
Acc.accName(child)
sleep 300
init := 1
}
Acc := Acc_ObjectFromPoint(child)
MsgBox, % Acc.accRole(child)
Return
Но проще имхо запускать скайп с --force-renderer-accessibility.
Ага, спасибо, оказалось, фишка была в Acc.accName(child). А я не мог врубиться, почему мой код не работает, а AhkSpy и AccViewer работают прекрасно. Понять бы ещё, почему так происходит.
Но проще имхо запускать скайп с --force-renderer-accessibility.
В моём случае не подойдёт, нужно читать с имеющегося.
Понять бы ещё, почему так происходит.
Не знаю, знаю только, что с некоторыми программами, например с Вивальди, так же надо посылать Acc.accName(child) для инициализации.
4.86
Добавлен переход к окнам и контролам.
Подробнее тут https://t.me/AhkSpy_Group
Можно перейти к багам.
Для начала предлагаю исправить баг автохотки, который не хочет исправлять lexikos.
Все оконные хендлы должны передаваться как "uptr".
DllCall("oleacc\AccessibleObjectFromWindow", "uptr", hWnd
Если сравниваешь оконные хендлы, либо используешь их в гуи то их надо преобразовывать, например так:
if (RealHwnd(hwnd) = RealHwnd(hGui))
msgbox
gui, % "child: +Parent" realHwnd(hwnd)
realHwnd(hwnd)
{
varsetcapacity(var, 8, 0)
numput(hwnd, var, 0, "uint64")
return numget(var, 0, "uint")
}
баг автохотки
А в чём этот баг проявляется?
сравниваешь оконные хендлы, либо используешь их в гуи
а тут?
WinGetPos,,, Width, Height, ahk_id %hwnd%
А в чём этот баг проявляется?
https://www.autohotkey.com/boards/viewt … mp;t=89745
а тут?
Тут всё будет в порядке (тестировать надо на ahk 64bit).
setbatchlines -1
loop 35000
{
gui, +hwndhwnd
if instr(hwnd, "0xffffffff")
{
Gui Show, w100 h100
WinGetPos,,, Width, Height, ahk_id %hwnd%
msgbox % width
}
tooltip % a_index
gui, destroy
}
Reload
Тут всё будет в порядке
А на 32бит?
Я на практике с таким не сталкивался.
На 32 бит этого бага не будет.
А в чём смысл пилить прогу под 64бит?
под анк 64бит
Я бы переиначил вопрос.
А смысл пилить прогу под устаревшую архитектуру?
Разве что если необходимо использовать 32bit dll, а аналогов для 64 bit нету или хакать 32bit процесс.
А смысл пилить прогу под устаревшую архитектуру?
dll я пока в AhkSpy не использую. Может кто запустит под ОС 32, или с такой переделкой тоже будет работать?
И прога уже давно запилена, переделка не маленькая, я пока не вижу практического смысла в ней если и так работает.
Я занёс ссылку на этот вопрос в описание скрипта на будущее.
Практический смысл - программа не работает на ahk 64bit при создании более 32000 окон.
На 32 бит ос, переделка из 1,656 поста ни на что влиять не будет.
Вот пример.
Запусти этот код на ахк64 бит:
setbatchlines -1
loop 35000
{
gui, +hwndhwnd
if instr(hwnd, "0xffffffff")
{
Gui Show, w100 h100
msgbox % hwnd
}
tooltip % a_index
gui, destroy
}
Reload
Как появится msgbox, запусти ahkspy, наведи на окно и увидишь, что iaccessible ничего не показывает.
Как появится msgbox, запусти ahkspy, наведи на окно и увидишь, что iaccessible ничего не показывает.
Это я понял. Может сделаю, у меня 32 бит AutoHotkey установлен, не запускаю под 64. Сам понимаешь, проект не коммерческий, сильно переживать не приходится за то что некоторым пользователям не удобно запускать под 32. Пока если время на это есть, трачу на функционал. То что сегодня запилил, уже давно хотел сделать.
По acc пути не смог разобраться, по Action у меня везде логика, нет значения нет строки.
Ты путь в vivaldi где взял, у меня в Accessible Info Viewer пустой путь.
4.87
При переходе (см. 4.86) окно будет отображено, если открыта лупа и окно поддерживает PrintWindow.
Malcev
4.88
Я добавил RealHwnd в Get Acc Info, если есть конкретные места куда ещё надо, пиши.
Я добавил RealHwnd
4.89
Забыл UPtr в AccessibleObjectFromWindow.
Я бы пробежался по всем dllcall и заменил бы ptr на uptr, где передается hwnd.
по Action у меня везде логика, нет значения нет строки.
Ну это не совсем верно.
Даже если нет значения, то default action может выполняться.
Возьми к примеру хром и в нем "звездочку для букмаркс".
Значения нет, но действие прописано.
Кстати в справке у msdn походу тоже непонятки.
Пишут:
[out, retval] pszDefaultAction
Type: BSTR*
Address of a BSTR that receives a localized string that describes the default action for the specified object; if this object has no default action, the value is NULL.
+
Return value
Type: HRESULT
If successful, returns S_OK.
If not successful, returns one of the values in the table that follows, or another standard COM error code. Servers return these values, but clients must always check output parameters to ensure that they contain valid values. For more information, see Checking IAccessible Return Values.
Error Description
S_FALSE
The specified object does not have a default action.
https://docs.microsoft.com/en-us/window … aultaction
Часто бывает, что возвращается S_OK с результатом BSTR=NULL.
Конечно надежней проверять default action через Invoke в uia, но там тоже могут быть свои сложности, так как не все действия uia могут быть сделаны чере msaa.
Поэтому я и предлагаю убрать DoDefaultAction и изменить твой алгоритм проверки.
Ты путь в vivaldi где взял, у меня в Accessible Info Viewer пустой путь.
Используй AccExplorer.
Я бы пробежался по всем dllcall и заменил бы ptr на uptr, где передается hwnd.
Все, или только для оконных хэндлов?
Поэтому я и предлагаю убрать DoDefaultAction и изменить твой алгоритм проверки
Не понял каков алгоритм должен быть, и если всегда есть кнопка DoDefaultAction, то зачем что то делать?
Используй AccExplorer.
Ок, но всё равно с путём непонятки.
Все, или только для оконных хэндлов?
Только для оконных хендлов.
Не понял каков алгоритм должен быть, и если всегда есть кнопка DoDefaultAction, то зачем что то делать?
Вот ее можно убрать, и проверять на ошибку accDefaultAction, если ошибки нет и значения тоже нет, то прописывать null * execute в Action.
С путем, сейчас скачаю вивальди и посмотрю.
на ошибку accDefaultAction, если ошибки нет и значения тоже нет
Так как проверять? И мы же обсуждали что action может быть пустым, а действие всё равно выполняет.
Только для оконных хендлов.
Для окон, не контролов?
И тут как?
"Ptr*", hWnd
Можно проверять, что возвращает функция, если 0, то создавать action.
comobjerror(false)
loop
tooltip % Acc_test(child).accName(child) "`n" Acc_test(child).accDefaultAction(child) "`n" A_LastError
Acc_test(ByRef _idChild_ = "", x = "", y = "")
{
Static h
If Not h
h:=DllCall("LoadLibrary","Str","oleacc","Ptr")
If DllCall("oleacc\AccessibleObjectFromPoint", "Int64", x==""||y==""?0*DllCall("GetCursorPos","Int64*",pt)+pt:x&0xFFFFFFFF|y<<32, "Ptr*", pacc, "Ptr", VarSetCapacity(varChild,8+2*A_PtrSize,0)*0+&varChild)=0
Return ComObjEnwrap(9,pacc,1), _idChild_:=NumGet(varChild,8,"UInt")
}
И тут как?
И тут
DllCall("User32.dll\GetDesktopWindow", "ptr")
Можно проверять, что возвращает функция, если 0, то создавать action.
То есть просто проверять A_LastError после accDefaultAction?
По идее, правильней uint, но чтобы не запутываться можно смело ptr.
Для окон, не контролов?
Контролы, тоже окна, так что для них тоже "uptr".
То есть просто проверять A_LastError после accDefaultAction?
Да.
По идее, правильней uint, но чтобы не запутываться можно смело ptr.
Это для "Ptr*", hWnd и DllCall("User32.dll\GetDesktopWindow", "ptr")?
П почему uint если говорили про uptr? Я запутался.
В gdip везде uptr, для всего подряд.
Контролы, тоже окна, так что для них тоже "uptr".
То есть в системе может быть не более 32000 включая контролы?
Я сам толком не понимаю, но после 32000 окон система начинает выдавать процессу UInt64 хендлы.
Автохотки UInt64 не поддерживает и конвертирует их в string.
Но при вызове dllcall мы отправляем уже не строку, а ptr, поэтому происходит ошибка.
А uptr, отсекает верхние биты.
Это для "Ptr*", hWnd и DllCall("User32.dll\GetDesktopWindow", "ptr")?
Тут можно использовать ptr и ptr*, главное потом посылать в dllcall как "uptr" и при сравнивании или при использовании этих значений в гуи отсекать верхние биты.
В DllCall заменил.
http://forum.script-coding.com/viewtopi … 25#p146925
У меня Format("0x{:x}", A_LastError) выдаёт 0xffffffff80020009
при сравнивании или при использовании этих значений в гуи отсекать верхние биты.
Автохотки UInt64 не поддерживает и конвертирует их в string.
А почему при сравнивании надо, пускай как строки сравнивает?
Автохотки UInt64 не поддерживает и конвертирует их в string.
Или ты про то что конвертирует когда отправляет в DllCall?
Так вроде норм сравнивает.
h1 := 0xffffffff80020009
h2 := 0xffffffff80020009
MsgBox % (h1 "" = h2)
Кстати в справке у msdn походу тоже непонятки....
Перепроверил, был не прав, вроде всегда, когда BSTR* = null, функция возвращает S_FALSE.
У меня Format("0x{:x}", A_LastError) выдаёт 0xffffffff80020009
Ну вот если выдает ошибку, то можно в action не вставлять.
Про сравнение, можно конечно сравнивать как строки, но, например, в таком случае не прокатит:
h1 := WinExist("A")
h2 := dllcall("GetForegroundWindow", "ptr")
MsgBox % (h1 "" = h2)
Перепроверил, был не прав
Это про UInt64 или про accDefaultAction?
можно в action не вставлять
Так сейчас и не вставляет. А по ссылке ты предлагал вставлять описание ошибки, и такой ошибки там нет.
в таком случае не прокатит
А если так?
h1 := WinExist("A")
h2 := dllcall("GetForegroundWindow", "ptr")
MsgBox % (h1 + 0 "" = h2 + 0 )
Я там поправил
MsgBox % (h1 + 0 "" = h2 + 0 )
Не знаешь что с WinExist? Когда отображено окно пуск выдаёт ноль.
1:: ToolTip % WinExist("ahk_class Shell_TrayWnd")
Это про UInt64 или про accDefaultAction?
Про accDefaultAction.
По ссылке ты предлагал вставлять описание ошибки
A_LasteError выдает нам 0x80020009 - DISP_E_EXCEPTION.
Описание ошибки можно так получать:
DllCall("LoadLibrary","str","oleacc","ptr")
loop
{
DllCall("oleacc\AccessibleObjectFromPoint", "Int64", x==""||y==""?0*DllCall("GetCursorPos","Int64*",pt)+pt:x&0xFFFFFFFF|y<<32, "Ptr*", pacc, "Ptr", VarSetCapacity(variant,8+2*A_PtrSize,0)*0+&variant)
if (A_PtrSize = 4)
tooltip % DllCall(NumGet(NumGet(pacc+0)+20*A_PtrSize), "ptr", pacc, "int64", NumGet(variant, 0, "int64"), "int64", NumGet(variant, 8, "int64"), "ptr*", name)
else
tooltip % DllCall(NumGet(NumGet(pacc+0)+20*A_PtrSize), "ptr", pacc, "ptr", &variant, "ptr*", name)
}
В таком случае не прокатит.
setbatchlines -1
loop 35000
{
gui, +hwndhwnd
if instr(hwnd, "0xffffffff")
{
Gui Show, w100 h100
sleep 1000
h1 := WinExist("A")
h2 := dllcall("GetForegroundWindow", "ptr")
MsgBox % (h1 + 0 "" = h2 + 0)
}
tooltip % a_index
gui, destroy
}
Reload
Зачем мучиться, когда просто надо отсечь ненужные биты?
Не знаешь что с WinExist? Когда отображено окно пуск выдаёт ноль.
Автохотки использует доисторическое апи.
msgbox % DllCall("FindWindowEx", "ptr", 0, "ptr", 0, "str", "Shell_TrayWnd", "ptr", 0)
Протестировал с вивальди.
Багов не нашел.
Просто при переходе к главному родительскому элементу дерево перестраивается и с нашим алгоритмом (снизу вверх) путь не найти.
Можно для наглядности при таких случаях добавлять хендл окна и писать path not found.
В играх вкладка Control(не сама вкладка, а ее содержимое) уезжает вниз, из-за чего инфу оттуда не узнать.
В играх вкладка Control(не сама вкладка, а ее содержимое) уезжает вниз, из-за чего инфу оттуда не узнать.
Скриншот где...
Описание ошибки можно так получать:
Число только вижу.
Зачем мучиться, когда просто надо отсечь ненужные биты?
Почему мучаться, просто варианты попроще методом тыка.)
Багов не нашел.
Где, в AhkSpy?
при таких случаях добавлять хендл окна и писать path not found
А как понять что это тот случай?
Число только вижу.
И это число равняется:
[Error: hr=0x80020003 - Member not found.]
Но я думаю, что можно смело при "Format("0x{:x}", A_LastError) выдаёт 0xffffffff80020009" писать что,
"DISP_E_MEMBERNOTFOUND The specified object does not support this property.".
Багов не нашел в нашем алгоритме в AhkSpy.
А как понять что это тот случай?
Когда верхний хендл окна от которого идет acc path не равен хендлу главного окна.
Почему мучаться, просто варианты попроще методом тыка.)
А чем мой вариант сложен?
Сложно было понять в чем причина этого бага.
Помню, был очень зол на autohotkey в целом и на lexikos в частности, когда 2 дня потратил на поиск причины подобного поведения.
0xffffffff80020009
У меня -0x7FFDFFFD. И я так и не пойму зачем нам обрабатывать эту ошибку. Чем она так полезна или интересна...
Когда верхний хендл окна от которого идет acc path не равен хендлу главного окна.
Не, не понятно.
А чем мой вариант сложен?
Если новое писать то несложно. Если что-то переделывать, то надо переменные в скобки заносить, а так просто в конце пару символов добавить.
Можно сделать так, если (A_LastError != ""), то писать:
Action:
The specified object does not support this property
Если defaultaction = "", то писать:
Action:
Null * execute
Не, не понятно.
Например у vivaldi acc path начинается с hwnd контрола Chrome_RenderWidgetHostHWND.
Поэтому можно писать так:
path not found ▪ 0x045454 ▪ Chrome_WidgetWin_1
4.3.4.1.4.1.4 ▪ 0x0100AE ▪ Chrome_RenderWidgetHostHWND
Используй AccExplorer.
У меня он vivaldi под курсором не видит.
Открыл через выбор окна, а где там путь смотреть?
Вивальди, как и скайп сразу не выдаст весь путь.
http://forum.script-coding.com/viewtopi … 74#p153374
Открыл через выбор окна, а где там путь смотреть?
Получаешь дерево и считаешь.