1

Тема: AHK: Получить хендлы всех вкладок браузера

Добра всем.
Скриптом, запускаю браузер в несколько профилей, для работы с одним ресурсом. Стартовой страницей в настройках указано открывать всегда пустую страницу, а переход на адрес осуществляется передачей его параметром браузеру при запуске. Далее автологин на сайт, размещение окна на рабочем столе и т.д. Но иногда бывает так, что вместе с адресом полученным при старте, открывается страница с прошлой позицией на ресурсе и автологин коннектит обе страницы на ресурс, что чревато баном, или схожими с этим проблемами. Решил отслеживать эти моменты при запуске:


SetTitleMatchMode, 2

ya := "Yandex"
cr := "Chrome"

WinGet, winVar, ID, %cr%
WinGet, ctrlVar, ControlListHwnd, ahk_id %winVar%

strSp := StrSplit(ctrlVar,"`n")

MsgBox,, Title, % "Main: " winVar "`n`nCount: " strSp.Length() "`n`nCtrlList:`n" ctrlVar

В Хроме прекрасно работает развязывая руки для следующих манипуляций, а в Яндексе всегда показывает только один контрол активной вкладки. Вся соль в расширениях, которые можно установить в эти браузеры, но Яндекс ведёт себя более стабильно в работе, поэтому прошу поделиться мудростью, как бы так проделать похожий трюк и с ним. Результат всегда одинаков в обоих режимах Яндекс-браузера.

2 (изменено: Malcev, 2016-08-24 11:28:12)

Re: AHK: Получить хендлы всех вкладок браузера

У хрома можно еще так получить:

SetBatchLines, -1
Window := "ahk_class Chrome_WidgetWin_1"
Role := 60  ; ROLE_SYSTEM_PAGETABLIST

AccObj := Acc_ObjectFromWindow(WinExist(Window))
; параметры, начиная со второго: Role, Name, Value
AccElem := SearchElement(AccObj, Role, "", "")
msgbox % AccElem.accChildCount - 1

SearchElement(ParentElement, params*)
{
   found := 1
   for k, v in params  {
      (k = 1 && ParentElement.accRole(0) != v && found := "")
      (k = 2 && ParentElement.accName(0) != v && found := "")
      (k = 3 && ParentElement.accValue(0) != v && found := "")
   }
   if found
      Return ParentElement
   
   for k, v in Acc_Children(ParentElement)
      if obj := SearchElement(v, params*)
         Return obj
}

У Яндекса, думаю, аналогично.

3

Re: AHK: Получить хендлы всех вкладок браузера

В хроме можно работать сразу с несколькими пользователями. Для каждого будут свой настройки.   https://support.google.com/chrome/answer/2364824?hl=ru
http://livelenta.com/wp-content/uploads/2015/02/rezhim-v-google-chrome_1.jpg

4

Re: AHK: Получить хендлы всех вкладок браузера

red2881, благодарю, но в первом посте я описал как раз этот момент:

KusochekDobra пишет:

Добра всем.
Скриптом, запускаю браузер в несколько профилей, для работы с одним ресурсом...

Malcev, AHK ругается, что происходит вызов несуществующей "Acc_ObjectFromWindow()" функции. =/

5

Re: AHK: Получить хендлы всех вкладок браузера

Нужен acc.ahk

;------------------------------------------------------------------------------
; Acc.ahk Standard Library
; by Sean
; Updated by jethrow:
; 	Modified ComObjEnwrap params from (9,pacc) --> (9,pacc,1)
; 	Changed ComObjUnwrap to ComObjValue in order to avoid AddRef (thanks fincs)
; 	Added Acc_GetRoleText & Acc_GetStateText
; 	Added additional functions - commented below
; 	Removed original Acc_Children function
;	Added Acc_Error, Acc_ChildrenByRole, & Acc_Get functions
; last updated 10/25/2012
;------------------------------------------------------------------------------

