Тема: AHK: Свёртка приложений в трей
Его гоняючи (из своего, правда, постоянно загруженного), не раз сталкивался с поведением, когда скрипт "забывает" список хэндлов свёрнутых ранее окон, не реагируя на хоткей восстановления.
К счастью, пришла идея вписывать эти хэндлы в создаваемый при запуске скрипта файл и удалять при выходе из него.
К несчастью же, всякий раз при попытке идею самостоятельно воплотить раздаётся нестройный глухой стук .
Вмешаться прошу.
#SingleInstance Force
;установка иконки скрипта в трее с изображением окошек
Menu, Tray, Icon, %A_WinDir%\system32\Shell32.dll, 99
;максимальное количество окон, которое может быть скрыто
mwt_MaxWindows = 30
;горячая клавиша, скрывающая текущее окно
mwt_Hotkey = #h ;Win+H
;горячая клавиша, восстанавливающая все скрытые окна
mwt_HotkeyRestoreAll = ^!r ;Ctrl+Alt+R
;эта установка убирает "системные" пункты меню иконки скрипта в трее;
;если эти пункты нужны, задайте "Y" вместо "N"
mwt_StandardMenu = N
;следующие настройки позволяют избежать потребности отпускать и
;нажимать снова модификатор горячей клавиши всякий раз,
;когда вы хотите быстро скрыть более, чем одно окно
#HotkeyModifierTimeout 100
SetWinDelay 10
SetKeyDelay 0
;может быть запущено не более одного экземпляра сценария
#SingleInstance
;работать со скрытыми окнами
DetectHiddenWindows, On
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;назначение горячей клавиши, скрывающей текущее окно
Hotkey, %mwt_Hotkey%, mwt_Minimize
;назначение горячей клавиши, восстанавливающей все скрытые окна
Hotkey, %mwt_HotkeyRestoreAll%, mwt_RestoreAll
;при завершении сценария необходимо отобразить все скрытые окна, если таковые есть
OnExit, mwt_RestoreAllThenExit
;настройка меню иконки скрипта в трее
if mwt_StandardMenu = Y
Menu, Tray, Add
else
{
Menu, Tray, NoStandard
Menu, Tray, Add, Выход и восстановление всех скрытых окон, mwt_RestoreAllThenExit
}
Menu, Tray, Add, Восстановление всех скрытых окон, mwt_RestoreAll
Menu, Tray, Add
;ограничение ширины меню
if a_AhkVersion = ;пусто (для версий ранее 1.0.22)
mwt_MaxLength = 100
else
mwt_MaxLength = 260
return ;конец секции авто-выполнения скрипта
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;отсюда начинается исполнение, когда нажата горячая клавиша, скрывающая текущее окно
mwt_Minimize:
if mwt_WindowCount >= %mwt_MaxWindows%
{
MsgBox Не может быть скрыто одновременно больше, чем %mwt_MaxWindows% окон.
return
}
;получаем активное окно и используем его как "последнее найденное окно",
;т.е. окно по умолчанию для всех дальнейших вызовов функций, связанных с окнами
WinWait, A,, 2
if ErrorLevel <> 0
return
;получаем информацию об активном окне
WinGet, mwt_ActiveID, ID ;хэндл (HWND) окна
WinGetTitle, mwt_ActiveTitle ;заголовок окна
WinGetClass, mwt_ActiveClass ;имя класса окна
WinGet, mwt_ActivePID, PID ;идентификатор процесса, которому принадлежит окно
WinGet, mwt_List, List, ahk_pid %mwt_ActivePID% ;список всех окон этого процесса
WinGet, mwt_ActiveProcess, ProcessName ;имя процесса, которому принадлежит окно
;рабочий стол и панель задач скрывать нельзя
if mwt_ActiveClass in Shell_TrayWnd,Progman
return
;поскольку скрытие окна не будет деактивировать его, переключимся
;в другое окно (как будто пользователь нажал Alt+Escape)
Send, !{esc}
;собственно скрываем нужные окна процесса (делаем их невидимыми),
;скрытию подлежат все окна текущего процесса со стилем WS_VISIBLE,
;mwt_WindowsIDs - список хэндлов скрываемых окон,
;разделённых символом | (вертикальная черта)
mwt_WindowsIDs = ""
;проводник - особый случай, скрывать можно только текущее окно процесса
If mwt_ActiveProcess = explorer.exe
{
WinHide, ahk_id %mwt_ActiveID%
mwt_WindowsIDs = %mwt_WindowsIDs%|%mwt_ActiveID%
}
;скрываем все видимые окна процесса
Else
{
Loop, %mwt_List%
{
mwt_id := mwt_List%A_Index%
WinGet, style, Style, ahk_id %mwt_id%
if ( style & 0x10000000 ) ;WS_VISIBLE
{
WinHide, ahk_id %mwt_id%
;Result := DllCall("ShowWindow", "UInt", mwt_id, "Int", "0")
mwt_WindowsIDs = %mwt_WindowsIDs%|%mwt_id%
}
}
}
;если заголовок окна пуст, используем имя класса,
;т.к. нам нужен непустой заголовок пункта меню
if mwt_ActiveTitle =
mwt_ActiveTitle = ahk_class %mwt_ActiveClass%
;обрезаем заголовок окна до максимальной длины mwt_MaxLength
StringLeft, mwt_ActiveTitle, mwt_ActiveTitle, %mwt_MaxLength%
;обеспечение уникальности заголовка пункта меню:
Loop, %mwt_MaxWindows%
{
if mwt_WindowTitle%a_index% = %mwt_ActiveTitle%
{
;Нашли такой же заголовок, так что заголовок не уникален.
StringTrimLeft, mwt_ActiveIDShort, mwt_ActiveID, 2
StringLen, mwt_ActiveIDShortLength, mwt_ActiveIDShort
StringLen, mwt_ActiveTitleLength, mwt_ActiveTitle
mwt_ActiveTitleLength += %mwt_ActiveIDShortLength%
mwt_ActiveTitleLength += 1
if mwt_ActiveTitleLength > %mwt_MaxLength%
{
TrimCount = %mwt_ActiveTitleLength%
TrimCount -= %mwt_MaxLength%
StringTrimRight, mwt_ActiveTitle, mwt_ActiveTitle, %TrimCount%
}
;создаём уникальный заголовок:
mwt_ActiveTitle = %mwt_ActiveTitle% %mwt_ActiveIDShort%
break
}
}
;на всякий случай проверим, нет ли этого окна уже в списке ранее скрытых
mwt_AlreadyExists = n
Loop, %mwt_MaxWindows%
{
if mwt_WindowID%a_index% = %mwt_ActiveID%
{
mwt_AlreadyExists = y
break
}
}
;добавим окно в массив и в меню
if mwt_AlreadyExists = n
{
Menu, Tray, add, %mwt_ActiveTitle%, RestoreFromTrayMenu
mwt_WindowCount += 1
Loop, %mwt_MaxWindows% ;поиск свободного элемента в массиве
{
if mwt_WindowID%a_index% = ;элемент свободен
{
mwt_WindowID%a_index% = %mwt_ActiveID%
mwt_WindowTitle%a_index% = %mwt_ActiveTitle%
mwt_WindowPID%a_index% = %mwt_ActivePID%
mwt_WinIDs%a_index% = %mwt_WindowsIDs%
break
}
}
}
return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;отсюда начинается исполнение, когда вызвана команда меню иконки скрипта в трее,
;соответствующая конкретному скрытому окну
RestoreFromTrayMenu:
Menu, Tray, delete, %A_ThisMenuItem%
;поиск окна по его уникальному заголовку, сохраненному как название пункта меню
Loop, %mwt_MaxWindows%
{
if mwt_WindowTitle%a_index% = %A_ThisMenuItem%
{
StringTrimRight, IDToRestore, mwt_WindowID%a_index%, 0
StringTrimRight, PIDToRestore, mwt_WindowPID%a_index%, 0
mwt_WindowsIDs := mwt_WinIDs%a_index%
Loop, parse, mwt_WindowsIDs, |
WinShow, ahk_id %A_LoopField%
WinActivate ahk_id %IDToRestore%
mwt_WindowID%a_index% = ;очищаем элементы массивов
mwt_WindowTitle%a_index% =
mwt_WindowPID%a_index% =
mwt_WinIDs%a_index% =
mwt_WindowCount -= 1
break
}
}
return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;отсюда начинается исполнение, когда вызвана команда меню иконки скрипта в трее
;"Выход и восстановление всех скрытых окон"
mwt_RestoreAllThenExit:
Gosub, mwt_RestoreAll
ExitApp ;завершение работы скрипта
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;отсюда начинается исполнение, когда вызвана команда меню иконки скрипта в трее
;"Восстановление всех скрытых окон"
mwt_RestoreAll:
Loop, %mwt_MaxWindows%
{
if mwt_WindowID%a_index% <>
{
StringTrimRight, IDToRestore, mwt_WindowID%a_index%, 0
StringTrimRight, PIDToRestore, mwt_WindowPID%a_index%, 0
mwt_WindowsIDs := mwt_WinIDs%a_index%
Loop, parse, mwt_WindowsIDs, |
WinShow, ahk_id %A_LoopField%
WinActivate ahk_id %IDToRestore%
StringTrimRight, MenuToRemove, mwt_WindowTitle%a_index%, 0
Menu, Tray, delete, %MenuToRemove%
mwt_WindowID%a_index% =
mwt_WindowTitle%a_index% =
mwt_WindowPID%a_index% =
mwt_WinIDs%a_index% =
mwt_WindowCount -= 1
}
if mwt_WindowCount = 0
break
}
return