1 (изменено: sim8783, 2021-03-23 23:36:34)

Тема: AHK: аналог altTab для окон определенной программы

Необходимо сделать функцию переключения по кнопке Tab в окнах программы FL Studio 20.
Все окна с включенной функцией "Detached", что дает им уникальные ID.
Как я вижу алгоритм:
1. Запрос по WinGet List список всех существующих окон в системе.
2. Отсеить все окна кроме принадлежащих  процессу FL.exe(или PID).
3. Послать команду  открытия в первое окно по ID из списка, кроме активного.

Собственно я со своими мизерными знаниями только на первом пункте...
WinGet List выдает результаты найденых ID отдельно в каждую переменную по порядку var1, var2 и т.д. для каждого. Как эффективно это все сгруппировать в один список, что бы из него можно было отсеить несоответствующие процессу FL.exe?

Как на основе этого списка послать команду активации следующего окна после активного? WinActivate посылает по заголовку, а у меня ID окон, нужно перевести ID окон в их заголовки, только так?

2

Re: AHK: аналог altTab для окон определенной программы

Думаю копать нужно в этом направлении:

App := "FL Studio 20" ; Название окна

F2::
Winget, App_List, List, % App
Loop % App_List
{
 WinGet, Pid, Pid, % "ahk_id " App_List%A_Index%
 MsgBox % App_List%A_Index% " - " Pid
}
Return
Win10x64, AHK v1.1.37.01 (Unicode 64-bit) | AHK-Wiki | Переменные и выражения | RegEx101

3

Re: AHK: аналог altTab для окон определенной программы

__Михаил__ Не подходит. Вы предлагаете выборку по заголовку, но он меняется в зависимости от названия открытого проекта в программе. Было бы ладно, постоянно запрашивать заголовок, но у окон внутри свои заголовки(тоже меняются от содержания), в следствии чего вашь скрипт просто видит лишь саму FL, без окон внутри нее.
Нужна выборка именнопо ahk_exe или ahk_pid, но желательно первое, т.к. пид пеняется,  а FL.exe всегда тот же.

4

Re: AHK: аналог altTab для окон определенной программы

sim8783, а справку почитать самому никак?

5

Re: AHK: аналог altTab для окон определенной программы

Malcevэто разве ремесло просто чтение?! Мне кажется нужно не просто прочитать, а понять действие каждой отдельной функции, все ее ньансы и параметры, ее взаимодействие с другими функциями и исключения каждого такого взаимодействия, взаимодействие этих функций с функциями ОС и ее параметрами, и кучу остального.  Если вы про это мне говорите, то нет, извините, но у меня нет на данный момент лишних пары лет. Для вас может это все и просто выглядит, но не проецируйте пожалуйста свое виденье на других. Если бы все так легко могли освоить всю справу АХК, то и форума этого бы не существовало.

Я сделал что мог, дальше я упираюсь в недостаток опытности. И не знаю куда копать, т.к. функции которые могли бы работать  в данном случае, не могут решить конкретную задачу. В связи с этим я и спрашиваю у опытных людей, куда мне податся, я не прошу у вас писать за меня код, но и не против конечно, если вас это не обременяет. Просто помогите двинутся дальше.

6

Re: AHK: аналог altTab для окон определенной программы

https://www.autohotkey.com/docs/misc/WinTitle.htm

7 (изменено: sim8783, 2021-03-25 20:54:07)

Re: AHK: аналог altTab для окон определенной программы

Malcev Благодарю! Я пробовал это дописать к коду Михаила, вместо заголовка, но ввел FL.exe вместо FL64.exe, и из-за неуверенности не стал больше пробовать. Сейчас все заработало. Отдельная благодарность Михаилу за идею с циклом!

В общем дошел до такой задачки:

#SingleInstance force

^TAB::
Winget, App_List, List, ahk_exe FL64.exe

