Orion9

|
Posted: Thu Apr 30, 2026 19:39 Post subject: |
|
|
FallenAngel
Я тут выше писал, что Autorun не боится поставленных задач, но правильнее было написать так: с тех пор, как Loopback добавил возможность работать с WinAPI через DllCall, Autorun вообще перестал бояться каких-либо задач ) Конечно, дело не только в DllCall. Есть еще объекты Buffer, Callback, побитовые операции и некоторые другие вещи, но в целом мощь Autorun зашкаливает.
И вот очень жаль, что болван об этом ничего не знает. Иначе можно было бы вытягивать из него готовый код как горячие пирожки из печки
Сами судите. Я последовательно вводил запросы, и каждый раз он мне выдавал готовые решения. Меня очень удивило то, что все решения оказались рабочими, именно рабочими, т.е. достаточно было просто сохранить файл и проверить его на работоспособность.
 1 | Code: | #Requires AutoHotkey v2.0
Persistent
; 1. Регистрация устройства (Мышь)
; RAWINPUTDEVICE структура
UsagePage := 1
Usage := 2
Flags := 0x00000100 ; RIDEV_INPUTSINK (получать ввод даже если окно не в фокусе)
hTarget := A_ScriptHwnd
RAWINPUTDEVICE := Buffer(8 + A_PtrSize, 0)
NumPut("UShort", UsagePage, RAWINPUTDEVICE, 0)
NumPut("UShort", Usage, RAWINPUTDEVICE, 2)
NumPut("UInt", Flags, RAWINPUTDEVICE, 4)
NumPut("Ptr", hTarget, RAWINPUTDEVICE, 8)
if !DllCall("RegisterRawInputDevices", "Ptr", RAWINPUTDEVICE, "UInt", 1, "UInt", 8 + A_PtrSize)
MsgBox "Ошибка регистрации Raw Input"
; 2. Подписка на сообщение WM_INPUT (0x00FF)
OnMessage(0x00FF, OnRawInput)
OnRawInput(wParam, lParam, msg, hwnd) {
; Определяем размер данных
size := 0
DllCall("GetRawInputData", "Ptr", lParam, "UInt", 0x10000003, "Ptr", 0, "UInt*", &size, "UInt", 8 + A_PtrSize * 2)
if (size > 0) {
rawBuffer := Buffer(size)
DllCall("GetRawInputData", "Ptr", lParam, "UInt", 0x10000003, "Ptr", rawBuffer, "UInt*", &size, "UInt", 8 + A_PtrSize * 2)
; Проверяем, что это мышь (тип 0)
type := NumGet(rawBuffer, 0, "UInt")
if (type == 0) {
; Смещение для данных мыши в структуре RAWINPUT
; header (16 байт на x86, 24 на x64) + данные мыши
headerSize := (A_PtrSize == 8) ? 24 : 16
flags := NumGet(rawBuffer, headerSize, "UShort")
lastX := NumGet(rawBuffer, headerSize + 8, "Int")
lastY := NumGet(rawBuffer, headerSize + 12, "Int")
ToolTip "Raw Delta X: " lastX " | Y: " lastY
}
}
} |
 2 | Code: | #Requires AutoHotkey v2.0