Acc_Init()
{
	Static	h
	If Not	h
		h:=DllCall("LoadLibrary","Str","oleacc","Ptr")
}
Acc_ObjectFromEvent(ByRef _idChild_, hWnd, idObject, idChild)
{
	Acc_Init()
	If	DllCall("oleacc\AccessibleObjectFromEvent", "Ptr", hWnd, "UInt", idObject, "UInt", idChild, "Ptr*", pacc, "Ptr", VarSetCapacity(varChild,8+2*A_PtrSize,0)*0+&varChild)=0
	Return	ComObjEnwrap(9,pacc,1), _idChild_:=NumGet(varChild,8,"UInt")
}

Acc_ObjectFromPoint(ByRef _idChild_ = "", x = "", y = "")
{
	Acc_Init()
	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)
{
	Acc_Init()
	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_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_GetStateText(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_SetWinEventHook(eventMin, eventMax, pCallback)
{
	Return	DllCall("SetWinEventHook", "Uint", eventMin, "Uint", eventMax, "Uint", 0, "Ptr", pCallback, "Uint", 0, "Uint", 0, "Uint", 0)
}

Acc_UnhookWinEvent(hHook)
{
	Return	DllCall("UnhookWinEvent", "Ptr", hHook)
}
/*	Win Events:

	pCallback := RegisterCallback("WinEventProc")
	WinEventProc(hHook, event, hWnd, idObject, idChild, eventThread, eventTime)
	{
		Critical
		Acc := Acc_ObjectFromEvent(_idChild_, hWnd, idObject, idChild)
		; Code Here:

	}
*/

; Written by jethrow
Acc_Role(Acc, ChildId=0) {
	try return ComObjType(Acc,"Name")="IAccessible"?Acc_GetRoleText(Acc.accRole(ChildId)):"invalid object"
}
Acc_State(Acc, ChildId=0) {
	try return ComObjType(Acc,"Name")="IAccessible"?Acc_GetStateText(Acc.accState(ChildId)):"invalid object"
}
Acc_Location(Acc, ChildId=0, byref Position="") { ; adapted from Sean's code
	try Acc.accLocation(ComObj(0x4003,&x:=0), ComObj(0x4003,&y:=0), ComObj(0x4003,&w:=0), ComObj(0x4003,&h:=0), ChildId)
	catch
		return
	Position := "x" NumGet(x,0,"int") " y" NumGet(y,0,"int") " w" NumGet(w,0,"int") " h" NumGet(h,0,"int")
	return	{x:NumGet(x,0,"int"), y:NumGet(y,0,"int"), w:NumGet(w,0,"int"), h:NumGet(h,0,"int")}
}
Acc_Parent(Acc) { 
	try parent:=Acc.accParent
	return parent?Acc_Query(parent):
}
Acc_Child(Acc, ChildId=0) {
	try child:=Acc.accChild(ChildId)
	return child?Acc_Query(child):
}
Acc_Query(Acc) { ; thanks Lexikos - www.autohotkey.com/forum/viewtopic.php?t=81731&p=509530#509530
	try return ComObj(9, ComObjQuery(Acc,"{618736e0-3c3d-11cf-810c-00aa00389b71}"), 1)
}
Acc_Error(p="") {
	static setting:=0
	return p=""?setting:setting:=p
}
Acc_Children(Acc) {
	if ComObjType(Acc,"Name") != "IAccessible"
		ErrorLevel := "Invalid IAccessible Object"
	else {
		Acc_Init(), 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:
		} else
			ErrorLevel := "AccessibleChildren DllCall Failed"
	}
	if Acc_Error()
		throw Exception(ErrorLevel,-1)
}
Acc_ChildrenByRole(Acc, Role) {
	if ComObjType(Acc,"Name")!="IAccessible"
		ErrorLevel := "Invalid IAccessible Object"
	else {
		Acc_Init(), 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(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)
}

6

Re: AHK: Получить хендлы всех вкладок браузера

Благодарю! В старом режиме работает. Но как получить HWND вкладок? И разъясните пожалуйста глупому эту запись:


(k = 1 && ParentElement.accRole(0) != v && found := "")

Если не сложно, дословно, что конкретно, пошагово выполняется.

7 (изменено: Malcev, 2016-08-25 02:21:47)

Re: AHK: Получить хендлы всех вкладок браузера

Перебираем роли элементов acc объекта, при нахождении нужного нам, возвращаем дочерний объект.
Хендл не получите, но можете закрыть ненужные вам вкладки.
А вообще для всеядности лучше так:

SetBatchLines, -1
Window := "ahk_class Chrome_WidgetWin_1"
Role := 60  ; ROLE_SYSTEM_PAGETABLIST

AccObj := Acc_ObjectFromWindow(WinExist(Window))
AccElem := SearchElement(AccObj, Role)
msgbox % AccElem.accChildCount - 1

SearchElement(ParentElement, Role)
{
   If (ParentElement.accRole(0) = Role)
      Return ParentElement

   for k, v in Acc_Children(ParentElement)
      if obj := SearchElement(v, Role)
         Return obj
}

8 (изменено: KusochekDobra, 2016-08-25 03:05:33)

Re: AHK: Получить хендлы всех вкладок браузера

Функционально-то понятно, что в цикле происходит прогон по массиву параметров сравниваемых со значениями, хранящимися в полях объекта, но по завершении цикла, "found" всегда будет равен 1, потому как операции присваивания иного значения не описано, или всегда пустое значение, если в конце строки присваивается "". Или, что более вероятно - я просто не улавливаю суть этой записи, к чему и был посвящён этот вопрос. Моё понимание описывает мне булевские операции внутри всех скобок цикла, но сам результат работы функции говорит об обратном. В общем, я запутался. Вы мне дали конфету, прозрачная упаковка которой явственно демонстрирует её начинку, но забыли показать, как эту упаковку вскрыть и теперь я большими, полными надежды глазами смотрю на неё глотая слюнки.
Простите, что не оправдал Ваших ожиданий, но я обычный нуб постигающий доселе неизвестное по справке.

Откуда взято "Role := 60" и где посмотреть значения для других ролей?
Как закрыть ненужные мне вкладки, имея лишь "accChildCount" - их количество?
Как узнать, какая из них - нужна мне?
Что обозначает комментарий - "; параметры, начиная со второго: Role, Name, Value" - ?
Как узнать другие поля объекта? Как узнать, какие вообще есть?
В библиотеке "Acc" нет "accName()" и "accValue()". Каким образом к ним идёт обращение в таком случае?

9

Re: AHK: Получить хендлы всех вкладок браузера

(k = 1 && ParentElement.accRole(0) != v && found := "")

Суть записи такова, что если k = 1 и роль не равна 60, то переменной found присваивается пустота.
Вот на более простом примере:

a:=1
found := 1
(a = 1 && found := 0)
msgbox % found

a:=0
found := 1
(a = 1 && found := 0)
msgbox % found

10

Re: AHK: Получить хендлы всех вкладок браузера

Вот как! Не знал о таком способе записи. Спасибо. Не поясните, чем она лучше:


a:=1
found := 1
if (a == 1)
	found := 0
msgbox % found

кроме однострочности? И было бы под завязку хорошо, если бы Вы до конца удовлетворили моё любопытство:

KusochekDobra пишет:

...
Откуда взято "Role := 60" и где посмотреть значения для других ролей?
Как закрыть ненужные мне вкладки, имея лишь "accChildCount" - их количество?
Как узнать, какая из них - нужна мне?
Что обозначает комментарий - "; параметры, начиная со второго: Role, Name, Value" - ?
Как узнать другие поля объекта? Как узнать, какие вообще есть?
В библиотеке "Acc" нет "accName()" и "accValue()". Каким образом к ним идёт обращение в таком случае?

11

Re: AHK: Получить хендлы всех вкладок браузера

Не поясните, чем она лучше

Да ничем наверное. Этот код писал teadrinker. И так было в оригинале.

Откуда взято "Role := 60" и где посмотреть значения для других ролей?

Это константы. Можно посмотреть через AhkSpy с нашего форума, либо тут их список:
https://autohotkey.com/board/topic/7730 … -09272012/

Как закрыть ненужные мне вкладки, имея лишь "accChildCount" - их количество?

Надо определиться, какое различие между нужными и ненужными, от него и отталкиваться.

Что обозначает комментарий - "; параметры, начиная со второго: Role, Name, Value" - ?

Что аргументы идут со второго параметра функции.

Как узнать другие поля объекта? Как узнать, какие вообще есть?

https://autohotkey.com/board/topic/7788 … 012-09-20/
https://github.com/Drugoy/Autohotkey-sc … Viewer.ahk

В библиотеке "Acc" нет "accName()" и "accValue()". Каким образом к ним идёт обращение в таком случае?

Это свойства:
https://msdn.microsoft.com/en-us/librar … .110).aspx

12 (изменено: KusochekDobra, 2016-08-26 02:11:47)

Re: AHK: Получить хендлы всех вкладок браузера

Очень исчерпывающе. Зачитался, ушёл по ссылкам МСДН, в итоге долго тупил, пока не обратил внимание, что "Acc_Children()" - возвращает массив дочерних объектов, а не просто объект. Тогда:


Acc_Children(AccElem)[2].accName(0)
Acc_Children(AccElem)[2].accValue(0)

дают мне значения для анализа, но, имена открывающихся вкладок всегда идентичны, потому как открывают один и тот же адрес, а значение в "accValue(0)" - всегда пустое. Остаётся делать вывод только на основании того, что значение в "accChildCount" больше 2 и ненужная мне вкладка браузера открывается всегда последней.

Скажите пожалуйста, можно ли закрыть вкладку на уровне объекта, или овчинка выделки не стоит и проще брать окно в фокус с последующим "Ctrl+ TabNum" => "Ctrl+W"?

13 (изменено: Malcev, 2016-08-26 11:51:20)

Re: AHK: Получить хендлы всех вкладок браузера

Так как в хроме отсутствует у кнопки "закрыть" action, то можно вычислить координаты кнопки "закрыть" и послать туда ControlClick.

SetBatchLines, -1
Window := "ahk_class Chrome_WidgetWin_1"
Role := 60  ; ROLE_SYSTEM_PAGETABLIST

f1::
AccObj := Acc_ObjectFromWindow(WinExist(Window))
AccElem := SearchElement(AccObj, Role)
count := AccElem.accChildCount
WinGetPos, Xwin, Ywin,,, %Window%  
loop % count - 2
{
   last := Acc_Children(AccElem)[count]
   oCoords := Acc_Location(Acc_Child(last, 4))
   X := oCoords.x - Xwin + oCoords.w//4
   Y := oCoords.y - Ywin + oCoords.h//2
   ControlClick, x%X% y%Y%, %Window%,,,,NA
   count--
   sleep 200
}
return


SearchElement(ParentElement, Role)
{
   If (ParentElement.accRole(0) = Role)
      Return ParentElement

   for k, v in Acc_Children(ParentElement)
      if obj := SearchElement(v, Role)
         Return obj
}

Либо приступить к изучению selenium и автоматизировать все действия с броузерами через него.
http://forum.script-coding.com/viewtopic.php?id=11530
https://autohotkey.com/boards/viewtopic … mp;t=11884

14

Re: AHK: Получить хендлы всех вкладок браузера

Бесконечно благодарен! Сделал закладку на этот топик. Обязательно ознакомлюсь с таким нужным материалом.