Что нового

альтернативный способ переключения окон в 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,665
Репутация
2,463
Задачка интересная, но как бы удивительно это не звучало, мне вот одно неясно - почему именно «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,716
Это возможно происходит, если Taskbar пустой.
 

ZanMax

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

Win - Right
er.jpg


Код:
>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,665
Репутация
2,463
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,665
Репутация
2,463
Alexey
А какой класс окна показывает Au3 Info Tool при наведении курсора над панелью задач (не над кнопками)?
 

CreatoR

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

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

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,665
Репутация
2,463
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,665
Репутация
2,463
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,665
Репутация
2,463
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,665
Репутация
2,463
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 - ничего не делает
 
Верх