Persistent
; Регистрация Raw Input для мыши
RAWINPUTDEVICE := Buffer(8 + A_PtrSize, 0)
NumPut("UShort", 1, RAWINPUTDEVICE, 0)
NumPut("UShort", 2, RAWINPUTDEVICE, 2)
NumPut("UInt", 0x00000100, RAWINPUTDEVICE, 4) ; RIDEV_INPUTSINK
NumPut("Ptr", A_ScriptHwnd, RAWINPUTDEVICE, 8)
if !DllCall("RegisterRawInputDevices", "Ptr", RAWINPUTDEVICE, "UInt", 1, "UInt", 8 + A_PtrSize)
MsgBox "Ошибка регистрации"
OnMessage(0x00FF, OnRawInput)
OnRawInput(wParam, lParam, *) {
size := 0
DllCall("GetRawInputData", "Ptr", lParam, "UInt", 0x10000003, "Ptr", 0, "UInt*", &size, "UInt", 8 + A_PtrSize * 2)
if (size > 0) {
raw := Buffer(size)
DllCall("GetRawInputData", "Ptr", lParam, "UInt", 0x10000003, "Ptr", raw, "UInt*", &size, "UInt", 8 + A_PtrSize * 2)
headerSize := (A_PtrSize == 8) ? 24 : 16
; Извлекаем флаги кнопок мыши
btnFlags := NumGet(raw, headerSize + 4, "UShort")
; 0x0001 — это флаг RI_MOUSE_LEFT_BUTTON_DOWN
if (btnFlags & 0x0001) {
; Проверяем, зажат ли Shift (логическое состояние)
if GetKeyState("Shift", "P") {
HandleShiftClick()
}
}
}
}
HandleShiftClick() {
; Получаем окно под курсором
DllCall("GetCursorPos", "Ptr", pt := Buffer(8))
hWnd := DllCall("WindowFromPoint", "Int64", NumGet(pt, 0, "Int64"), "Ptr")
if (hWnd) {
classBuffer := Buffer(512)
DllCall("GetClassName", "Ptr", hWnd, "Ptr", classBuffer, "Int", 256)
className := StrGet(classBuffer)
ToolTip "Shift + Click на окне класса: " className
SetTimer () => ToolTip(), -2000
}
} |
 3 | Code: | #Requires AutoHotkey v2.0
Persistent
; Константы WinAPI
WM_NCHITTEST := 0x84
HTCAPTION := 2
; Регистрация Raw Input
RAWINPUTDEVICE := Buffer(8 + A_PtrSize, 0)
NumPut("UShort", 1, RAWINPUTDEVICE, 0)
NumPut("UShort", 2, RAWINPUTDEVICE, 2)
NumPut("UInt", 0x00000100, RAWINPUTDEVICE, 4)
NumPut("Ptr", A_ScriptHwnd, RAWINPUTDEVICE, 8)
if !DllCall("RegisterRawInputDevices", "Ptr", RAWINPUTDEVICE, "UInt", 1, "UInt", 8 + A_PtrSize)
MsgBox "Ошибка регистрации"
OnMessage(0x00FF, OnRawInput)
OnRawInput(wParam, lParam, *) {
size := 0
DllCall("GetRawInputData", "Ptr", lParam, "UInt", 0x10000003, "Ptr", 0, "UInt*", &size, "UInt", 8 + A_PtrSize * 2)
if (size > 0) {
raw := Buffer(size)
DllCall("GetRawInputData", "Ptr", lParam, "UInt", 0x10000003, "Ptr", raw, "UInt*", &size, "UInt", 8 + A_PtrSize * 2)
headerSize := (A_PtrSize == 8) ? 24 : 16
btnFlags := NumGet(raw, headerSize + 4, "UShort")
; Проверяем нажатие левой кнопки мыши + Shift
if (btnFlags & 0x0001) && GetKeyState("Shift", "P") {
HandleShiftClick()
}
}
}
HandleShiftClick() {
; 1. Получаем текущие координаты курсора
DllCall("GetCursorPos", "Ptr", pt := Buffer(8))
x := NumGet(pt, 0, "Int")
y := NumGet(pt, 4, "Int")
; 2. Находим окно под курсором
hWnd := DllCall("WindowFromPoint", "Int64", NumGet(pt, 0, "Int64"), "Ptr")
if (hWnd) {
; Поднимаемся к главному родительскому окну (если кликнули по дочернему элементу)
rootHWnd := DllCall("GetAncestor", "Ptr", hWnd, "UInt", 2, "Ptr") ; GA_ROOT
; 3. Получаем класс окна
classBuffer := Buffer(512)
DllCall("GetClassName", "Ptr", rootHWnd, "Ptr", classBuffer, "Int", 256)
className := StrGet(classBuffer)
; 4. Проверяем, заголовок ли это (WM_NCHITTEST)
; Передаем координаты в формате (y << 16) | (x & 0xFFFF)
hitTest := DllCall("SendMessage", "Ptr", rootHWnd, "UInt", WM_NCHITTEST, "Ptr", 0, "Ptr", (y << 16) | (x & 0xFFFF))
isTitleBar := (hitTest == HTCAPTION)
; Вывод результата
msg := "Class: " className "`n"
msg .= "Is Title Bar: " (isTitleBar ? "✅ ДА" : "❌ НЕТ")
ToolTip msg
SetTimer () => ToolTip(), -3000
}
}
|
 4 | Code: | #Requires AutoHotkey v2.0
