Что нового

альтернативный способ переключения окон в windows

Alexey

Новичок
Сообщения
171
Репутация
0
AutoIT: 3.3.0.0
Описание: программа должна сделать возможным активирование предыдущего (следующего) окна на панели задач по нажатию комбинации клавиш. к примеру:
ctrl+win+1 - сделать активным (вывести на передний план) предыдущее окно панели задач (даже если оно свёрнуто)
ctrl+win+2 - сделать активным (вывести на передний план) следующее окно панели задач (даже если оно свёрнуто)
(если активно последнее окно на панели задач, то ctrl+win+2 переключает на первое)
(если активно первое окно на панели задач, то ctrl+win+1 переключает на последнее)

именно эта возможность в windows никак не реализована. есть пара очень похожих комбинаций: alt+esc и alt+shift+esc. обе осуществляют переход между окнами в том порядке, в котором они были запущены. но это не вполне то, что нужно (недостаток этого способа особенно ощущается при наличии 3-4 (и более) запущенных приложений). про жутко не удобный (для меня) alt+tab я вообще молчу
т.е., необходимо, чтобы окна переключались вне зависимости от того, какое из них когда было запущено - раньше или позже

как это всё реализовать мне не совсем ясно. по-видимому, готовый и скомпилированный скрипт в виде exe-файла прийдётся ставить в автозагрузку (во время работы прога будет висеть в трэе), чтобы он отслеживал только нажатия на ctrl+win+1 и ctrl+win+2

Примечания: не вполне уверен, уместна ли эта тема на данном форуме, если что - строго не судите, слишком уж хочется удобно работать с окнами в ОС 'Окна' :smile:
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 164
Репутация
2 329
Задачка интересная, но как бы удивительно это не звучало, мне вот одно неясно - почему именно «Ctrl + Win + 1/2»? это же неудобно. Вообщем вот сделал через «Win + Left/Right»:

Код:
#NoTrayIcon
#include <GUIToolbar.au3>
#include <Array.au3>
;

Opt("WinWaitDelay", 1)

$sExit_HT = "^+e" ;Выход по <Ctrl + Shift + E>
$sSwitchBack_HT = "#{Left}"
$sSwitchNext_HT = "#{Right}"

HotKeySet($sExit_HT, "_Exit")
HotKeySet($sSwitchBack_HT, "_SwitchTaskBarWindow_Proc")
HotKeySet($sSwitchNext_HT, "_SwitchTaskBarWindow_Proc")

While 1
	Sleep(1000)
WEnd

Func _SwitchTaskBarWindow_Proc()
	HotKeySet(@HotKeyPressed)
	
	Local $sActivate_Window = ""
	Local $aTaskBarWindows = _WinListTaskBarWindowsEx()
	
	If @error Then Return HotKeySet(@HotKeyPressed, "_SwitchTaskBarWindow_Proc")
	
	For $i = 1 To $aTaskBarWindows[0][0]
		If WinActive($aTaskBarWindows[$i][0]) Then
			Switch @HotKeyPressed
				Case $sSwitchBack_HT ;Предыдущее окно
					If $i = 1 Then $i = $aTaskBarWindows[0][0] + 1
					$sActivate_Window = $aTaskBarWindows[$i - 1][0]
				Case $sSwitchNext_HT ;Следующее окно
					If $i = $aTaskBarWindows[0][0] Then $i = 0
					$sActivate_Window = $aTaskBarWindows[$i + 1][0]
			EndSwitch
			
			ExitLoop
		EndIf
	Next
	
	;Не найдены активные окна...
	If $sActivate_Window = "" Then
		Switch @HotKeyPressed
			Case $sSwitchBack_HT
				;Активируем последнее окно
				$sActivate_Window = $aTaskBarWindows[$aTaskBarWindows[0][0]][0]
			Case $sSwitchNext_HT
				;Активируем первое окно
				$sActivate_Window = $aTaskBarWindows[1][0]
		EndSwitch
	EndIf
	
	WinActivate($sActivate_Window)
	HotKeySet(@HotKeyPressed, "_SwitchTaskBarWindow_Proc")