Loop % App_List
{
NextW:= 2 
active:= % App_List%NextW%
WinActivate ahk_id %active% 

KeyWait, TAB, ,T2
sleep 2000
 
 
agen:
 Loop 
 {
 Fw:= A_Index+NextW 
 active2:= % App_List%Fw%
  WinActivate  ahk_id %active2% 
  
KeyWait, TAB, ,T2
 sleep 2000
 goto agen
  
  return
 }

Мне необходимо запускать этот скрипт по клавши таб, одно нажатие - одно переключение окна, но не одних и тех же, а следующего, как в windows. Собственно такая задумка, что скрипт сначала переключает на одно окно, и если в течении 2 секунд небыло повторного переключения, то он останавливается. Если было еще нажатие в этот период - он переходит в следующий цикл с активацией второго по порядку окна из списка App_List, так же ждет 2 секунды, и если было переключение на следующее окно, то скрипт прыгает на начало второй петли и так до конца(что в конце пока не ясно, нужно все заного как то начать, но пока в другом задача). Так вот задача, что когда я нажимаю кнопку таб, то я всегда активирую скрипт сначала, в следствии чего я активирую по очереди только два последних окна, а не все из списка.
Как сделать, что бы горячая клавиша перестала быть "горячей" на время исполнения цикла? Или ваш вариант решения задачи.

P.s: "KeyWait, TAB, ,T2sleep 2000" написано условно, для обозначения места ожидания\перехода.

8

Re: AHK: аналог altTab для окон определенной программы

Malcev
Недавно сам искал аналог altTab (для всех окон). Пробовал такое, и другое, все иногда ошибаются, чему нашёл подтверждение на нескольких форумах, ответа не нашёл, так и остался на Send {LAlt Down}{Tab}{LAlt Up}.
Я не понял как получить Z order который используется в altTab.

В данной задаче (если знать Z order) достаточно активировать следующее окно в Z order принадлежащее процессу, твою ссылку на WinTitle, как на ключ к решению вопроса, я тоже не понял.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

9 (изменено: Malcev, 2021-03-25 22:04:53)

Re: AHK: аналог altTab для окон определенной программы

serzh82saratov пишет:

Я не понял как получить Z order который используется в altTab.

https://devblogs.microsoft.com/oldnewth … 0/?p=42093
https://devblogs.microsoft.com/oldnewth … 0/?p=24863
Возможно в современном виндовсе надо проверять еще cloak и window band.

serzh82saratov пишет:

твою ссылку на WinTitle, как на ключ к решению вопроса, я тоже не понял.

Я отвечал на вопрос

sim8783 пишет:

Нужна выборка именнопо ahk_exe или ahk_pid, но желательно первое, т.к. пид пеняется,  а FL.exe всегда тот же.

10

Re: AHK: аналог altTab для окон определенной программы

Я таким пользуюсь:

arr := EnumerateAltTabWindows()
for k, v in arr {
   WinGetClass, winClass, ahk_id %v%
   WinGetTitle, title, ahk_id %v%
   altTabWindows .= A_Index . ".`nTitle: " . title . "`nClass: " . winClass . "`nhWnd: " . v . "`n`n"
}
MsgBox, % altTabWindows

EnumerateAltTabWindows() {
   AltTabList := []
   WinGet, list, List
   Loop % list
      if IsAltTabWindow(list%A_Index%)
         AltTabList.Push(list%A_Index%)
   Return AltTabList
}

IsAltTabWindow(hWnd) {
   static GA_ROOTOWNER := 3, WS_EX_APPWINDOW := 0x40000, WS_EX_TOOLWINDOW := 0x80, DWMWA_CLOAKED := 14
   if !DllCall("IsWindowVisible", "Ptr", hWnd)
      Return false
   
   hOwner := DllCall("GetAncestor", "Ptr", hWnd, "UInt", GA_ROOTOWNER, "Ptr")
   hPopup := DllCall("GetLastActivePopup", "Ptr", hOwner, "Ptr")
   if (hOwner = hWnd && hPopup != hWnd)
      Return false
   
   WinGet, exStyles, ExStyle, ahk_id %hWnd%
   if (exStyles & WS_EX_TOOLWINDOW) && !(exStyles & WS_EX_APPWINDOW)
      Return false
   
   DllCall("DwmApi\DwmGetWindowAttribute", "Ptr", hWnd, "UInt", DWMWA_CLOAKED, "UIntP", cloaked, "UInt", 4)
   Return !cloaked
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

11

Re: AHK: аналог altTab для окон определенной программы

В этом коде используется z-order, а не порядок активации окон.

12

Re: AHK: аналог altTab для окон определенной программы

У меня соответствует тому, что я вижу в окне AltTab. За исключением того, что вот сейчас появилось невидимое окно ApplicationManager_ImmersiveShellWindow, которое всегда на первом месте, потом пропало, надо с ним ещё разобраться.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

13

Re: AHK: аналог altTab для окон определенной программы

Это получается, когда окно Корзины открыто.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

14

Re: AHK: аналог altTab для окон определенной программы

Наверно, его просто исключить нужно.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

15

Re: AHK: аналог altTab для окон определенной программы

Ну например у меня не соответсвует если notepad++ поставить always on top - всегда будет первым.
Либо открыть любое окно с более высоким band, например taskmgr.

16

Re: AHK: аналог altTab для окон определенной программы

А есть способ получать окна в порядке активации?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

17

Re: AHK: аналог altTab для окон определенной программы

Думаю, только если хук ставить и отслеживать.
Но лично мне непонятно зачем вообще использовать аналог alttab вместо alttab.

18

Re: AHK: аналог altTab для окон определенной программы

Предполагаю, что может понадобиться для каких-то целей просто получить список AltTab окон.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

19

Re: AHK: аналог altTab для окон определенной программы

Ну если нужны только имена, то можно через открытие alttab окна и UI Automation.
Msdn пишет, что можно (было раньше?) через GetAltTabInfo.
Но у меня ее вызвать не получается.

20

Re: AHK: аналог altTab для окон определенной программы

Так это тоже, если только окно AltTab открыто.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

21

Re: AHK: аналог altTab для окон определенной программы

Ну да. Окно AltTab можно перекрыть снимком экрана.

22

Re: AHK: аналог altTab для окон определенной программы

Наверно, но к таким костылям стоит прибегать лишь в крайнем случае, проще мой код выше дополнить исключением определённых окон.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

23

Re: AHK: аналог altTab для окон определенной программы

Так в твоем коде окна не по порядку идут.

24

Re: AHK: аналог altTab для окон определенной программы

Ну так порядок как раз не особо важен, если не делаешь «аналог AltTab».

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

25

Re: AHK: аналог altTab для окон определенной программы

А есть у вас соображения по поводу моего вопроса http://forum.script-coding.com/viewtopic.php?pid=146919#p146919 с горячей клавишей и ее блокировкой на время выполнения лупа?

26

Re: AHK: аналог altTab для окон определенной программы

https://www.autohotkey.com/docs/commands/Hotkey.htm

27

Re: AHK: аналог altTab для окон определенной программы

MalcevЗабавно, вроде самая говорящая за название AutoHotKey функция, а раньше не прегождалась) Благодарю!

Вообщем все теперь работает как нужно, изначально думал GUI со скриншетами сделать, как в windows, но почти сразу отказался от этой затеи, да и стандартное переключение в FL по Tab для не "Detach"нных окон работает без предпросмотра.
Работает скрипт как и стандартный переключаетель FL. Если листать с интервалом меньше секунды, то листает по поряду, а если нажать через секунду после предыдущего раза, то откроет предидущее окно, как и в windows. Если вдруг кому пригодится вот код, можно адоптировать под любую программу или как альтернативу встроенному в windows:

TAB::
start:
Winget, App_List, List, ahk_exe FL64.exe,,,ahk_class TFruityLoopsMainForm

Loop % App_List
{
NextW:= 2 
active:= % App_List%NextW%
WinActivate ahk_id %active% 
Sleep 50
Hotkey, TAB,, Off
KeyWait, TAB, D, T1

if errorlevel
            {
Hotkey, TAB,, On
return
             }
  else
  {
agen:
 Loop 
    {
 Fw:= A_Index+NextW 

 if ( Fw < App_List )

                 {

 active2:= % App_List%Fw%
 WinActivate  ahk_id %active2% 
 Sleep 50
 KeyWait, TAB, D, T1

if errorlevel
        {
Hotkey, TAB,, On
return
        }
                 }
                       else
                       {
                       Hotkey, TAB,, On
                       goto start
                       return
                       }
    } ;loop2
  }

} ;loop1
 
return

28 (изменено: Malcev, 2023-08-23 14:33:58)

Re: AHK: аналог altTab для окон определенной программы

teadrinker пишет:

У меня соответствует тому, что я вижу в окне AltTab. За исключением того, что вот сейчас появилось невидимое окно ApplicationManager_ImmersiveShellWindow, которое всегда на первом месте, потом пропало, надо с ним ещё разобраться.

У меня пока так вроде идентично показывает, хотя надо еще тестировать:

arr := EnumerateAltTabWindows()
for k, v in arr {
   WinGetClass, winClass, ahk_id %v%
   WinGetTitle, title, ahk_id %v%
   altTabWindows .= A_Index . ".`nTitle: " . title . "`nClass: " . winClass . "`nhWnd: " . v . "`n`n"
}
MsgBox, % altTabWindows


EnumerateAltTabWindows() {
   AltTabList := []
   WinGet, list, List
   Loop % list
      if IsAltTabWindow(list%A_Index%)
         AltTabList.Push(list%A_Index%)
   Return AltTabList
}

IsAltTabWindow(hWnd)
{
   static GA_ROOTOWNER := 3, WS_EX_APPWINDOW := 0x40000, WS_EX_TOOLWINDOW := 0x80, DWMWA_CLOAKED := 14
   if !DllCall("IsWindowVisible", "uptr", hWnd)
      return
   DllCall("DwmApi\DwmGetWindowAttribute", "uptr", hWnd, "uint", DWMWA_CLOAKED, "uint*", cloaked, "uint", 4)
   if cloaked
      return
   WinGet, exStyles, ExStyle, ahk_id %hWnd%
   if (exStyles & WS_EX_APPWINDOW)
      return true
   if (exStyles & WS_EX_TOOLWINDOW)
      return
   hwndWalk := DllCall("GetAncestor", "uptr", hWnd, "uint", GA_ROOTOWNER, "ptr")
   loop
   {
      hwndTry := DllCall("GetLastActivePopup", "uptr", hwndWalk, "ptr")
      if (realHwnd(hwndTry) = realHwnd(hwndWalk))
      {
         if (realHwnd(hwndWalk) = realHwnd(hWnd)) or !DllCall("IsWindowVisible", "uptr", hwndWalk)
            return true
         return
      }
      if DllCall("IsWindowVisible", "uptr", hwndTry)
      {
         if (realHwnd(hwndWalk) = realHwnd(hWnd))
            return true
         return
      }
      hwndWalk := hwndTry
   }
}

realHwnd(hwnd)
{
   varsetcapacity(var, 8, 0)
   numput(hwnd, var, 0, "uint64")
   return numget(var, 0, "uint")
}

29 (изменено: teadrinker, 2023-08-22 23:22:06)

Re: AHK: аналог altTab для окон определенной программы

У меня после открытия окна скрытых трей-иконок начинает показывать

1.
Title:
Class: ApplicationManager_ImmersiveShellWindow
hWnd: 0x101aa

Malcev пишет:
realHwnd(hwnd)
{
   varsetcapacity(var, 8, 0)
   numput(hwnd, var, 0, "uint64")
   return numget(var, 0, "uint")
}

А не проще

realHwnd(hwnd)
{
   return hwnd & 0xFFFFFFFF
}

?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

30

Re: AHK: аналог altTab для окон определенной программы

Так не сработает тут:

setbatchlines -1
loop
{
   gui, +hwndhwnd
   if instr(hwnd, "0xffffffff")
   {
      msgbox % hwnd
      ; failed
      if (dllcall("IsWindow", "ptr", hwnd) = 0)
         msgbox dllcall failed
      else
         msgbox dllcall ok

      ; ok
      if (dllcall("IsWindow", "ptr", realHwnd(hwnd)) = 0)
         msgbox dllcall failed
      else
         msgbox dllcall ok

      gui, % "child1: +Parent" realHwnd(hwnd)   ; ok
      gui, % "child2: +Parent" hwnd   ; failed
   }
   tooltip % a_index
   gui, destroy
}

realHwnd(hwnd)
{
   return hwnd & 0xFFFFFFFF
}

А что за "окно скрытых трей-иконок"?
И при нажатии alt+tab это окно присутствует?

31

Re: AHK: аналог altTab для окон определенной программы

ahk_class NotifyIconOverflowWindow.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

32

Re: AHK: аналог altTab для окон определенной программы

Malcev пишет:

Так не сработает

Да, ahk с такими большими числами работает ограниченно. Но если ты всё равно ориентируешься на 0xffffffff, как на строку, то проще так:

realHwnd(hwnd)
{
   Return "0x" . SubStr(hwnd, 11)
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

33

Re: AHK: аналог altTab для окон определенной программы

Раньше я каждый хендл пропускал через realHwnd(hwnd) для вызова dllcall и для сравнения, пока не обнаружил, что их можно отправлять, как "uptr" без конвертации.
А  вот это окно когда присутствует в списке скрипта, то в Alt+Tab листе также появляется?

1.
Title:
Class: ApplicationManager_ImmersiveShellWindow
hWnd: 0x101aa

Просто у себя я такого окна поймать не могу.

34

Re: AHK: аналог altTab для окон определенной программы

Нет, в том то и проблема, что оно в списке, который выдаёт функция, появляется, но его нет ни в Alt+Tab, ни в видимых окнах.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

35 (изменено: Malcev, 2023-10-08 06:11:20)

Re: AHK: аналог altTab для окон определенной программы

При тестах обнаружил, что alt-tab иногда показывает какие-то левые окна, которых невидно на экране и пользователю как бы не нужны.
Поэтому добавил закоментированную проверку на MonitorFromWindow:

arr := EnumerateAltTabWindows()
for k, v in arr {
   WinGetClass, winClass, ahk_id %v%
   WinGetTitle, title, ahk_id %v%
   altTabWindows .= A_Index "`nTitle: " title "`nClass: " winClass "`nhWnd: " v "`nLastActivePopup: " format("0x{:x}", GetLastActivePopup(v)) "`n`n"
}
MsgBox, % altTabWindows


EnumerateAltTabWindows() {
   AltTabList := []
   WinGet, list, List
   Loop % list
      if IsAltTabWindow(list%A_Index%)
         AltTabList.Push(list%A_Index%)
   Return AltTabList
}

IsAltTabWindow(hWnd)
{
   static WS_EX_APPWINDOW := 0x40000, WS_EX_TOOLWINDOW := 0x80, WS_EX_NOACTIVATE := 0x8000000, DWMWA_CLOAKED := 14, GA_PARENT := 1, GW_OWNER := 4, MONITOR_DEFAULTTONULL := 0
   if !DllCall("IsWindowVisible", "uptr", hWnd)
      return
   DllCall("DwmApi\DwmGetWindowAttribute", "uptr", hWnd, "uint", DWMWA_CLOAKED, "uint*", cloaked, "uint", 4)
   if cloaked
      return
   if (realHwnd(DllCall("GetAncestor", "uptr", hwnd, "uint", GA_PARENT, "ptr")) != realHwnd(DllCall("GetDesktopWindow", "ptr")))
      return
   ; if !DllCall("MonitorFromWindow", "uptr", hwnd, "uint", MONITOR_DEFAULTTONULL, "ptr")
   ;   return
   WinGet, exStyles, ExStyle, ahk_id %hWnd%
   if (exStyles & WS_EX_APPWINDOW)
      return true
   if (exStyles & WS_EX_TOOLWINDOW) or (exStyles & WS_EX_NOACTIVATE)
      return
   loop
   {
      hwnd := DllCall("GetWindow", "uptr", hwnd, "uint", GW_OWNER, "ptr")
      if !hwnd
         return true
      if DllCall("IsWindowVisible", "uptr", hwnd)
      {
         DllCall("DwmApi\DwmGetWindowAttribute", "uptr", hwnd, "uint", DWMWA_CLOAKED, "uint*", cloaked, "uint", 4)
         if !cloaked
            return
      }
      WinGet, exStyles, ExStyle, ahk_id %hwnd%
      if ((exStyles & WS_EX_TOOLWINDOW) or (exStyles & WS_EX_NOACTIVATE)) and !(exStyles & WS_EX_APPWINDOW)
         return
   }
}

GetLastActivePopup(hwnd)
{
   static GA_ROOTOWNER := 3
   hwnd := DllCall("GetAncestor", "uptr", hwnd, "uint", GA_ROOTOWNER, "ptr")
   hwnd := DllCall("GetLastActivePopup", "uptr", hwnd, "ptr")
   return hwnd
}

realHwnd(hwnd)
{
   varsetcapacity(var, 8, 0)
   numput(hwnd, var, 0, "uint64")
   return numget(var, 0, "uint")
}

teadrinker, может на твое окно "ApplicationManager_ImmersiveShellWindow" это тоже подействует?

36

Re: AHK: аналог altTab для окон определенной программы

Сейчас пока всё верно показывает.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

37 (изменено: Malcev, 2023-10-05 20:17:26)

Re: AHK: аналог altTab для окон определенной программы

Кстати, еще в alt-tab не показываются окна, которые имеют WS_EX_NOACTIVATE стиль и не имеют WS_EX_APPWINDOW.
Подправил предыдущий код.

38

Re: AHK: аналог altTab для окон определенной программы

Пересмотрел алгоритм.

39

Re: AHK: аналог altTab для окон определенной программы

А цикл там для чего?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

40

Re: AHK: аналог altTab для окон определенной программы

Потому что у parent может быть owner.

41 (изменено: Malcev, 2023-10-08 15:48:08)

Re: AHK: аналог altTab для окон определенной программы

Опять допилил код.
Как я понял, в alt-tab не появляются:
1) Child окна.
2) Невидимые окна (почему-то cloaked считаются за видимыми).
3) Имеющие стиль (WS_EX_TOOLWINDOW или WS_EX_NOACTIVATE) без WS_EX_APPWINDOW.
Owned окна рассматриваем отдельно.
Owned окна показываются когда:
1) Они имеют WS_EX_APPWINDOW.
2) Они являются первыми видимыми окнами c верха дерева owned->owned->...->owner, при этом ни одно вышестоящее окно не должно иметь стиля (WS_EX_TOOLWINDOW или WS_EX_NOACTIVATE) без WS_EX_APPWINDOW.
После чего получаем хендлы на окна через GetAncestor и GetLastActivePopup.
Но имхается мне, что это еще не последняя правка.