Persistent
; Константы для читаемости
RI_MOUSE_LEFT_BUTTON_DOWN := 0x0001
WM_INPUT := 0xFF
RID_INPUT := 0x10000003
; Регистрация устройства
RAWINPUTDEVICE := Buffer(8 + A_PtrSize, 0)
NumPut("UShort", 1, RAWINPUTDEVICE, 0)
NumPut("UShort", 2, RAWINPUTDEVICE, 2)
NumPut("UInt", 0x00000100, RAWINPUTDEVICE, 4) ; RIDEV_INPUTSINK
NumPut("Ptr", A_ScriptHwnd, RAWINPUTDEVICE, 8)
DllCall("RegisterRawInputDevices", "Ptr", RAWINPUTDEVICE, "UInt", 1, "UInt", 8 + A_PtrSize)
OnMessage(WM_INPUT, OnRawInput)
OnRawInput(wParam, lParam, *) {
; 1. БЫСТРАЯ ПРОВЕРКА: Если нет нажатия кнопок, мгновенно выходим.
; Это отсекает 99% вызовов, связанных с простым движением мыши.
; Сначала получаем размер данных
size := 0
DllCall("GetRawInputData", "Ptr", lParam, "UInt", RID_INPUT, "Ptr", 0, "UInt*", &size, "UInt", 8 + A_PtrSize * 2)
if (size <= 0)
return
raw := Buffer(size)
DllCall("GetRawInputData", "Ptr", lParam, "UInt", RID_INPUT, "Ptr", raw, "UInt*", &size, "UInt", 8 + A_PtrSize * 2)
headerSize := (A_PtrSize == 8) ? 24 : 16
btnFlags := NumGet(raw, headerSize + 4, "UShort")
; 2. ПРОВЕРКА СОБЫТИЯ: Нас интересует только момент НАЖАТИЯ левой кнопки (0x0001)
if !(btnFlags & RI_MOUSE_LEFT_BUTTON_DOWN)
return
; 3. ПРОВЕРКА МОДИФИКАТОРА: Зажат ли Shift?
if !GetKeyState("Shift", "P")
return
; Если мы дошли сюда, значит произошло ровно то, что нужно: Shift + Left Down
HandleAction()
}
HandleAction() {
; Вся тяжелая логика (WinAPI, ToolTip и т.д.) теперь вызывается
; ТОЛЬКО в момент клика, а не при каждом движении.
DllCall("GetCursorPos", "Ptr", pt := Buffer(8))
x := NumGet(pt, 0, "Int"), y := NumGet(pt, 4, "Int")
hWnd := DllCall("WindowFromPoint", "Int64", NumGet(pt, 0, "Int64"), "Ptr")
if (hWnd) {
rootHWnd := DllCall("GetAncestor", "Ptr", hWnd, "UInt", 2)
classBuffer := Buffer(512)
DllCall("GetClassName", "Ptr", rootHWnd, "Ptr", classBuffer, "Int", 256)
hitTest := DllCall("SendMessage", "Ptr", rootHWnd, "UInt", 0x84, "Ptr", 0, "Ptr", (y << 16) | (x & 0xFFFF))
isTitleBar := (hitTest == 2) ; HTCAPTION
MsgBox "Класс: " . StrGet(classBuffer) . "`nЗаголовок: " . (isTitleBar ? "Да" : "Нет")
}
}
|
 5 | Code: | #Requires AutoHotkey v2.0