EndFunc

Func _WinListTaskBarWindowsEx()
	Local $hToolbar
	
	For $i = 2 To 4
		$hToolbar = ControlGetHandle("[CLASS:Shell_TrayWnd]", "", "ToolbarWindow32" & $i)
		If Not @error Then ExitLoop
	Next
	
	Local $aWinList = WinList()
	Local $aRet_List[$aWinList[0][0] + 1][2], $iToolbarButtonIndex
	
	For $i = $aWinList[0][0] To 1 Step -1
		If Not BitAND(WinGetState($aWinList[$i][1]), 2) Then ContinueLoop
		
		$iToolbarButtonIndex = _WinAPI_FindToolbarButton($aWinList[$i][1], $hToolbar)
		
		If $iToolbarButtonIndex > 0 Then
			$aRet_List[0][0] += 1
			$aRet_List[$aRet_List[0][0]][0] = $aWinList[$i][1]
			$aRet_List[$aRet_List[0][0]][1] = $iToolbarButtonIndex
		EndIf
	Next
	
	If $aRet_List[0][0] = 0 Then Return SetError(1, 0, 0)
	
	ReDim $aRet_List[$aRet_List[0][0] + 1][2]
	_ArraySort($aRet_List, 0, 1, $aRet_List[0][0], 1)
	
	Return $aRet_List
EndFunc

Func _WinAPI_FindWindowEx($hParent, $hChild, $sClass, $sWindow)
	; must create structs and use ptrs to account for passing a true NULL as classname or window title
	; simply using "wstr" and "" does NOT work
	Local $sStruct1, $sStruct2
	
	If $sClass = "" Then
		$sClass = 0
	Else
		$sStruct1 = DllStructCreate("wchar[256]")
		DllStructSetData($sStruct1, 1, $sClass)
		$sClass = DllStructGetPtr($sStruct1)
	EndIf
	
	If $sWindow = "" Then
		$sWindow = 0
	Else
		$sStruct2 = DllStructCreate("wchar[256]")
		DllStructSetData($sStruct2, 1, $sWindow)
		$sWindow = DllStructGetPtr($sStruct2)
	EndIf
	
	Local $aRet = DllCall("user32.dll", "hwnd", "FindWindowExW", "hwnd", $hParent, "hwnd", $hChild, "ptr", $sClass, "ptr", $sWindow)
	
	$sStruct1 = 0
	$sStruct2 = 0
	
	Return $aRet[0]
EndFunc

Func _WinAPI_FindToolbarButton($hWnd, $hTB)
	Local $iReturn = -1, $iPID, $hProcess, $sStruct = DllStructCreate("ptr")
	
	; open process owning toolbar control
	_WinAPI_GetWindowThreadProcessId($hTB, $iPID)
	
	$hProcess = _WinAPI_OpenProcess(0x410, False, $iPID)
	
	If $hProcess Then
		Local $iCount = _GUICtrlToolbar_ButtonCount($hTB)
		
		For $i = 0 To $iCount - 1
			Local $iID = _GUICtrlToolbar_IndexToCommand($hTB, $i)
			
			; button param is ptr to owner's window handle, stored in target process's memory space
			Local $dwData = _GUICtrlToolbar_GetButtonParam($hTB, $iID)
			
			; read the window handle from the explorer process
			Local $aRet = DllCall("kernel32.dll", "int", "ReadProcessMemory", "ptr", _
					$hProcess, "ptr", $dwData, "ptr", DllStructGetPtr($sStruct), "uint", 4, "uint*", 0)
			
			If $aRet[5] Then
				If $hWnd = DllStructGetData($sStruct, 1) Then
					$iReturn = $i
					ExitLoop
				EndIf
			EndIf
		Next
		
		_WinAPI_CloseHandle($hProcess)
	EndIf
	
	Return $iReturn
EndFunc

Func _Exit()
	Exit