42 (изменено: Malcev, 2023-10-08 15:49:58)

Re: AHK: аналог altTab для окон определенной программы

Почему-то cloaked окна у alt-tab считаются за видимыми.
Косяки майкрософта...

43 (изменено: Malcev, 2023-10-16 20:36:48)

Re: AHK: аналог altTab для окон определенной программы

Моя борьба с alt-tab продолжается.
Поправил код.
Думаю, что если надоест обходными путями  - буду инжектиться в эксплорер и выуживать оттуда (не хотелось бы).

arr := EnumerateAltTabWindows()
for k, v in arr
{
   WinGetClass, winClass, ahk_id %v%
   WinGetTitle, title, ahk_id %v%
   altTabWindows .= A_Index "`nTitle: " title "`nClass: " winClass "`nhWnd: " v "`nLastActivePopup: " format("0x{:x}", GetLastActivePopup(v)) "`n`n"
}
MsgBox, % altTabWindows


EnumerateAltTabWindows()
{
   PrevDetectHiddenWindows := A_DetectHiddenWindows
   DetectHiddenWindows On
   AltTabList := []
   WinGet, list, List
   Loop % list
   {
      if IsAltTabWindow(list%A_Index%)
         AltTabList.Push(list%A_Index%)
   }
   DetectHiddenWindows % PrevDetectHiddenWindows
   Return AltTabList
}

