1

Тема: AHK: Почему в моём скрипте иногда hotkey перестаёт работать?

Здравствуйте.
Наблюдаю проблему в своём скрипте, когда по какой-то причине hotkey перестаёт работать без видимых на то причин.
Весь скрипт состоит из инициализации, различных хоткеев, функций и меток.
Проблема происходит со вполне конкретными хоткеями, так что привожу только часть скрипта: инициализацию, проблемные хоткеи и одну используемую метку.

;{ Initialization
#SingleInstance, Force
#UseHook, On
SetWinDelay, 0
SendMode, Input	; Recommended For new scripts due to its superior speed and reliability.
#NoEnv	; Recommended For performance and compatibility with future AutoHotkey releases.
CoordMode, Mouse	; By default all coordinates are relative to the active window, changing them to be related to screen is required for proper work of some script's commands.
#MaxThreadsBuffer, Off
	;{ Retrieve data from monitors.
SysGet, monCount, MonitorCount	; Retrieving the number of monitors.
Loop, %monCount%
{
	SysGet, mon%A_Index%, Monitor, %A_Index%	; Retrieving monitors' positions data.
	SysGet, UA%A_Index%, MonitorWorkArea, %A_Index%	; Getting Usable Area info.
	; Defining variables' values for later use.
	UA%A_Index%centerX := UA%A_Index%Left + (UA%A_Index%halfW := (UA%A_Index%Right - UA%A_Index%Left) / 2)
	UA%A_Index%centerY := UA%A_Index%Top + (UA%A_Index%halfH := (UA%A_Index%Bottom - UA%A_Index%Top) / 2)
}
	;}
; OnExit, Exit	; Remove hooks upon exit.
; Bind left click on tray icon to open menu like If you right clicked it.
Menu, Tray, Click, 1	; Enable single click action on tray.
GoSub, AddMenu	; Add new default menu.
WS_CAPTION := 0xC00000
WS_SIZEBOX := 0x40000
Return
;}
;{ Scroll inactive windows without activating them
; Tab Wheel Scroll For Notepad++, Miranda IM (TabSRMM), AkelPad and Internet Explorer.
; Can't do the same For Google Chrome and Mozilla Firefox.
$WheelUp::
$WheelDown::
	OutputDebug, % "A_ScriptName: '" A_ScriptName "' " "A_ThisHotkey: '" A_ThisHotkey "' start"
	
	MouseGetPos, m_x, m_y, id, control, 1
	If (A_PtrSize != 8)	; AHK is 32-bit.
		hw_m_target := DllCall("WindowFromPoint", "Int", m_x, "Int", m_y)
	Else	; AHK is 64-bit.
	{
		m_xy := ((m_y & 0xFFFFFFFF) << 32) | (m_x & 0xFFFFFFFF)
		hw_m_target := DllCall("WindowFromPoint", "Int64", m_xy, "UPtr")
	}
	WinGetClass, class, ahk_id %id%
	; WinGetClass, classA, A
	WinGet, idA, ID, A
	If (class == "Notepad++") && (control == "SysTabControl325")	; Notepad++: ahk_class Notepad++	tab-bar classNN: SysTabControl325	listener-window classNN: Scintilla1
	{
		OutputDebug, % "A_ScriptName: '" A_ScriptName "' " "A_ThisHotkey: '" A_ThisHotkey "' case: notepad++"
		ControlGet, properTargetWin, Hwnd,, Scintilla1, ahk_id %id%
		PostMessage 0x319, 0, (A_ThisHotkey == "$WheelUp") ? 0x10000 : 0x20000,, ahk_id %properTargetWin%
	}
	Else If (control == "TSTabCtrlClass1")	; Miranda IM, TabSRMM window's tab-bar classNN: TSTabCtrlClass1	listener-window classNN: RichEdit20W1
	{
		OutputDebug, % "A_ScriptName: '" A_ScriptName "' " "A_ThisHotkey: '" A_ThisHotkey "' case: tabsrmm"
		ControlGet, properTargetWin, Hwnd,, RichEdit50W1, ahk_id %id%
		PostMessage 0x319, 0, (A_ThisHotkey == "$WheelUp") ? 0x10000 : 0x20000,, ahk_id %properTargetWin%
	}
	Else If (class == "AkelPad4" && control == "SysTabControl321") || (class == "IEFrame" && control == "DirectUIHWND2")	; AkelPad tab bar
	{
		OutputDebug, % "A_ScriptName: '" A_ScriptName "' " "A_ThisHotkey: '" A_ThisHotkey "' case: akelpad tabbar"
		ControlSend,, % (A_ThisHotkey == "$WheelUp") ? ("{Ctrl Down}{Shift Down}{Tab}{Shift Up}{Ctrl Up}") : ("{Ctrl Down}{Tab}{Ctrl Up}"), ahk_id %id%
	}
	Else If (id != idA) && (class != "Progman")	; If the cursor hovers an inactive window (and which is not the desktop) - that inactive window should receive a scrolling event (without the activation of that window).
 	{
		OutputDebug, % "A_ScriptName: '" A_ScriptName "' " "A_ThisHotkey: '" A_ThisHotkey "' case: inactive window" " id: '" id "' " "control: '" control "' " "hw_m_target: '" hw_m_target "' " "m_x: '" m_x "' " "m_y: '" m_y "' " "class: '" class "' "
 		PostMessage, 0x20A, (A_ThisHotkey == "$WheelUp") ? 120 << 16 : -120 << 16, (m_y << 16) | (m_x & 0xFFFF),, ahk_id %hw_m_target%
 		; PostMessage, 0x20A, (A_ThisHotkey == "$WheelUp") ? 120 << 16 : -120 << 16, (m_y << 16) | (m_x & 0xFFFF),, ahk_id %id%
	}
	Else If (id == idA) || (class == "Progman")	; If the cursor hovers the active window or desktop - the regular scrolling event should get sent to the active window.
	{
		OutputDebug, % "A_ScriptName: '" A_ScriptName "' " "A_ThisHotkey: '" A_ThisHotkey "' case: active win"
		If (A_ThisHotkey == "$WheelUp")
			Send {WheelUp}
		Else
			Send {WheelDown}
	}
	GoSub, CleanVars
Return
	;}