EndFunc
 
Автор
A

Alexey

Новичок
Сообщения
171
Репутация
0
вот что пишет при первом же нажатии на win + right:

Line 49 (File "C:\al.au3"):
$sActivate_Window = $aTaskBarWindows[1][0]
$sActivate_Window = ^ERROR
Error: Array variable has incorrect number of subscripts or subscript dimension range exceeded.

а Ctrl + Win + 1/2 я привёл вот почему: запускать каждую из двух команд всё равно буду исключительно одной кнопкой (это максимально удобно, ради этого, по сути, всё и затевается). у меня это будут num 7 (previous window) и num 1 (next window). но если именно их прописать в скрипте, то будут конфликты с другими клавишами, поскольку num 7 - это также и home (а num 1 - end)
поэтому и прошу в коде прописать Ctrl + Win + 1/2, а я уже потом в другой спец. программе переназначу num 7 на ctrl+win+1, а num 1 - на ctrl+win+2
 

kaster

Мой Аватар, он лучший самый
Команда форума
Глобальный модератор
Сообщения
4 020
Репутация
622
хм. а у меня норм пашет. как win+left, так и win+right/
полезный скрипт
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5 379
Репутация
2 703
Это возможно происходит, если Taskbar пустой.
 

ZanMax

Тестер
Сообщения
120
Репутация
5
И у мну проблема :

Win - Right


Код:
>Running:(3.3.0.0):C:\Program Files\AutoIt3\autoit3.exe "D:\Activate.au3"    
D:\Activate.au3 (49) : ==> Array variable has incorrect number of subscripts or subscript dimension range exceeded.:
$sActivate_Window = $aTaskBarWindows[1][0]
$sActivate_Window = ^ ERROR
->12:08:06 AutoIT3.exe ended.rc:1
+>12:08:08 AutoIt3Wrapper Finished
>Exit code: 1    Time: 10.220
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 164
Репутация
2 329
Yashied [?]
Это возможно происходит, если Taskbar пустой.
Да, но это ещё зависит от системы, не везде одинаковы класс taskbar'а.

Поправил свой пример.
 
Автор
A

Alexey

Новичок
Сообщения
171
Репутация
0
Какая система? Я проверял только под WiXP SP2
у меня именно xp sp 2 pro

(при пустом task-bar и мысли не было проверять)

на исправленном примере ошибка не выскакивает вообще, но и переключений нет - ничего не происходит
(пробую нажимать и Ctrl+Win+1/2 и win+left / win+right, поскольку не понял, какие из них прописаны в новом примере)
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 164
Репутация
2 329
Alexey
А какой класс окна показывает Au3 Info Tool при наведении курсора над панелью задач (не над кнопками)?
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 164
Репутация
2 329
Поправил пример, пробуйте ещё раз.

Alexey [?]
пробую нажимать и Ctrl+Win+1/2 и win+left / win+right, поскольку не понял, какие из них прописаны в новом примере
«Win + Left/Right».
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 164
Репутация
2 329
Alexey [?]
пока не работает
А если сменить первый цикл в функции _WinListTaskBarWindowsEx() на это:
Код:
$hToolbar = ControlGetHandle("[CLASS:Shell_TrayWnd]", "", "ToolbarWindow323")


?
:scratch:

Да, и убедись что процесс предыдущего скрипта не запущен (Ctrl + Shift + E).
 
Автор
A

Alexey

Новичок
Сообщения
171
Репутация
0
А если сменить первый цикл в функции _WinListTaskBarWindowsEx() на это
заменил только строку 63 (правильно ? вроде бы больше нигде ничего не нужно было менять). теперь окна переключаются

а что мне нужно изменить, чтобы было не Win + Left/Right, а Ctrl+Win+1/2 ?
я правильно понимаю, что в строках 9 и 10 #{Left} и #{Right} надо поменять на ^#{1} и ^#{2} ?