IsAltTabWindow(hWnd)
{
   static WS_EX_APPWINDOW := 0x40000, WS_EX_TOOLWINDOW := 0x80, DWMWA_CLOAKED := 14, DWM_CLOAKED_SHELL := 2, WS_EX_NOACTIVATE := 0x8000000, GA_PARENT := 1, GW_OWNER := 4, MONITOR_DEFAULTTONULL := 0, VirtualDesktopAltTabFilter := "null", PropEnumProcEx := RegisterCallback("PropEnumProcEx", "Fast", 4)
   if (VirtualDesktopAltTabFilter = "null")
      RegRead, VirtualDesktopAltTabFilter, HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced, VirtualDesktopAltTabFilter
   if !DllCall("IsWindowVisible", "uptr", hWnd)
      return
   if (VirtualDesktopAltTabFilter != 0)
   {
      DllCall("DwmApi\DwmGetWindowAttribute", "uptr", hWnd, "uint", DWMWA_CLOAKED, "uint*", cloaked, "uint", 4)
      if (cloaked = DWM_CLOAKED_SHELL)
         return
   }
   if (realHwnd(DllCall("GetAncestor", "uptr", hwnd, "uint", GA_PARENT, "ptr")) != realHwnd(DllCall("GetDesktopWindow", "ptr")))
      return
   WinGetClass, winClass, ahk_id %hWnd%
   if (winClass = "Windows.UI.Core.CoreWindow")
      return
   if (winClass = "ApplicationFrameWindow")
   {
      varsetcapacity(ApplicationViewCloakType, 4, 0)
      DllCall("EnumPropsEx", "uptr", hWnd, "ptr", PropEnumProcEx, "ptr", &ApplicationViewCloakType)
      if (numget(ApplicationViewCloakType, 0, "int") = 1)   ; https://github.com/kvakulo/Switcheroo/commit/fa526606d52d5ba066ba0b2b5aa83ed04741390f
         return
   }
   ; if !DllCall("MonitorFromWindow", "uptr", hwnd, "uint", MONITOR_DEFAULTTONULL, "ptr")   ; test if window is shown on any monitor. alt-tab shows any window even if window is out of monitor.
   ;   return
   WinGet, exStyles, ExStyle, ahk_id %hWnd%
   if (exStyles & WS_EX_APPWINDOW)
   {
      if DllCall("GetProp", "uptr", hWnd, "str", "ITaskList_Deleted", "ptr")
         return
      if (VirtualDesktopAltTabFilter = 0) or IsWindowOnCurrentVirtualDesktop(hwnd)
         return true
      else
         return
   }
   if (exStyles & WS_EX_TOOLWINDOW) or (exStyles & WS_EX_NOACTIVATE)
      return
   loop
   {
      hwndPrev := hwnd
      hwnd := DllCall("GetWindow", "uptr", hwnd, "uint", GW_OWNER, "ptr")
      if !hwnd
      {
         if DllCall("GetProp", "uptr", hwndPrev, "str", "ITaskList_Deleted", "ptr")
            return
         if (VirtualDesktopAltTabFilter = 0) or IsWindowOnCurrentVirtualDesktop(hwndPrev)
            return true
         else
            return
      }
      if DllCall("IsWindowVisible", "uptr", hwnd)
         return
      WinGet, exStyles, ExStyle, ahk_id %hwnd%
      if ((exStyles & WS_EX_TOOLWINDOW) or (exStyles & WS_EX_NOACTIVATE)) and !(exStyles & WS_EX_APPWINDOW)
         return
   }
}