; --- 1. Регистрация класса окна ---
className := "MyInvisibleReceiver"
wndClass := Buffer(120, 0) ; Структура WNDCLASSEX
NumPut("UInt", 80, wndClass, 0) ; cbSize
NumPut("Ptr", CallbackCreate(WndProc), wndClass, 8) ; lpfnWndProc
NumPut("Ptr", StrPtr(className), wndClass, 64) ; lpszClassName
if !DllCall("RegisterClassEx", "Ptr", wndClass) {
MsgBox "Ошибка регистрации класса"
ExitApp
}
; --- 2. Создание Message-Only окна ---
; HWND_MESSAGE = -3
hwnd := DllCall("CreateWindowEx",
"UInt", 0,
"Str", className,
"Str", "HiddenWindow",
"UInt", 0,
"Int", 0, "Int", 0, "Int", 0, "Int", 0,
"Ptr", -3, ; ПЕРЕМЕННАЯ HWND_MESSAGE
"Ptr", 0, "Ptr", 0, "Ptr", 0, "Ptr")
if !hwnd {
MsgBox "Ошибка создания окна"
ExitApp
}
MsgBox("Окно создано. HWND: " hwnd)
; --- 3. Обработчик событий (Window Procedure) ---
WndProc(hwnd, msg, wParam, lParam) {
; Пример: ловим кастомное сообщение 0x5555
if (msg == 0x5555) {
MsgBox "Событие перехвачено через WinAPI! wParam: " wParam
return 0
}
; Обязательно вызываем стандартный обработчик для остальных сообщений
return DllCall("DefWindowProc", "Ptr", hwnd, "UInt", msg, "Ptr", wParam, "Ptr", lParam, "Ptr")
}
; Тест: отправить сообщение самому себе
F2::DllCall("PostMessage", "Ptr", hwnd, "UInt", 0x5555, "Ptr", 1, "Ptr", 2)
|
Мне оставалось только привести все это к формату Autorun, добавив небольшой специфики. Но на этом все.
 aucfg | Code: | RunThread("InvisibleReceiver")