ещё в одном моменте прошу помочь. нужно сделать, чтобы по нажатию Ctrl+Win+3 происходило переключение окон по типу alt+tab
т.е., к примеру, имеем 5 окон на task-bar. находясь во втором, при нажатии на Ctrl+Win+3, переключаемся на третий, при повторном нажатии - обратно на второй, при ещё одном нажатии - снова на третий и т.д.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 164
Репутация
2 329
Alexey [?]
заменил только строку 63 (правильно ? вроде бы больше нигде ничего не нужно было менять)
Я написал что заменить - весь этот цикл, нет смысла менять одну строчку, тогда цикл без причины будет вызываться.

я правильно понимаю, что в строках 9 и 10 #{Left} и #{Right} надо поменять на ^#{1} и ^#{2} ?
Да, но цифры не обязательно ставить в скобки.

т.е., к примеру, имеем 5 окон на task-bar. находясь во втором, при нажатии на Ctrl+Win+3, переключаемся на третий, при повторном нажатии - обратно на второй, при ещё одном нажатии - снова на третий и т.д.
Не понимаю логику. Как будет определяться куда далее нужно переключить? и Alt + Tab переключает окна в порядке последнего активного окна.
 
Автор
A

Alexey

Новичок
Сообщения
171
Репутация
0
Я написал что заменить - весь этот цикл, нет смысла менять одну строчку, тогда цикл без причины будет вызываться
я не понимаю, эту строчку нужно ставить вместо строк 62-64 ? или вместо 59-87 ? не забывай, пожалуйста, что для меня это тёмный лес. по возможности, пиши, если не сложно, буквально: допустим, строку
$hToolbar = ControlGetHandle("[CLASS:Shell_TrayWnd]", "", "ToolbarWindow323")
вставь вместо строк 95-98. и мне будет мгновенно всё ясно

Не понимаю логику. Как будет определяться куда далее нужно переключить?
я не программист, как и что будет определяться - без понятия. в просьбе я детально описал, что именно требуется. не знаю, как ещё объяснить этот элементарный процесс переключения с текущего активного окна на следующее и затем обратно
и Alt + Tab переключает окна в порядке последнего активного окна
alt+tab при этом отображает своё мелкое надоедливое окно. для меня это существенный недостаток
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 164
Репутация
2 329
Alexey [?]
не знаю, как ещё объяснить этот элементарный процесс переключения с текущего активного окна на следующее и затем обратно
Т.е просто переключать вперёд и назад? а если окно последнее?
Просто оно не совпадает с поведением «Alt + Tab», где циклический переключаются окна, а не вперёд и назад.

alt+tab при этом отображает своё мелкое надоедливое окно. для меня это существенный недостаток
Т.е значит всё же нужно переключение аналогично как у Alt + Tab, только без окна индикации?
 
Автор
A

Alexey

Новичок
Сообщения
171
Репутация
0
Т.е просто переключать вперёд и назад?
да, смысл именно в переключении только между двумя соседними кнопками на панели задач (текущей и следующей)
а если окно последнее?
переключать с него на первое при первом нажатии, и с первого на него - при втором нажатии
Просто оно не совпадает с поведением «Alt + Tab», где циклический переключаются окна, а не вперёд и назад
Т.е значит всё же нужно переключение аналогично как у Alt + Tab, только без окна индикации?
если в объяснениях я где-то выразился не корректно или не точно, прошу извинить. не будем тогда привязываться к поведению alt+tab
резюмирую, что конкретно нужно:
переключение с текущего окна на следующее (при первом нажатии комбинации клавиш),
при повторном нажатии на ту же комбинацию клавиш - переключение назад, на предыдущее окно и т.д.

всё-таки не сочти за труд подсказать мне, какие по счёту строки убрать, заменив их вот этой строкой:
$hToolbar = ControlGetHandle("[CLASS:Shell_TrayWnd]", "", "ToolbarWindow323")
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 164
Репутация
2 329
Alexey
Вообщем вот ещё одна версия. В ней хоткеи те которые изначально были заказаны, плюс (как бонус) переключение по Alt + Tab как обычно, но без окна-индикатора:

Код:
#NoTrayIcon
#include <GUIToolbar.au3>
#include <Array.au3>
;

Opt("WinWaitDelay", 1)

Global $sLast_NextBack_Window 	= ""

Global $sExit_HT 				= "^+e" ;Выход по <Ctrl + Shift + E>
Global $sSwitchBack_HT 			= "^#1" ;"#{Left}"
Global $sSwitchNext_HT 			= "^#2" ;"#{Right}"
Global $sSwitchNextBack_HT 		= "^#3"
Global $sSwitchAsAltTab_HT 		= "!{TAB}"

HotKeySet($sExit_HT, 			"_Exit")
HotKeySet($sSwitchBack_HT, 		"_SwitchTaskBarWindow_Proc")
HotKeySet($sSwitchNext_HT, 		"_SwitchTaskBarWindow_Proc")
HotKeySet($sSwitchNextBack_HT, 	"_SwitchTaskBarWindow_Proc")
HotKeySet($sSwitchAsAltTab_HT, 	"_SwitchTaskBarWindow_Proc") ;Это как опция (переключение как по Alt + Tab), можно опустить.

While 1
	Sleep(1000)
WEnd

Func _SwitchTaskBarWindow_Proc()
	HotKeySet(@HotKeyPressed)
	
	Local $sActivate_Window = ""
	Local $aTaskBarWindows = _WinListTaskBarWindowsEx(Number(@HotKeyPressed <> $sSwitchAsAltTab_HT))
	
	If @error Then Return HotKeySet(@HotKeyPressed, "_SwitchTaskBarWindow_Proc")
	
	For $i = 1 To $aTaskBarWindows[0][0]
		If WinActive($aTaskBarWindows[$i][0]) Then
			Switch @HotKeyPressed
				Case $sSwitchBack_HT, $sSwitchAsAltTab_HT ;Предыдущее окно
					If $i = 1 Then $i = $aTaskBarWindows[0][0] + 1
					$sActivate_Window = $aTaskBarWindows[$i - 1][0]
				Case $sSwitchNext_HT, $sSwitchAsAltTab_HT ;Следующее окно
					If $i = $aTaskBarWindows[0][0] Then $i = 0
					$sActivate_Window = $aTaskBarWindows[$i + 1][0]
				Case $sSwitchNextBack_HT ;Следующее <> Предыдущее окно
					If $sLast_NextBack_Window <> $aTaskBarWindows[$i][0] Then $sLast_NextBack_Window = ""
					
					If $sLast_NextBack_Window = "" Then
						If $i = $aTaskBarWindows[0][0] Then $i = 0
						$sActivate_Window = $aTaskBarWindows[$i + 1][0]
						$sLast_NextBack_Window = $sActivate_Window
					Else
						If $i = 1 Then $i = $aTaskBarWindows[0][0] + 1
						$sActivate_Window = $aTaskBarWindows[$i - 1][0]
						$sLast_NextBack_Window = ""
					EndIf
			EndSwitch
			
			ExitLoop
		EndIf
	Next
	
	;Не найдены активные окна...
	If $sActivate_Window = "" Then
		Switch @HotKeyPressed
			Case $sSwitchBack_HT
				;Активируем последнее окно
				$sActivate_Window = $aTaskBarWindows[$aTaskBarWindows[0][0]][0]
			Case Else
				;Активируем первое окно
				$sActivate_Window = $aTaskBarWindows[1][0]
		EndSwitch
	EndIf
	
	WinActivate($sActivate_Window)
	HotKeySet(@HotKeyPressed, "_SwitchTaskBarWindow_Proc")
EndFunc