GetLastActivePopup(hwnd)
{
   static GA_ROOTOWNER := 3
   hwnd := DllCall("GetAncestor", "uptr", hwnd, "uint", GA_ROOTOWNER, "ptr")
   hwnd := DllCall("GetLastActivePopup", "uptr", hwnd, "ptr")
   return hwnd
}

IsWindowOnCurrentVirtualDesktop(hwnd)
{
   static IVirtualDesktopManager := ComObjCreate(CLSID_VirtualDesktopManager := "{AA509086-5CA9-4C25-8F95-589D3C07B48A}", IID_IVirtualDesktopManager := "{A5CD92FF-29BE-454C-8D04-D82879FB3F1B}")
   DllCall(NumGet(NumGet(IVirtualDesktopManager+0), 3*A_PtrSize), "ptr", IVirtualDesktopManager, "uptr", hwnd, "int*", onCurrentDesktop)   ; IsWindowOnCurrentVirtualDesktop
   return onCurrentDesktop
}

PropEnumProcEx(hWnd, lpszString, hData, dwData)
{
   if (strget(lpszString, "UTF-16") = "ApplicationViewCloakType")
   {
      numput(hData, dwData+0, 0, "int")
      return false
   }
   return true
}

realHwnd(hwnd)
{
   varsetcapacity(var, 8, 0)
   numput(hwnd, var, 0, "uint64")
   return numget(var, 0, "uint")
}

Кстати, время активации окна можно получить через недокументированную GetLastActivationTimestamp.
https://github.com/MScholtes/VirtualDes … top.cs#L93