Func InvisibleReceiver()
Local text = "InvisibleReceiver"
Local proc = Callback("ReceiverWndProc", "hwnd;uint;wparam;lparam")
Local name = Buffer(StrLen(text) + 2)
name.Zero()
name.SetStr(text, 0, StrLen(text), "ANSI")
Local wndClass = Buffer(auX64 ? 80 : 48) # Структура WNDCLASSEX
wndClass.Zero()
wndClass.SetNum(0, "UInt", wndClass.Size) # cbSize
wndClass.SetNum(8, "Ptr", proc.ptr) # lpfnWndProc
wndClass.SetNum(auX64 ? 64 : 40, "Ptr", name.ptr) # lpszClassName
If Not DllCall("RegisterClassExA", "Ptr", wndClass.Ptr) Then
MsgBox('Ошибка регистрации класса "MyInvisibleReceiver"')
Return 0
EndIf
Free(wndClass)
# Message-Only window
Static HWND_MESSAGE = -3
Local hwnd = DllCall("CreateWindowExA", _
"Dword", 0, _
"Str", text, _
"Str", "HiddenWindow", _
"Dword", 0, _
"Int", 0, "Int", 0, "Int", 0, "Int", 0, _
"Ptr", HWND_MESSAGE, _
"Ptr", 0, "Ptr", 0, "Ptr", 0, "Ptr")
If Not hwnd Then
MsgBox 'Ошибка создания окна "HiddenWindow"'
Return 0
EndIf
Static RIDEV_INPUTSINK = 0x00000100
Local rid, buf
rid = Buffer(8 + auPtrSize, 0)
rid.Zero()
rid.SetNum(0, "UShort", 1, "UShort", 2, "UInt", RIDEV_INPUTSINK, "Ptr", hwnd)
# Регистрация устройства
If Not DllCall("RegisterRawInputDevices", "Ptr", rid.Ptr, "UInt", 1, "UInt", 8 + auPtrSize) Then
MsgBox("Ошибка регистрации Raw Input")
Return 0
EndIf
buf = Buffer(auX64 ? 56 : 28)
While 1
DllCall("GetMessage", "ptr", buf.ptr, "hwnd", hwnd, "uint", 0, "uint", 0)
DllCall("TranslateMessage", "ptr", buf.ptr)
DllCall("DispatchMessage", "ptr", buf.ptr)
Wend
Free(rid, buf)
Return 1
EndFunc
Func ReceiverWndProc(hwnd, uMsg, wParam, lParam)
If uMsg = 0x00FF Then
OnRawInput(hWnd, uMsg, wParam, lParam)
EndIf
Return DllCall("DefWindowProc", _
"hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam, "ptr")
EndFunc
Func OnRawInput(hWnd, uMsg, wParam, lParam)
Static RID_INPUT = 0x10000003, _
RI_MOUSE_LEFT_BUTTON_DOWN = 0x0001
# Получение размера данных
Local size = 0
DllCall("GetRawInputData", "Ptr", lParam, "UInt", RID_INPUT, "Ptr", 0, "UInt*", @size, "UInt", 8 + auPtrSize * 2)
If size <= 0 Then Return
Local raw = Buffer(size)
DllCall("GetRawInputData", "Ptr", lParam, "UInt", RID_INPUT, "Ptr", raw.ptr, "UInt*", @size, "UInt", 8 + auPtrSize * 2)
# Извлечение флагов кнопок мыши
Local headerSize = auX64 ? 24 : 16
Local btnFlags = raw.GetNum(headerSize + 4, "UShort")
Free(raw)
# Проверка левого клика
If Not BitAND(btnFlags, RI_MOUSE_LEFT_BUTTON_DOWN) Then Return
# Проверка состояния Shift
If Not IsPressed(0x10) Then Return
# Произошло нажатие Shift + Left Down
HandleAction()
EndFunc
Func HandleAction()
Local x, y
MouseGetPos("x","y")
# Дескриптор окна под курсором
Local hWnd = DllCall("WindowFromPoint", "Int64", MakeInt(x, y, 2), "Ptr")
If Not hWnd Then Return
# Переход к главному родительскому окну
Local rootHWnd = DllCall("GetAncestor", "Ptr", hWnd, "UInt", 2)
# Буфер для получения имени класса
Static classBuffer = Buffer(512)
classBuffer.Zero()
# Получение имени класса
Static WM_NCHITTEST = 0x0084
DllCall("GetClassName", "Ptr", rootHWnd, "Ptr", classBuffer.Ptr, "Int", 256)
Local hitTest = DllCall("SendMessage", _
"Ptr", rootHWnd, "UInt", WM_NCHITTEST, "Ptr", 0, "Ptr", MakeInt(x, y, 0))
# Проверка области клика
Static HTCAPTION = 2
Local isTitleBar = (hitTest = HTCAPTION)
If isTitleBar And classBuffer.GetStr() = "TLister" Then
AlignListerWindow()
EndIf
# MsgBox("Класс: " & classBuffer.GetStr() & auCRLF & "Заголовок: " & (isTitleBar ? "Да" : "Нет"))
EndFunc |
Решение полностью рабочее. Перехват клика в других окнах помимо листера еще может пригодиться.
| FallenAngel wrote: | | Я все равно не понимаю, почему просто не сменить: |
Ну попробуйте, никто ведь не мешает. Может я и перегнул палку, вдруг у вас все заработает.
Если используется ResolutionSpecific=0, то надо проверить, как поведет себя запись в AllResolutions. Но если в секции Buttonbar есть ключи типа Buttonheight96 и SmallIconSize96, то простая смена Buttonheight и SmallIconSize точно работать не будет.
В любом случае, даже если у вас все получится, то это будет только на тоненького и только для вашей сборки и скорее всего до первой смены параметров DPI дисплея. |
|