Func _WinListTaskBarWindowsEx($iListSort=1)
	Local $hToolbar = ControlGetHandle("[CLASS:Shell_TrayWnd]", "", "ToolbarWindow323")
	If @error Then $hToolbar = ControlGetHandle("[CLASS:Shell_TrayWnd]", "", "ToolbarWindow322")
	If @error Then Return SetError(1, 0, 0)
	
	Local $aWinList = WinList()
	Local $aRet_List[$aWinList[0][0] + 1][2], $iToolbarButtonIndex
	
	For $i = 1 To $aWinList[0][0] Step 1
		If Not BitAND(WinGetState($aWinList[$i][1]), 2) Then ContinueLoop
		
		$iToolbarButtonIndex = _WinAPI_FindToolbarButton($aWinList[$i][1], $hToolbar)
		
		If $iToolbarButtonIndex > 0 Then
			$aRet_List[0][0] += 1
			$aRet_List[$aRet_List[0][0]][0] = $aWinList[$i][1]
			$aRet_List[$aRet_List[0][0]][1] = $iToolbarButtonIndex
		EndIf
	Next
	
	If $aRet_List[0][0] = 0 Then Return SetError(2, 0, 0)
	
	ReDim $aRet_List[$aRet_List[0][0] + 1][2]
	If $iListSort Then _ArraySort($aRet_List, 0, 1, $aRet_List[0][0], 1)
	
	Return $aRet_List
EndFunc

Func _WinAPI_FindWindowEx($hParent, $hChild, $sClass, $sWindow)
	; must create structs and use ptrs to account for passing a true NULL as classname or window title
	; simply using "wstr" and "" does NOT work
	Local $sStruct1, $sStruct2
	
	If $sClass = "" Then
		$sClass = 0
	Else
		$sStruct1 = DllStructCreate("wchar[256]")
		DllStructSetData($sStruct1, 1, $sClass)
		$sClass = DllStructGetPtr($sStruct1)
	EndIf
	
	If $sWindow = "" Then
		$sWindow = 0
	Else
		$sStruct2 = DllStructCreate("wchar[256]")
		DllStructSetData($sStruct2, 1, $sWindow)
		$sWindow = DllStructGetPtr($sStruct2)
	EndIf
	
	Local $aRet = DllCall("user32.dll", "hwnd", "FindWindowExW", "hwnd", $hParent, "hwnd", $hChild, "ptr", $sClass, "ptr", $sWindow)
	
	$sStruct1 = 0
	$sStruct2 = 0
	
	Return $aRet[0]
EndFunc

Func _WinAPI_FindToolbarButton($hWnd, $hTB)
	Local $iReturn = -1, $iPID, $hProcess, $sStruct = DllStructCreate("ptr")
	
	; open process owning toolbar control
	_WinAPI_GetWindowThreadProcessId($hTB, $iPID)
	
	$hProcess = _WinAPI_OpenProcess(0x410, False, $iPID)
	
	If $hProcess Then
		Local $iCount = _GUICtrlToolbar_ButtonCount($hTB)
		
		For $i = 0 To $iCount - 1
			Local $iID = _GUICtrlToolbar_IndexToCommand($hTB, $i)
			
			; button param is ptr to owner's window handle, stored in target process's memory space
			Local $dwData = _GUICtrlToolbar_GetButtonParam($hTB, $iID)
			
			; read the window handle from the explorer process
			Local $aRet = DllCall("kernel32.dll", "int", "ReadProcessMemory", "ptr", _
					$hProcess, "ptr", $dwData, "ptr", DllStructGetPtr($sStruct), "uint", 4, "uint*", 0)
			
			If $aRet[5] Then
				If $hWnd = DllStructGetData($sStruct, 1) Then
					$iReturn = $i
					ExitLoop
				EndIf
			EndIf
		Next
		
		_WinAPI_CloseHandle($hProcess)
	EndIf
	
	Return $iReturn
EndFunc

Func _Exit()
	Exit
EndFunc
 
Автор
A

Alexey

Новичок
Сообщения
171
Репутация
0
Вообщем вот ещё одна версия. В ней хоткеи те которые изначально были заказаны, плюс (как бонус) переключение по Alt + Tab как обычно, но без окна-индикатора
теперь ничего не работает - окна не переключаются, alt+tab - ничего не делает
 
Верх