44

Re: AHK: аналог altTab для окон определенной программы

Malcev пишет:

через недокументированную GetLastActivationTimestamp

Почему -ую? Я так понял, это метод интерфейса IApplicationView, который в целом недокументированный?

Malcev пишет:

буду инжектиться в эксплорер и выуживать оттуда

А разве антивирус спасибо скажет за такое?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

45

Re: AHK: аналог altTab для окон определенной программы

Именно так.
Антивирус наверняка будет не доволен.
Но для меня самая большая проблема - это то, что я в этом ни в зуб ногой.

46

Re: AHK: аналог altTab для окон определенной программы

Опять исправил код.
Обнаружил интересный баг:
https://github.com/kvakulo/Switcheroo/c … d04741390f

47

Re: AHK: аналог altTab для окон определенной программы

Malcev пишет:

время активации окна можно получить через недокументированную GetLastActivationTimestamp

А ты пробовал? У меня работает, но странно:

#Requires AutoHotkey v2

^1:: MsgBox GetLastActiveTime(WinExist('ahk_class Notepad'))

GetLastActiveTime(hwnd?) {
    static CLSID_ImmersiveShell           := '{C2F03A33-21F5-47FA-B4BB-156362A2F239}'
         , IID_IServiceProvider           := '{6D5140C1-7436-11CE-8034-00AA006009FA}'
         , IID_IApplicationViewCollection := '{1841C6D7-4F9D-42C0-AF41-8747538F10E5}'
         , VT_UNKNOWN := 13

    hwnd ?? hwnd := WinExist('A')
    IServiceProvider := ComObject(CLSID_ImmersiveShell, IID_IServiceProvider)
    IApplicationViewCollection := ComObjQuery(IServiceProvider, IID_IApplicationViewCollection, IID_IApplicationViewCollection)
    ComCall(GetViewForHwnd := 6, IApplicationViewCollection, 'Ptr', hwnd, 'PtrP', IApplicationView := ComValue(VT_UNKNOWN, 0))
    ComCall(GetLastActivationTimestamp := 23, IApplicationView, 'UIntP', &timestamp := 0)
    return timestamp // 10000000
}

Возвращает время в секундах, но не более 500, после этого обнуляется, и считается по новой. И непонятно, откуда отсчитывает.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

48

Re: AHK: аналог altTab для окон определенной программы

Если брать Int64*, то возвращает более внятное число, но тоже непонятно, откуда отсчитывается.
UPD: после перезагрузки соответствует A_TickCount, однако до этого не соответствовало.

#Requires AutoHotkey v2

^1:: MsgBox GetLastActiveTime() . '`n' . A_TickCount // 1000