CleanVars:
	currOpacity := hwndUnderCursor := x1 := y1 := winClass := maximized := winX1 := winY1 := x2 := y2 := winX2 := winY2 := ID := IDactive := control := key := value := Class := classA := properTargetWin := Hwnd := Height := m_x := m_y := hw_m_target := onTop := procName := XWin := YWin := title := ""
Return

;Open context menu via single left click on script's tray icon.
AddMenu:	; Add menuitem and make it default (bind to left click)
	Menu Tray, Add, ShowMenu		; Add temporary menu item.
	Menu Tray, Default, ShowMenu	; Set it to be default (it gets executed (or opened) just when you left click the tray icon).
Return

; Remove the created menuitem and show the normal menu. Then add the menuitem back The temporary menu item that 
ShowMenu:
	Menu Tray, Delete, ShowMenu	; If we don't delete that temporary menu item we created - it will be displayed in the tray icon's context menu and since clicking on it would just reopen the very same menu - we don't need it to be displayed, so we delete it.
	MouseGetPos MouseX, MouseY
	Menu Tray, Show, %MouseX%, % MouseY - 15	; By default context menu gets opened at cursor's position and to let user be able to double click the tray icon - we move this menu a 15px higher.
	GoSub, AddMenu	; We have to restore the deleted menu item for later use.
Return

	;{ Exit
Exit:	; Removing hooks upon exit.
	DllCall("UnhookWinEvent", Ptr, HWINEVENTHOOK1)
	DllCall("UnhookWinEvent", Ptr, HWINEVENTHOOK2)
	; Removing WS_EX_TOPMOST from every window we set it earlier.
	For k, v in GUIs
		WinSet, AlwaysOnTop, Off, % "ahk_id " v.Parent
	GUIs := ""
	ExitApp
	;}