GetLastActiveTime(hwnd?) {
    static CLSID_ImmersiveShell           := '{C2F03A33-21F5-47FA-B4BB-156362A2F239}'
         , IID_IServiceProvider           := '{6D5140C1-7436-11CE-8034-00AA006009FA}'
         , IID_IApplicationViewCollection := '{1841C6D7-4F9D-42C0-AF41-8747538F10E5}'
         , VT_UNKNOWN := 13

    hwnd ?? hwnd := WinExist('A')
    IServiceProvider := ComObject(CLSID_ImmersiveShell, IID_IServiceProvider)
    IApplicationViewCollection := ComObjQuery(IServiceProvider, IID_IApplicationViewCollection, IID_IApplicationViewCollection)
    ComCall(GetViewForHwnd := 6, IApplicationViewCollection, 'Ptr', hwnd, 'PtrP', IApplicationView := ComValue(VT_UNKNOWN, 0))
    ComCall(GetLastActivationTimestamp := 23, IApplicationView, 'Int64P', &timestamp := 0)
    return timestamp // 10000000
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

49

Re: AHK: аналог altTab для окон определенной программы

Я думаю там uint64 должно быть.

STDMETHOD(GetLastActivationTimestamp)(THIS_ ULONGLONG*) PURE;

https://www.pg-fl.jp/program/tips/nonamefn/iappview.htm

50

Re: AHK: аналог altTab для окон определенной программы

Не важно, Int64 достаточно. Но понял, в чём косяк. Расхождения с A_TickCount начинаются после спящего режима. Время во сне как бы не плюсуется ко времени, возвращаемому этой функцией.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

51

Re: AHK: аналог altTab для окон определенной программы

Я имел в виду, что у MScholtes "uint" - ошибка.

52

Re: AHK: аналог altTab для окон определенной программы

Это да. В целом, время активации этим определить можно, но только если с момента запуска скрипта не было режима сна.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

53

Re: AHK: аналог altTab для окон определенной программы

Я не тестировал, но можно при выходе из сна пробегаться по окнам и вызывать SetLastActivationTimestamp с A_TickCount.

54

Re: AHK: аналог altTab для окон определенной программы

Не поможет, если окно создано уже после этого.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

55

Re: AHK: аналог altTab для окон определенной программы

Тогда можно при создании или активации каждого окна вызывать SetLastActivationTimestamp.

56

Re: AHK: аналог altTab для окон определенной программы

Ну это уж совсем костыль. Если отслеживать время активаций, то зачем нужен этот метод.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

57

Re: AHK: аналог altTab для окон определенной программы

А вот GetShowInSwitchers, это не то, что надо для определения AltTab?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

58

Re: AHK: аналог altTab для окон определенной программы

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

59

Re: AHK: аналог altTab для окон определенной программы

Malcev пишет:

не показать точное время активации скрипта, а сравнить это время с другими окнами

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

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

60

Re: AHK: аналог altTab для окон определенной программы

Ну мы в начале топика говорили о получении окон в порядке активации.

teadrinker пишет:

А вот GetShowInSwitchers, это не то, что надо для определения AltTab?

Похоже оно самое.

61

Re: AHK: аналог altTab для окон определенной программы

Да, отличная находка.
Спасибо!
Кстати, сами майкрософтцы его не используют, а используют исключения:

newWindow.ClassName != "Windows.UI.Core.CoreWindow"

https://github.com/microsoft/PowerToys/ … ws.cs#L116

62 (изменено: Malcev, 2023-10-17 05:36:19)

Re: AHK: аналог altTab для окон определенной программы

С этим апи также приходится использовать исключения.

DetectHiddenWindows On
arr := EnumerateAltTabWindows()
for k, v in arr
{
   WinGetClass, winClass, ahk_id %v%
   WinGetTitle, title, ahk_id %v%
   altTabWindows .= A_Index "`nTitle: " title "`nClass: " winClass "`nhWnd: " v "`nLastActivePopup: " format("0x{:x}", GetLastActivePopup(v)) "`n`n"
}
MsgBox, % altTabWindows


EnumerateAltTabWindows()
{
   AltTabList := []
   WinGet, list, List
   Loop % list
   {
      if IsAltTabWindow(list%A_Index%)
         AltTabList.Push(list%A_Index%)
   }
   Return AltTabList
}

IsAltTabWindow(hwnd)
{
   static ImmersiveShell, IApplicationViewCollection, MONITOR_DEFAULTTONULL := 0, VirtualDesktopAltTabFilter := "null", PropEnumProcEx := RegisterCallback("PropEnumProcEx", "Fast", 4)
   if (VirtualDesktopAltTabFilter = "null")
   {
      RegRead, VirtualDesktopAltTabFilter, HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced, VirtualDesktopAltTabFilter
      OSbuildNumber := StrSplit(A_OSVersion, ".")[3]
      if (OSbuildNumber < 14393)
      {
         msgbox Your %A_OSVersion% can not handle virtual desktops
         exitapp
      }
      else if (OSbuildNumber <= 17134)   ; Windows 10 1607 to 1803 and Windows Server 2016
         IID_IApplicationViewCollection := "{2C08ADF0-A386-4B35-9250-0FE183476FCC}"
      else
         IID_IApplicationViewCollection := "{1841C6D7-4F9D-42C0-AF41-8747538F10E5}"
      if !(ImmersiveShell := ComObjCreate(CLSID_ImmersiveShell := "{C2F03A33-21F5-47FA-B4BB-156362A2F239}", IID_IUnknown := "{00000000-0000-0000-C000-000000000046}"))
      {
         MsgBox ImmersiveShell not supported.
         ExitApp
      }
      if !(IApplicationViewCollection := ComObjQuery(ImmersiveShell, IID_IApplicationViewCollection, IID_IApplicationViewCollection))
      {
         MsgBox IApplicationViewCollection interface not supported.
         ExitApp	
      }
   }
   WinGetClass, winClass, ahk_id %hWnd%
   if (winClass = "Windows.UI.Core.CoreWindow")
      return
   if (winClass = "ApplicationFrameWindow")
   {
      varsetcapacity(ApplicationViewCloakType, 4, 0)
      DllCall("EnumPropsEx", "uptr", hWnd, "ptr", PropEnumProcEx, "ptr", &ApplicationViewCloakType)
      if (numget(ApplicationViewCloakType, 0, "int") = 1)   ; https://github.com/kvakulo/Switcheroo/commit/fa526606d52d5ba066ba0b2b5aa83ed04741390f
         return
   }
   ; if !DllCall("MonitorFromWindow", "uptr", hwnd, "uint", MONITOR_DEFAULTTONULL, "ptr")   ; test if window is shown on any monitor. alt-tab shows any window even if window is out of monitor.
   ;   return
   DllCall(NumGet(NumGet(IApplicationViewCollection+0)+6*A_PtrSize), "ptr", IApplicationViewCollection, "uptr", hwnd, "ptr*", pView)   ; GetViewForHwnd
   if pView
   {
      DllCall(NumGet(NumGet(pView+0)+27*A_PtrSize), "ptr", pView, "int*", ShowInSwitchers)   ; GetShowInSwitchers
      ObjRelease(pView)
   }
   if ShowInSwitchers and ((VirtualDesktopAltTabFilter = 0) or IsWindowOnCurrentVirtualDesktop(hwnd))
      return true
   return
}

GetLastActivePopup(hwnd)
{
   static GA_ROOTOWNER := 3
   hwnd := DllCall("GetAncestor", "uptr", hwnd, "uint", GA_ROOTOWNER, "ptr")
   hwnd := DllCall("GetLastActivePopup", "uptr", hwnd, "ptr")
   return hwnd
}

IsWindowOnCurrentVirtualDesktop(hwnd)
{
   static IVirtualDesktopManager
   if !IVirtualDesktopManager
      IVirtualDesktopManager := ComObjCreate(CLSID_VirtualDesktopManager := "{AA509086-5CA9-4C25-8F95-589D3C07B48A}", IID_IVirtualDesktopManager := "{A5CD92FF-29BE-454C-8D04-D82879FB3F1B}")
   DllCall(NumGet(NumGet(IVirtualDesktopManager+0), 3*A_PtrSize), "ptr", IVirtualDesktopManager, "uptr", hwnd, "int*", onCurrentDesktop)   ; IsWindowOnCurrentVirtualDesktop
   return onCurrentDesktop
}

PropEnumProcEx(hWnd, lpszString, hData, dwData)
{
   if (strget(lpszString, "UTF-16") = "ApplicationViewCloakType")
   {
      numput(hData, dwData+0, 0, "int")
      return false
   }
   return true
}

Вариант без использования недокументированных функций (получаем окна только на активном virtual display):

DetectHiddenWindows On
arr := EnumerateAltTabWindows()
for k, v in arr
{
   WinGetClass, winClass, ahk_id %v%
   WinGetTitle, title, ahk_id %v%
   altTabWindows .= A_Index "`nTitle: " title "`nClass: " winClass "`nhWnd: " v "`nLastActivePopup: " format("0x{:x}", GetLastActivePopup(v)) "`n`n"
}
MsgBox, % altTabWindows


EnumerateAltTabWindows()
{
   AltTabList := []
   WinGet, list, List
   Loop % list
   {
      if IsAltTabWindow(list%A_Index%)
         AltTabList.Push(list%A_Index%)
   }
   Return AltTabList
}

IsAltTabWindow(hWnd)
{
   static WS_EX_APPWINDOW := 0x40000, WS_EX_TOOLWINDOW := 0x80, DWMWA_CLOAKED := 14, DWM_CLOAKED_SHELL := 2, WS_EX_NOACTIVATE := 0x8000000, GA_PARENT := 1, GW_OWNER := 4, MONITOR_DEFAULTTONULL := 0, VirtualDesktopExist, PropEnumProcEx := RegisterCallback("PropEnumProcEx", "Fast", 4)
   if (VirtualDesktopExist = "")
   {
      OSbuildNumber := StrSplit(A_OSVersion, ".")[3]
      if (OSbuildNumber < 14393)
         VirtualDesktopExist := 0
      else
         VirtualDesktopExist := 1
   }
   if !DllCall("IsWindowVisible", "uptr", hWnd)
      return
   DllCall("DwmApi\DwmGetWindowAttribute", "uptr", hWnd, "uint", DWMWA_CLOAKED, "uint*", cloaked, "uint", 4)
   if (cloaked = DWM_CLOAKED_SHELL)
      return
   if (realHwnd(DllCall("GetAncestor", "uptr", hwnd, "uint", GA_PARENT, "ptr")) != realHwnd(DllCall("GetDesktopWindow", "ptr")))
      return
   WinGetClass, winClass, ahk_id %hWnd%
   if (winClass = "Windows.UI.Core.CoreWindow")
      return
   if (winClass = "ApplicationFrameWindow")
   {
      varsetcapacity(ApplicationViewCloakType, 4, 0)
      DllCall("EnumPropsEx", "uptr", hWnd, "ptr", PropEnumProcEx, "ptr", &ApplicationViewCloakType)
      if (numget(ApplicationViewCloakType, 0, "int") = 1)   ; https://github.com/kvakulo/Switcheroo/commit/fa526606d52d5ba066ba0b2b5aa83ed04741390f
         return
   }
   ; if !DllCall("MonitorFromWindow", "uptr", hwnd, "uint", MONITOR_DEFAULTTONULL, "ptr")   ; test if window is shown on any monitor. alt-tab shows any window even if window is out of monitor.
   ;   return
   WinGet, exStyles, ExStyle, ahk_id %hWnd%
   if (exStyles & WS_EX_APPWINDOW)
   {
      if DllCall("GetProp", "uptr", hWnd, "str", "ITaskList_Deleted", "ptr")
         return
      if (VirtualDesktopExist = 0) or IsWindowOnCurrentVirtualDesktop(hwnd)
         return true
      else
         return
   }
   if (exStyles & WS_EX_TOOLWINDOW) or (exStyles & WS_EX_NOACTIVATE)
      return
   loop
   {
      hwndPrev := hwnd
      hwnd := DllCall("GetWindow", "uptr", hwnd, "uint", GW_OWNER, "ptr")
      if !hwnd
      {
         if DllCall("GetProp", "uptr", hwndPrev, "str", "ITaskList_Deleted", "ptr")
            return
         if (VirtualDesktopExist = 0) or IsWindowOnCurrentVirtualDesktop(hwndPrev)
            return true
         else
            return
      }
      if DllCall("IsWindowVisible", "uptr", hwnd)
         return
      WinGet, exStyles, ExStyle, ahk_id %hwnd%
      if ((exStyles & WS_EX_TOOLWINDOW) or (exStyles & WS_EX_NOACTIVATE)) and !(exStyles & WS_EX_APPWINDOW)
         return
   }
}

GetLastActivePopup(hwnd)
{
   static GA_ROOTOWNER := 3
   hwnd := DllCall("GetAncestor", "uptr", hwnd, "uint", GA_ROOTOWNER, "ptr")
   hwnd := DllCall("GetLastActivePopup", "uptr", hwnd, "ptr")
   return hwnd
}

IsWindowOnCurrentVirtualDesktop(hwnd)
{
   static IVirtualDesktopManager
   if !IVirtualDesktopManager
      IVirtualDesktopManager := ComObjCreate(CLSID_VirtualDesktopManager := "{AA509086-5CA9-4C25-8F95-589D3C07B48A}", IID_IVirtualDesktopManager := "{A5CD92FF-29BE-454C-8D04-D82879FB3F1B}")
   DllCall(NumGet(NumGet(IVirtualDesktopManager+0), 3*A_PtrSize), "ptr", IVirtualDesktopManager, "uptr", hwnd, "int*", onCurrentDesktop)   ; IsWindowOnCurrentVirtualDesktop
   return onCurrentDesktop
}

PropEnumProcEx(hWnd, lpszString, hData, dwData)
{
   if (strget(lpszString, "UTF-16") = "ApplicationViewCloakType")
   {
      numput(hData, dwData+0, 0, "int")
      return false
   }
   return true
}

realHwnd(hwnd)
{
   varsetcapacity(var, 8, 0)
   numput(hwnd, var, 0, "uint64")
   return numget(var, 0, "uint")
}

63

Re: AHK: аналог altTab для окон определенной программы

Только в таком варианте до того, как выдаст сообщение "Your %A_OSVersion% can not handle virtual desktops", выскочит ошибка из строки

static IVirtualDesktopManager := ComObjCreate ...
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

64 (изменено: Malcev, 2023-10-17 05:07:21)

Re: AHK: аналог altTab для окон определенной программы

Поправил. Без недокументированных функций я так и не обнаружил возможность определить из-за чего наложен cloak shell - из-за того, что окно бекграундное/системное или из-за того, что оно находится на другом виртуальном дисплее.