Как видно из кода - в попытке разобраться что же чёрт возьми происходит - я добавил команды для отладки.
Это не помогло, т.к. когда происходит вышеописанный глюк - хоткеи тупо не срабатывают, так что в отладочном журнале в этот момент ничего не оседает.

Глюк исправляется только 2 способами: рестартом скрипта или же просто открытием меню его иконки в трее.
Журнал выполнения команд тоже не показывает ничего, что помогло бы в расследовании инцидента: судя по журналу, когда происходит глюк скрипт просто отдыхает: у строки Return хоткея просто продолжает тикать таймер, как будто новых нажатий хоткея не происходит.

Помогите разобраться, в чём может быть дело? Глюк появляется в произвольный момент, может прям во время кручения колеса (т.е. последовательного многократного wheelup/down), а может и в любой другой момент. Появляется не часто, несколько раз в день, никакого другого софта, который работал бы с колесом мыши - в системе не используется, да и "лечение" глюка тоже, в общем-то, странное.

p.s.: вырезанная часть скрипта никак не влияет на работу оставшегося куска кода.

2 (изменено: serzh82saratov, 2015-12-29 03:33:59)

Re: AHK: Почему в моём скрипте иногда hotkey перестаёт работать?

$WheelUp::
$WheelDown::SetTimer, Label, -10

Попробуйте то что выполняется по горячей клавише, запускать через метку по таймеру.

Ну и на всякий, если есть #UseHook, On, то не надо использовать $ в префиксе хоткея.

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

3

Re: AHK: Почему в моём скрипте иногда hotkey перестаёт работать?

Что показывает ListLines после зависания? Или ее невозможно открыть?

Win 10 x64
AHK
                       Справка AHK v1 тебе в помощь.

4

Re: AHK: Почему в моём скрипте иногда hotkey перестаёт работать?

serzh82saratov пишет:

Попробуйте то что выполняется по горячей клавише, запускать через метку по таймеру.

Спасибо, попробую.

serzh82saratov пишет:

Ну и на всякий, если есть #UseHook, On, то не надо использовать $ в префиксе хоткея.

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

Alectric пишет:

Что показывает ListLines после зависания? Или ее невозможно открыть?

Drugoy пишет:

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

5

Re: AHK: Почему в моём скрипте иногда hotkey перестаёт работать?

serzh82saratov пишет:

Попробуйте то что выполняется по горячей клавише, запускать через метку по таймеру.

Увы, глюк снова проявился.

6

Re: AHK: Почему в моём скрипте иногда hotkey перестаёт работать?

Похоже на какой-то баг AutoHotkey: с совершенно другим скриптом наблюдаются аналогичные проблемы.

7

Re: AHK: Почему в моём скрипте иногда hotkey перестаёт работать?

Возможно в синхронных посылах WinHttpRequest причина.
Попробуй посылать асинхронно - мне помогло.

8 (изменено: stealzy, 2016-01-02 00:21:00)

Re: AHK: Почему в моём скрипте иногда hotkey перестаёт работать?

Возможно, что у вас оказывается зажатой (логически) какая-либо клавиша, из-за чего хоткеи перестают работать.
1) После зависания понажимайте вручную все ваши Ctrl, Shift и Tab. Если скрипт "отвиснет" - причина найдена.
Либо попробуйте поставить * перед ними - *WheelUp::
2) Также поставьте ToolTip сразу после хоткеев, чтобы знать наверняка, срабатывают они или нет.

9 (изменено: Drugoy, 2016-01-02 06:18:50)

Re: AHK: Почему в моём скрипте иногда hotkey перестаёт работать?

Нда, зря я смешал две проблемы в 1 топик: скриншутер и правда срабатывает всегда, если закомментировать вызов функции upload(), а значит дело и правда в работе с сетью.
Malcev, я не умею, можешь показать как это делается?

stealzy, не, там дело не в логическом залипании модификаторов и отладку я провожу outputdebug'ом, а не tooltip'ами - так надёжней.

EDIT: Асинхронно отправлять научился, осталось только придумать как грамотно очередь сделать, но это уже оффтопик.