1

Тема: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Я хочу освоить работу с GDI+ и улучшить свой имеющийся скрипт.
Скрипт позволяет с клавиатуры или мышью назначать активному окну (если с клавиатуры) или окну под курсором (если мышью) параметр AlwaysOnTop. Скрипт написан, работает, но т.к. к теме он не особо относится - его код не выкладываю.

Сейчас же я хочу добавить в скрипт визуальное оповещение пользователя о том, какие окна имеют параметр AlwaysOnTop. Сделать это я хочу пририсовав иголочку (кружочек, что угодно) в левый верхний угол окон с таким параметром.
Как я уже понял, рисование делается с помощью GDI+. Я почитал статьи на англоязычном форуме с примерами применения, но всё равно не понял как это сделать.
GDI+ же, по сути, создаёт собственные окна, т.е. их позиционировать поверх существующих окон (причём, чтоб они таскались вместе с основным окном [docking]),  но при этом сделать их и прозрачными для кликов, т.к. возможен вариант, что в левом верхнем углу у окна будет какая-то кнопка.

Как всё это сделать? В GDI+ я - ни бум-бум; в скрипте - уже знаю момент, когда должна начинаться пририсовка иконки окну: тогда же, когда и назначается alwaysontop.
Минусом такого подхода будет то, что alwaysontop окна, которые получили этот параметр не от скрипта - останутся без иконки, поэтому параллельно пытаюсь писать код, чтобы в скрипте был 1 отдельный поток, который занимался бы тем, что делал бы петлю из следующих действий: получить список всех окон (исключая 3 системных: Пуск, ahk_class Shell_TrayWnd, ahk_class Progman), а затем проанализировать каждое окно из списка (имеет ли alwaysontop параметр и имеет ли GDI+ прикреплённое к нему).

И тут возникает вопрос: а если окно alwaysontop, то прилепленное к нему GDI+ окно будет ли видно? В винде же положение окон по z-оси ранжируется по 3-ём критериям: является ли окно tooltip-ом (всплывающей подсказкой), является ли оно alwaysontop, является ли оно активным или как давно было активным.
Т.е. если GDI+ окно сделать не alwaysontop, то оно же (по идее, сам я не пробовал проверять) скроется за прикреплённым к нему окном, как только то станет alwaysontop?
А если же оно будет тоже alwaysontop, то всё равно будет видимым до первого клика по прикреплённому к нему основному alwaysontop окну, т.к. то получит фокус и окажется в иерархии выше (по z-оси), и опять закроет собой gdi+ окно?

2

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Для начала, чтобы создать окно-значок, GDI+ не нужно.

Gui, -Caption +ToolWindow
Gui, Color, Red
Gui, Show, w20 h20

Можно отображать и другие формы окна с помощью WinSet, Region.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

3

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Gui создаёт обычное окно и да, я знаю, что можно в нём отключить и заголовок и даже рамку убрать, но как такое окно поместить поверх alwaysontop окна? если с GDI+ ещё есть вероятность, что оно будет держаться поверх такого окна, то с обычным окном - такой вероятности нет, т.к. при клике внутри alwaysontop окна - оно получает фокус, а значит становится по z-оси выше Gui окна (даже если у него стоит alwaysontop).

4

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Drugoy пишет:

если с GDI+ ещё есть вероятность, что оно будет держаться поверх такого окна, то с обычным окном - такой вероятности нет

Это обычное суеверие, в смысле положения в z-порядке между "обычными" окнами и окнами, созданными с применением GDI+, нет никакой разницы.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

5 (изменено: teadrinker, 2013-02-08 03:38:37)

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Примерно так:

global GUIs := []
EVENT_OBJECT_DESTROY := 0x8001
WS_EX_TRANSPARENT := 0x20

SetWinDelay, 0

; отслеживаем закрытие окон
HWINEVENTHOOK := SetWinEventHook(EVENT_OBJECT_DESTROY, EVENT_OBJECT_DESTROY, 0
                                          , RegisterCallback("WatchingDestroyWindow", "F"), 0, 0, 0)
                                          
OnExit, Exit
return

Exit:
   DllCall("UnhookWinEvent", Ptr, HWINEVENTHOOK)

; снимаем стиль WS_EX_TOPMOST со всех окон, где он был установлен
   for k, v in GUIs
      WinSet, AlwaysOnTop, Off, % "ahk_id" v.Parent
   
   GUIs := ""
   ExitApp
   
!1::
   WinGet, ID,, A
   
   for k, v in GUIs
      if (v.Parent = ID)
         return
      
   WinSet, AlwaysOnTop, On, A
   WinGetPos, X, Y,,, A

; устанавливая расширенный стиль WS_EX_TRANSPARENT делаем окно прозрачным для мыши
; опцией Owner%ID% делаем GUI принадлежащим активному окну, тогда GUI всегда будет поверх окна-хозяина
   Gui, New, +E%WS_EX_TRANSPARENT% -Caption +LastFound +AlwaysOnTop +hwndhGui +Owner%ID%
   WinSet, Transparent, 200  ; прозрачность окна-метки, от 0 до 255
   Gui, Color, Red
   Gui, Show, % "NA w12 h12 x" X+5 " y" Y+5
   
   GUIs.Insert({Parent: ID, Owned: hGui})
   return
   
!2::
   WinGet, ID,, A
   AlwaysOnTopOff(ID)
   return
   
~LButton::   ; отслеживаем перемещение окон мышью и изменение размеров
   MouseGetPos,,, ID
   for k, v in GUIs
      if (v.Parent = ID)
      {
         mod := ""
         OwnedWindowID := v.Owned
         MonitoredWindowID := v.Parent
         SetTimer, WatchingMove, 10
         break
      }
   return
   
~LButton Up::
   mod := 1
   return
   
WatchingMove:
   WinGetPos, X, Y,,, ahk_id %MonitoredWindowID%
   WinMove, ahk_id %OwnedWindowID%,, X+5, Y+5
   
   if (mod && ++mod = 30)
      SetTimer, WatchingMove, Off
   return
   
Esc::ExitApp

WatchingDestroyWindow(hWinEventHook, event, hwnd)
{
   AlwaysOnTopOff(hwnd)
}

AlwaysOnTopOff(hwnd)
{
   for k, v in GUIs
      if (v.Parent = hwnd)
      {
         WinSet, AlwaysOnTop, Off, ahk_id %hwnd%
         Gui, % v.Owned ":Destroy"
         GUIs.Remove(k)
         break
      }
}

SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags)
{
   return DllCall("SetWinEventHook" , UInt, eventMin, UInt, eventMax
                                    , Ptr, hmodWinEventProc, Ptr, lpfnWinEventProc
                                    , UInt, idProcess, UInt, idThread
                                    , UInt, dwFlags, Ptr)
}

По Alt+1 устанавливает стиль WS_EX_TOPMOST для активного окна и создаёт поверх него в левом верхнем углу красную метку, по Alt+2 снимает.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

6 (изменено: Drugoy, 2013-02-08 18:28:01)

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Спасибо, код прекрасно работает!
Но очень он сложен для моего понимания: читаю его и узнаю, что, оказывается, можно объявляя переменную задать ей значение в виде какой-то команды (у вас это вызов функции); что параметром функции может являться вызов другой функции (со своими параметрами).

Вопрос по этой функции:

WatchingDestroyWindow(hWinEventHook, event, hwnd)
{
    AlwaysOnTopOff(hwnd)
}

Эта функция заключается в вызове другой функции, причём для её вызова нужно ей передать всего 1 параметр hwnd, а зачем тогда изначальной функции 3 параметра и нельзя ли её вообще целиком заменить на 2-ую функцию? В смысле, что я не понимаю зачем функции неиспользуемые параметры, расскажите, пожалуйста.

7

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Функция WatchingDestroyWindow() является callback-функцией для SetWinEventHook() (которая отслеживает определённые системные события, в данном случае — EVENT_OBJECT_DESTROY — закрытие окон). Она может принимать 7 параметров, расположенных в определённом порядке. Это примерно, как OnMessage() вызывает функцию с 4-мя определёнными параметрами.

Drugoy пишет:

нельзя ли её вообще целиком заменить на 2-ую функцию?

В данном случае можно было бы всё, что делается в функции AlwaysOnTopOff() просто записать в WatchingDestroyWindow(), из второй обращение к первой сделано для краткости записи, ведь функция AlwaysOnTopOff() определяется один раз, а вызывается в коде дважды.

Drugoy пишет:

заметил, что красное окошко сдвигается на 1-2 пикселя влево и/или вверх

У меня, вроде, не сдвигается, ничего не могу сказать. Может, ты менял в коде размеры окна или значения сдвига?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

8

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Спасибо за разъяснения, стало понятней.
Вот только удивляюсь откуда ahk-скриптующий народ знает столько про DllCalls?

У меня, вроде, не сдвигается, ничего не могу сказать. Может, ты менял в коде размеры окна или значения сдвига?

Ага, поменял только в одном месте сначала, а во втором забыл. Потом подправил и глюк исчез. Единственный глюк с позиционированием теперь остался - только если окно быстро перетащить и "отпустить" до момента остановки движения - тогда красный квадратик не успевает догнать свою цель.

9

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Drugoy пишет:

только если окно быстро перетащить и "отпустить" до момента остановки движения - тогда красный квадратик не успевает догнать свою цель.

Такого тоже не должно быть, т. к. таймер, отслеживающий перемещение окна, отключается не сразу после отпускания мыши, а только через 1/3 секунды, соответственно, метка должна в любом случае встать на своё место. Так что, ищи ошибку у себя.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

10

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

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

#NoEnv
SetBatchLines, -1
SetWinDelay, 0

global GUIs := []
EVENT_OBJECT_HIDE := 0x8003

; сдвиг окна-метки относительно верхнего левого угла окна-хозяина
ShiftX := 5
ShiftY := 5

; отслеживаем закрытие и скрытие окон
HWINEVENTHOOK := SetWinEventHook(EVENT_OBJECT_HIDE, EVENT_OBJECT_HIDE, 0
                                          , RegisterCallback("WatchingDestroyWindow", "F"), 0, 0, 0)
OnExit, Exit
return

Exit:
   DllCall("UnhookWinEvent", Ptr, HWINEVENTHOOK)

; снимаем стиль WS_EX_TOPMOST со всех окон, где он был установлен
   for k, v in GUIs
      WinSet, AlwaysOnTop, Off, % "ahk_id" v.Parent
   
   GUIs := ""
   ExitApp
   
!1::
   WinGet, ID,, A
   WinGetPos, XWIn, YWin,,, ahk_id %ID%
   
   for k, v in GUIs
      if (v.Parent = ID)
      {
         AlwaysOnTopOff(ID)
         return
      }
      
   WinSet, AlwaysOnTop, On, ahk_id %ID%
   
   CreateMarker(ID, XWin+ShiftX, YWin+ShiftY)
   return
   
~LButton::   ; отслеживаем перемещение окон мышью и изменение размеров
   MouseGetPos,,, ID
   for k, v in GUIs
      if (v.Parent = ID)
      {
         mod := ""
         OwnedWindowID := v.Owned
         MonitoredWindowID := v.Parent
         SetTimer, WatchingMove, 10
         break
      }
   return
   
~LButton Up::
   mod := 1
   return
   
WatchingMove:
   WinGetPos, X, Y,,, ahk_id %MonitoredWindowID%
   WinMove, ahk_id %OwnedWindowID%,, X+ShiftX, Y+ShiftY
   
   if (mod && ++mod = 30)
      SetTimer, WatchingMove, Off
   return
   
Esc::ExitApp

CreateMarker(OwnerID, X, Y)
{
   static WS_EX_TRANSPARENT := 0x20
   
; устанавливая расширенный стиль WS_EX_TRANSPARENT делаем окно-метку прозрачным для мыши
; опцией Owner%OwnerID% делаем GUI принадлежащим активному окну, тогда GUI всегда будет поверх окна-хозяина
   Gui, New, +E%WS_EX_TRANSPARENT% -Caption +LastFound +AlwaysOnTop +hwndhGui +Owner%OwnerID%
   WinSet, Transparent, 200  ; прозрачность окна-метки, от 0 до 255
   Gui, Color, Red
   Gui, Show, NA w12 h12 x%X% y%Y%
   
   GUIs.Insert({Parent: OwnerID, Owned: hGui})
}

WatchingDestroyWindow(hWinEventHook, event, hwnd)
{
   DHW := A_DetectHiddenWindows
   DetectHiddenWindows, Off
   
   if !WinExist("ahk_id" hwnd)
      AlwaysOnTopOff(hwnd)
   
   DetectHiddenWindows, %DHW%
}

AlwaysOnTopOff(hwnd)
{
   for k, v in GUIs
      if (v.Parent = hwnd)
      {
         DHW := A_DetectHiddenWindows
         DetectHiddenWindows, On
         WinSet, AlwaysOnTop, Off, ahk_id %hwnd%
         DetectHiddenWindows, %DHW%
         
         Gui, % v.Owned ":Destroy"
         GUIs.Remove(k)
         break
      }
}

SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags)
{
   return DllCall("SetWinEventHook" , UInt, eventMin, UInt, eventMax
                                    , Ptr, hmodWinEventProc, Ptr, lpfnWinEventProc
                                    , UInt, idProcess, UInt, idThread
                                    , UInt, dwFlags, Ptr)
}

Установка и снятие WS_EX_TOPMOST для активного окна по одной и той же горячей клавише, Alt+1.

А вот вариант с GDI+. Никаких дополнительных библиотек не требуется. Скрипт вначале скачивает изображение для метки.

#NoEnv
SetBatchLines, -1
SetWinDelay, 0
CoordMode, Mouse

global GUIs := []
EVENT_OBJECT_HIDE := 0x8003
ShiftX := -10
ShiftY := -17

if !FileExist("pin.png")
{
   Critical
   TrayTip,, Идёт загрузка изображения`, подождите...
   URLDownloadToFile, % "http://content.screencast.com/users/teadrinker/folders/pin/media/"
      . "3eb0ac81-5d35-400b-9c1e-ddee60f22552/icon-23622_150.png?downloadOnly=true", pin.png
   if !ErrorLevel
   {
      TrayTip,, Загрузка заверщена успешно!
      Sleep, 1500
      TrayTip
   }
   else
   {
      MsgBox, Загрузка не удалась!`nПоместите pin.png файл в директорию скрипта и перезапустите скрипт.
      ExitApp
   }
}

; отслеживаем закрытие окон
HWINEVENTHOOK := SetWinEventHook(EVENT_OBJECT_HIDE, EVENT_OBJECT_HIDE, 0
                                          , RegisterCallback("WatchingDestroyWindow", "F"), 0, 0, 0)
OnExit, Exit
return

Exit:
   DllCall("UnhookWinEvent", Ptr, HWINEVENTHOOK)

; снимаем стиль WS_EX_TOPMOST со всех окон, где он был установлен
   for k, v in GUIs
      WinSet, AlwaysOnTop, Off, % "ahk_id" v.Parent
   
   GUIs := ""
   ExitApp
   
!1::
   WinGet, ID,, A
   WinGetPos, XWIn, YWin,,, ahk_id %ID%
   
   for k, v in GUIs
      if (v.Parent = ID)
      {
         AlwaysOnTopOff(ID)
         return
      }
      
   WinSet, AlwaysOnTop, On, ahk_id %ID%

   CreateMarker(ID, XWin+ShiftX, YWin+ShiftY)
   return
   
~LButton::   ; отслеживаем перемещение окон мышью и изменение размеров
   MouseGetPos,,, ID
   for k, v in GUIs
      if (v.Parent = ID)
      {
         mod := ""
         OwnedWindowID := v.Owned
         MonitoredWindowID := v.Parent
         SetTimer, WatchingMove, 10
         break
      }
   return
   
~LButton Up::
   mod := 1
   return
   
WatchingMove:
   WinGetPos, X, Y,,, ahk_id %MonitoredWindowID%
   WinMove, ahk_id %OwnedWindowID%,, X+ShiftX, Y+ShiftY
   
   if (mod && ++mod = 30)
      SetTimer, WatchingMove, Off
   return
   
Esc::ExitApp

CreateMarker(OwnerID, X, Y)
{
   static WS_EX_TRANSPARENT := 0x20, WS_EX_LAYERED := 0x80000
   
   Gui, New, % "-Caption +LastFound +AlwaysOnTop +hwndhGui +Owner" OwnerID " +E" WS_EX_LAYERED|WS_EX_TRANSPARENT
   Gui, Show, NA

   pToken := GdipStartup()
   pBitmap := GdipCreateBitmapFromFile("pin.png")

   Width := GdipGetImageWidth(pBitmap), Height := GdipGetImageHeight(pBitmap)
   hbm := CreateDIBSection(Width//6, Height//6)
   hdc := CreateCompatibleDC()
   obm := SelectObject(hdc, hbm)
   G := GdipGraphicsFromHDC(hdc)
   GdipSetInterpolationMode(G, 7)
   GdipDrawImage(G, pBitmap, 0, 0, Width//6, Height//6, 0, 0, Width, Height)
   
   UpdateLayeredWindow(hGui, hdc, X, Y, Width//6, Height//6)

   SelectObject(hdc, obm), DeleteObject(hbm)
   DeleteDC(hdc), GdipDeleteGraphics(G), GdipDisposeImage(pBitmap)
   GdipShutdown(pToken)
   
   GUIs.Insert({Parent: OwnerID, Owned: hGui})
}

WatchingDestroyWindow(hWinEventHook, event, hwnd)
{
   DHW := A_DetectHiddenWindows
   DetectHiddenWindows, Off
   
   if !WinExist("ahk_id" hwnd)
      AlwaysOnTopOff(hwnd)
   
   DetectHiddenWindows, %DHW%
}

AlwaysOnTopOff(hwnd)
{
   for k, v in GUIs
      if (v.Parent = hwnd)
      {
         DHW := A_DetectHiddenWindows
         DetectHiddenWindows, On
         WinSet, AlwaysOnTop, Off, ahk_id %hwnd%
         DetectHiddenWindows, %DHW%
         
         Gui, % v.Owned ":Destroy"
         GUIs.Remove(k)
         break
      }
}

SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags)
{
   return DllCall("SetWinEventHook" , UInt, eventMin, UInt, eventMax
                                    , Ptr, hmodWinEventProc, Ptr, lpfnWinEventProc
                                    , UInt, idProcess, UInt, idThread
                                    , UInt, dwFlags, Ptr)
}

; ********************************************** Функции GDI+ ***************************************************

GdipStartup()
{
   if !DllCall("GetModuleHandle", "str", "gdiplus", "ptr")
      DllCall("LoadLibrary", "str", "gdiplus")
   VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
   DllCall("gdiplus\GdiplusStartup", "uptr*", pToken, "ptr", &si, "ptr", 0)
   return pToken
}

GdipCreateBitmapFromFile(sFile)
{
   DllCall("gdiplus\GdipCreateBitmapFromFile", "wstr", sFile, "ptr*", pBitmap)
   return pBitmap
}

GdipGetImageWidth(pBitmap)
{
   DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap, "uint*", Width)
   return Width
}

GdipGetImageHeight(pBitmap)
{
   DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap, "uint*", Height)
   return Height
}

GetDC(hwnd=0)
{
   return DllCall("GetDC", "ptr", hwnd, "ptr")
}

CreateDIBSection(w, h, hdc="", bpp=32, ByRef ppvBits=0)
{
   hdc2 := hdc ? hdc : GetDC()
   VarSetCapacity(bi, 40, 0)
   NumPut(w, bi, 4, "uint"), NumPut(h, bi, 8, "uint"), NumPut(40, bi, 0, "uint")
   NumPut(1, bi, 12, "ushort"), NumPut(0, bi, 16), NumPut(bpp, bi, 14, "ushort")
   hbm := DllCall("CreateDIBSection", "ptr" , hdc2, "ptr" , &bi, "uint" , 0
                                    , "ptr*", ppvBits, "ptr" , 0, "uint" , 0, "ptr")
   if !hdc
      ReleaseDC(hdc2)
   return hbm
}

ReleaseDC(hdc, hwnd=0)
{
   return DllCall("ReleaseDC", "ptr", hwnd, "ptr", hdc)
}

CreateCompatibleDC(hdc=0)
{
   return DllCall("CreateCompatibleDC", "ptr", hdc, "ptr")
}

SelectObject(hdc, hgdiobj)
{
   return DllCall("SelectObject", "ptr", hdc, "ptr", hgdiobj, "ptr")
}

GdipGraphicsFromHDC(hdc)
{
    DllCall("gdiplus\GdipCreateFromHDC", "ptr", hdc, "ptr*", pGraphics)
    return pGraphics
}

GdipSetInterpolationMode(pGraphics, InterpolationMode)
{
   return DllCall("gdiplus\GdipSetInterpolationMode", "ptr", pGraphics, "int", InterpolationMode)
}

GdipDrawImage(pGraphics, pBitmap, dx, dy, dw, dh, sx, sy, sw, sh)
{
   E := DllCall("gdiplus\GdipDrawImageRectRect", "ptr", pGraphics, "ptr", pBitmap
   , "float", dx, "float", dy, "float", dw, "float", dh
   , "float", sx, "float", sy, "float", sw, "float", sh
   , "int", 2, "ptr", ImageAttr, "ptr", 0, "ptr", 0)
   return E
}

UpdateLayeredWindow(hwnd, hdc, x="", y="", w="", h="", Alpha=255)
{
   if ((x != "") && (y != ""))
      VarSetCapacity(pt, 8), NumPut(x, pt, 0, "uint"), NumPut(y, pt, 4, "uint")

   if (w = "") ||(h = "")
      WinGetPos,,, w, h, ahk_id %hwnd%
   
   return DllCall("UpdateLayeredWindow", "ptr", hwnd, "ptr", 0, "ptr", ((x = "") && (y = "")) ? 0 : &pt
   , "int64*", w|h<<32, "ptr", hdc, "int64*", 0, "uint", 0, "uint*", Alpha<<16|1<<24, "uint", 2)
}

DeleteObject(hObject)
{
   return DllCall("DeleteObject", "ptr", hObject)
}

DeleteDC(hdc)
{
   return DllCall("DeleteDC", "ptr", hdc)
}

GdipDeleteGraphics(pGraphics)
{
   return DllCall("gdiplus\GdipDeleteGraphics", "ptr", pGraphics)
}

GdipDisposeImage(pBitmap)
{
   return DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap)
}

GdipShutdown(pToken)
{
   DllCall("gdiplus\GdiplusShutdown", "uptr", pToken)
   if hModule := DllCall("GetModuleHandle", "str", "gdiplus", "ptr")
      DllCall("FreeLibrary", "ptr", hModule)
   return 0
}

Та же горячая клавиша Alt+1.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

11 (изменено: Drugoy, 2013-02-09 14:00:56)

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

teadrinker пишет:

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

А можно пример?
У меня установлен анвир и в нём в настройках стоит "при закрытии - сворачиваться в трей" - и скрипт всё нормально учитывает при повторном вызове окна.
Мне не нравится в новом скрипте постоянное слежение за мышью при каждом клике + я окна перемещаю и скриптами (и с клавиатуры и мышью через хоткей XButton2 & LButton).

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

12

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Drugoy пишет:

А можно пример?

Например, если запустить старый вариант, и присвоить метку окну скайпа, затем закрыть его (оно как раз не уничтожается, а скрывается), то метка остаётся на месте.

Drugoy пишет:

Мне не нравится в новом скрипте постоянное слежение за мышью при каждом клике

В старом было так же.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

13

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Ну хорошо, тогда покритикую новые (да и в старом - проблема тоже присутствует) скрипт: если им назначать alwaysontop окну - то иконка нормально пририсовывается, но если после этого другим скриптом убрать этот параметр - иконка не исчезает. Т.е. скрипт следит только за собственными действиями.

14

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Ну да, не считаю это недостатком. Зачем одно и то же делать разными скриптами? Отслеживать же подобные события в рамках AHK можно лишь, установив постоянный таймер, который будет мониторить все окна, что как-то некрасиво (хотя и выполнимо).

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

15

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Ну так есть программы, которые сами устанавливают своим окнам alwaysontop.

16

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Вот вариант, который мониторит все окна, за исключением тех, названия классов которых (или их часть) добавлены в массив Exceptions. Отслеживает перемещения окон с метками независимо от кликов мыши. При завершении работы не отменяет добавленный стиль WS_EX_TOPMOST. Исправлено несколько багов.

#NoEnv
SetBatchLines, -1
SetWinDelay, 0

Hotkey := "!1"   ; указать горячую клавишу для установки/снятия стиля WS_EX_TOPMOST

; сдвиг окна-метки относительно верхнего левого угла окна-хозяина
global ShiftX := 5, ShiftY := 5
, GUIs := [], ScriptPid, Exceptions, WS_EX_TOPMOST := 0x8
, EVENT_OBJECT_SHOW := 0x8002, EVENT_OBJECT_HIDE := 0x8003, OBJID_WINDOW := 0
, EVENT_OBJECT_LOCATIONCHANGE := 0x800B, WINEVENT_SKIPOWNPROCESS := 0x2

; сюда добавляем названия классов окон (или их часть), которые нужно исключить из мониторинга
Exceptions := ["Button", "tooltip", "shadow", "TaskListThumbnailWnd", "Progman", "ComboLBox"
                  , "Shell_TrayWnd", "TTrayAlert", "#327", "NotifyIconOverflowWindow"]

; отслеживаем появление и скрытие окон
HWINEVENTHOOK1 := SetWinEventHook(EVENT_OBJECT_SHOW, EVENT_OBJECT_HIDE, 0
                     , RegisterCallback("WatchingShowHideWindow", "F"), 0, 0, WINEVENT_SKIPOWNPROCESS)
; отслеживаем изменение позиции окон                             
HWINEVENTHOOK2 := SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, 0
                     , RegisterCallback("WatchingChangeLocation", "F"), 0, 0, WINEVENT_SKIPOWNPROCESS)
OnExit, Exit

Process, Exist
ScriptPid := ErrorLevel

Hotkey, %Hotkey%, SetDeleteOnTop, On

; таймер будет отслеживать AlwaysOnTop статус всех окон, кроме добавленных в Exceptions
SetTimer, MonitoringWindows, 1000
return
   
Esc::ExitApp

MonitoringWindows:
   MonitoringWindows()
   return
   
SetDeleteOnTop:
   SetDeleteOnTop()
   return

Exit:
   DllCall("UnhookWinEvent", Ptr, HWINEVENTHOOK1)
   DllCall("UnhookWinEvent", Ptr, HWINEVENTHOOK2)
   GUIs := ""
   ExitApp

MonitoringWindows()
{
   WinGet, List, List
   Loop % List
   {
      ID := List%A_Index%
      
      WinWait, ahk_id %ID%
      WinGet, PID, PID
      if (PID = ScriptPid)
         continue
      
      for k, v in GUIs
         if (v.Parent = ID)
         {
            WinGet, ExStyle, ExStyle
            if !(ExStyle & WS_EX_TOPMOST)
               AlwaysOnTopOff(ID)
            continue 2
         }

      CheckingAndMarking(ID)
   }
}

SetDeleteOnTop()
{
   WinGet, ID,, A
   WinGetPos, XWIn, YWin,,, ahk_id %ID%
   WinGetClass, Class, ahk_id %ID%
   
   for k, v in Exceptions
      if InStr(Class, v)
      {
         CoordMode, ToolTip
         ToolTip, Это окно класса %Class% исключено из мониторинга!, XWIn, YWin
         Sleep, 1500
         ToolTip
         return
      }
   
   for k, v in GUIs
      if (v.Parent = ID)
      {
         AlwaysOnTopOff(ID)
         return
      }
      
   WinSet, AlwaysOnTop, On, ahk_id %ID%
   CreateMarker(ID, XWin+ShiftX, YWin+ShiftY)
}

CreateMarker(OwnerID, X, Y)
{
   static WS_EX_TRANSPARENT := 0x20
   
; устанавливая расширенный стиль WS_EX_TRANSPARENT делаем окно-метку прозрачным для мыши
; опцией Owner%OwnerID% делаем GUI принадлежащим активному окну, тогда GUI всегда будет поверх окна-хозяина
   Gui, New, +E%WS_EX_TRANSPARENT% -Caption +LastFound +AlwaysOnTop +ToolWindow +hwndhGui +Owner%OwnerID%
   WinSet, Transparent, 200  ; прозрачность окна-метки, от 0 до 255
   Gui, Color, Red
   Gui, Show, NA w12 h12 x%X% y%Y%
   
   GUIs.Insert({Parent: OwnerID, Owned: hGui})
}

WatchingShowHideWindow(hWinEventHook, event, hwnd, idObject)
{
   if (idObject != OBJID_WINDOW)
      return
   
   if (event = EVENT_OBJECT_SHOW)
      CheckingAndMarking(hwnd)
   
   else if (event = EVENT_OBJECT_HIDE)
   {
      if !WinExist("ahk_id" hwnd)
         AlwaysOnTopOff(hwnd)
   }
}

CheckingAndMarking(hwnd)
{
   WinWait, ahk_id %hwnd%
   WinGetClass, Class
   for k, v in Exceptions
      if InStr(Class, v)
         return
      
   WinGet, ExStyle, ExStyle
   if (ExStyle & WS_EX_TOPMOST)
   {
;     ToolTip % Class   ; раскомментировать строку для определения класса и добавления в Exceptions
      WinGetPos, XWin, YWin
      CreateMarker(hwnd, XWin+ShiftX, YWin+ShiftY)
   }
}

WatchingChangeLocation(hWinEventHook, event, hwnd, idObject)
{
   if (idObject != OBJID_WINDOW)
      return
  
   static i := 0, OwnedWindowID, MonitoredWindowID
   for k, v in GUIs
      if (v.Parent = hwnd)
      {
         OwnedWindowID := v.Owned
         MonitoredWindowID := v.Parent
         SetTimer, WatchingMove, 10
         break
      }
   return
   
WatchingMove:
   WinGetPos, X, Y,,, ahk_id %MonitoredWindowID%
   WinMove, ahk_id %OwnedWindowID%,, X+ShiftX, Y+ShiftY
   
   if (i++ = 30)
   {
      i := 0
      SetTimer, WatchingMove, Off
   }
   return
}

AlwaysOnTopOff(hwnd)
{
   for k, v in GUIs
      if (v.Parent = hwnd)
      {
         WinSet, AlwaysOnTop, Off, ahk_id %hwnd%
         Gui, % v.Owned ":Destroy"
         GUIs.Remove(k)
         break
      }
}

SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags)
{
   return DllCall("SetWinEventHook" , UInt, eventMin, UInt, eventMax
                                    , Ptr, hmodWinEventProc, Ptr, lpfnWinEventProc
                                    , UInt, idProcess, UInt, idThread
                                    , UInt, dwFlags, Ptr)
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

17 (изменено: Drugoy, 2013-02-10 12:30:48)

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Это хорошо, что он больше не убирает alwaysontop при выходе, да и в остальном стал получше работать.
В исключения только надо ещё добавить:
1. preview'шки окон, появляющиеся при наведении на окно на панели задач (вин7).
2. fullscreen окна, а для этого надо бы добавить проверку на fullscreen - как это лучше сделать? на ум приходит только такое: через сравнение размера монитора с размером активного окна: если совпадают, значит окно - в полноэкранном режиме. Есть предложения получше?
3. вносить в исключения тултипы по их классам - как-то не комильфо. Я так понимаю, что можно их как-то иначе исключить (через стили?).
А ещё, для уменьшения визуального лага при перетаскивании окна с красной меткой - не лучше ли сделать слежку не только по таймеру, но и по {LButton Down} + {LButton Up}?

Спасибо большое за улучшения скрипта!

18

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

#NoEnv
SetBatchLines, -1
SetWinDelay, 0

Hotkey := "!1"   ; указать горячую клавишу для установки/снятия стиля WS_EX_TOPMOST

; сдвиг окна-метки относительно верхнего левого угла окна-хозяина
global ShiftX := 5, ShiftY := 5
, GUIs := [], ScriptPid, Exceptions, WS_EX_TOPMOST := 0x8
, EVENT_OBJECT_SHOW := 0x8002, EVENT_OBJECT_HIDE := 0x8003, OBJID_WINDOW := 0
, EVENT_OBJECT_LOCATIONCHANGE := 0x800B, WINEVENT_SKIPOWNPROCESS := 0x2

; сюда добавляем названия классов окон (или их часть), которые нужно исключить из мониторинга
Exceptions := ["Button", "tooltip", "shadow", "TaskListThumbnailWnd", "TaskListOverlayWnd", "Progman"
                  , "ComboLBox", "Shell_TrayWnd", "TTrayAlert", "#327", "NotifyIconOverflowWindow"]

; отслеживаем появление и скрытие окон
HWINEVENTHOOK1 := SetWinEventHook(EVENT_OBJECT_SHOW, EVENT_OBJECT_HIDE, 0
                     , RegisterCallback("WatchingShowHideWindow", "F"), 0, 0, WINEVENT_SKIPOWNPROCESS)
; отслеживаем изменение позиции окон                             
HWINEVENTHOOK2 := SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, 0
                     , RegisterCallback("WatchingChangeLocation", "F"), 0, 0, WINEVENT_SKIPOWNPROCESS)
OnExit, Exit

Process, Exist
ScriptPid := ErrorLevel

Hotkey, %Hotkey%, SetDeleteOnTop, On

; таймер будет отслеживать AlwaysOnTop статус всех окон, кроме добавленных в Exceptions
SetTimer, MonitoringWindows, 1000
return
   
Esc::ExitApp

MonitoringWindows:
   MonitoringWindows()
   return
   
SetDeleteOnTop:
   SetDeleteOnTop()
   return

Exit:
   DllCall("UnhookWinEvent", Ptr, HWINEVENTHOOK1)
   DllCall("UnhookWinEvent", Ptr, HWINEVENTHOOK2)
   GUIs := ""
   ExitApp

MonitoringWindows()
{
   WinGet, List, List
   Loop % List
   {
      ID := List%A_Index%
      
      WinWait, ahk_id %ID%
      WinGet, PID, PID
      if (PID = ScriptPid)
         continue
      
      for k, v in GUIs
         if (v.Parent = ID)
         {
            WinGet, ExStyle, ExStyle
            if !(ExStyle & WS_EX_TOPMOST)
               AlwaysOnTopOff(ID)
            continue 2
         }

      CheckingAndMarking(ID)
   }
}

SetDeleteOnTop()
{
   WinGet, ID,, A
   WinGetPos, XWIn, YWin,,, ahk_id %ID%
   WinGetClass, Class, ahk_id %ID%
   
   for k, v in Exceptions
      if InStr(Class, v)
      {
         CoordMode, ToolTip
         ToolTip, Это окно класса %Class% исключено из мониторинга!, XWIn, YWin
         Sleep, 1500
         ToolTip
         return
      }
   
   for k, v in GUIs
      if (v.Parent = ID)
      {
         AlwaysOnTopOff(ID)
         return
      }
      
   WinSet, AlwaysOnTop, On, ahk_id %ID%
   CreateMarker(ID, XWin+ShiftX, YWin+ShiftY)
}

CreateMarker(OwnerID, X, Y)
{
   static WS_EX_TRANSPARENT := 0x20
   
; устанавливая расширенный стиль WS_EX_TRANSPARENT делаем окно-метку прозрачным для мыши
; опцией Owner%OwnerID% делаем GUI принадлежащим активному окну, тогда GUI всегда будет поверх окна-хозяина
   Gui, New, +E%WS_EX_TRANSPARENT% -Caption +LastFound +AlwaysOnTop +ToolWindow +hwndhGui +Owner%OwnerID%
   WinSet, Transparent, 200  ; прозрачность окна-метки, от 0 до 255
   Gui, Color, Red
   Gui, Show, NA w12 h12 x%X% y%Y%
   
   GUIs.Insert({Parent: OwnerID, Owned: hGui})
}

WatchingShowHideWindow(hWinEventHook, event, hwnd, idObject)
{
   if (idObject != OBJID_WINDOW)
      return
   
   if (event = EVENT_OBJECT_SHOW)
      CheckingAndMarking(hwnd)
   
   else if (event = EVENT_OBJECT_HIDE)
   {
      if !WinExist("ahk_id" hwnd)
         AlwaysOnTopOff(hwnd)
   }
}

CheckingAndMarking(hwnd)
{
   WinWait, ahk_id %hwnd%
   WinGetClass, Class
   for k, v in Exceptions
      if InStr(Class, v)
         return
      
   WinGet, ExStyle, ExStyle
   if (ExStyle & WS_EX_TOPMOST)
   {
;     ToolTip % Class   ; раскомментировать строку для определения класса и добавления в Exceptions
      WinGetPos, XWin, YWin, WWin, HWin
      
      if (WWin = A_ScreenWidth && HWin = A_ScreenHeight)   ; проверка на полноэкранные окна
         return
      
      CreateMarker(hwnd, XWin+ShiftX, YWin+ShiftY)
   }
}

WatchingChangeLocation(hWinEventHook, event, hwnd, idObject)
{
   if (idObject != OBJID_WINDOW)
      return
  
   static i := 0, OwnedWindowID, MonitoredWindowID
   for k, v in GUIs
      if (v.Parent = hwnd)
      {
         OwnedWindowID := v.Owned
         MonitoredWindowID := v.Parent
         SetTimer, WatchingMove, 10
         break
      }
   return
   
WatchingMove:
   WinGetPos, X, Y,,, ahk_id %MonitoredWindowID%
   WinMove, ahk_id %OwnedWindowID%,, X+ShiftX, Y+ShiftY
   
   if (i++ = 30)
   {
      i := 0
      SetTimer, WatchingMove, Off
   }
   return
}

AlwaysOnTopOff(hwnd)
{
   for k, v in GUIs
      if (v.Parent = hwnd)
      {
         WinSet, AlwaysOnTop, Off, ahk_id %hwnd%
         Gui, % v.Owned ":Destroy"
         GUIs.Remove(k)
         break
      }
}

SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags)
{
   return DllCall("SetWinEventHook" , UInt, eventMin, UInt, eventMax
                                    , Ptr, hmodWinEventProc, Ptr, lpfnWinEventProc
                                    , UInt, idProcess, UInt, idThread
                                    , UInt, dwFlags, Ptr)
}
Drugoy пишет:

В исключения только надо ещё добавить:
1. preview'шки окон, появляющиеся при наведении на окно на панели задач (вин7).

Добавил.

Drugoy пишет:

2. fullscreen окна

Сделал проверку на fullscreen — вроде работает.

Drugoy пишет:

3. вносить в исключения тултипы по их классам - как-то не комильфо. Я так понимаю, что можно их как-то иначе исключить (через стили?).

Нет идей.

Добавлять в исключения можно так. В функции CheckingAndMarking() раскомментировать указанную там строчку, запустить скрипт, открыть тестируемое окно. Если оно WS_EX_TOPMOST, то в тултипе будет указан его класс, который и нужно добавить в массив Exceptions.

А ещё, для уменьшения визуального лага при перетаскивании окна с красной меткой

А какой именно лаг?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

19

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

teadrinker пишет:

Сделал проверку на fullscreen — вроде работает.

Проверил на mpc-hc - не работает, а в коде я даже не нашёл где эта проверка: укажите, пожалуйста №№ строк.

teadrinker пишет:

3. вносить в исключения тултипы по их классам - как-то не комильфо. Я так понимаю, что можно их как-то иначе исключить (через стили?).

Нет идей.
Добавлять в исключения можно так. В функции CheckingAndMarking() раскомментировать указанную там строчку, запустить скрипт, открыть тестируемое окно. Если оно WS_EX_TOPMOST, то в тултипе будет указан его класс, который и нужно добавить в массив Exceptions.

Да как узнать узнать класс окон - я-то знаю. Просто подумал, что можно для все тултипы сразу внести в исключения проведя ряд каких-нибудь проверок на различные стили: они же довольно сильно отличаются от обычных окон: не имеют рамки как у окон, не имеют тайтлбара и кнопок свернуть/развернуть/закрыть.

А какой именно лаг?

А, в принципе, не такой уж и большой, не обращайте внимания.

20

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Drugoy пишет:

Проверил на mpc-hc - не работает, а в коде я даже не нашёл где эта проверка: укажите, пожалуйста №№ строк.

В функции CheckingAndMarking(). Проверяется соответствие размеров окна размерам экрана. Другого способа пока в голову не приходит.

Drugoy пишет:

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

Всё это может быть и у других окон. Можно исключить все окна без рамки и без заголовка, но не уверен, что это будет корректно.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

21

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

teadrinker пишет:

Можно исключить все окна без рамки и без заголовка, но не уверен, что это будет корректно.

Вот такой вариант, можешь протестировать. Может, и с полноэкранными окнами прокатит. Заодно ещё баг исправил.

#NoEnv
SetBatchLines, -1
SetWinDelay, 0

Hotkey := "!1"   ; указать горячую клавишу для установки/снятия стиля WS_EX_TOPMOST

; сдвиг окна-метки относительно верхнего левого угла окна-хозяина
global ShiftX := 5, ShiftY := 5
, GUIs := [], ScriptPid, Exceptions, WS_EX_TOPMOST := 0x8
, EVENT_OBJECT_SHOW := 0x8002, EVENT_OBJECT_HIDE := 0x8003, OBJID_WINDOW := 0
, EVENT_OBJECT_LOCATIONCHANGE := 0x800B, WINEVENT_SKIPOWNPROCESS := 0x2

; сюда добавляем названия классов окон (или их часть), которые нужно исключить из мониторинга
Exceptions := ["Button", "tooltip", "shadow", "TaskListThumbnailWnd", "TaskListOverlayWnd", "Progman"
                  , "ComboLBox", "Shell_TrayWnd", "TTrayAlert", "#327", "NotifyIconOverflowWindow"]

; отслеживаем появление и скрытие окон
HWINEVENTHOOK1 := SetWinEventHook(EVENT_OBJECT_SHOW, EVENT_OBJECT_HIDE, 0
                     , RegisterCallback("WatchingShowHideWindow", "F"), 0, 0, WINEVENT_SKIPOWNPROCESS)
; отслеживаем изменение позиции окон                             
HWINEVENTHOOK2 := SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, 0
                     , RegisterCallback("WatchingChangeLocation", "F"), 0, 0, WINEVENT_SKIPOWNPROCESS)
OnExit, Exit

Process, Exist
ScriptPid := ErrorLevel

Hotkey, %Hotkey%, SetDeleteOnTop, On

; таймер будет отслеживать AlwaysOnTop статус всех окон, кроме добавленных в Exceptions
SetTimer, MonitoringWindows, 1000
return

Esc::ExitApp

MonitoringWindows:
   MonitoringWindows()
   return
   
SetDeleteOnTop:
   SetDeleteOnTop()
   return

Exit:
   DllCall("UnhookWinEvent", Ptr, HWINEVENTHOOK1)
   DllCall("UnhookWinEvent", Ptr, HWINEVENTHOOK2)
   GUIs := ""
   ExitApp

MonitoringWindows()
{
   for k, v in GUIs
      if !WinExist("ahk_id" v.Parent)
         AlwaysOnTopOff(v.Parent)
      
   WinGet, List, List
   Loop % List
   {
      ID := List%A_Index%
      
      WinWait, ahk_id %ID%
      WinGet, PID, PID
      if (PID = ScriptPid)
         continue
      
      for k, v in GUIs
         if (v.Parent = ID)
         {
            WinGet, ExStyle, ExStyle
            if !(ExStyle & WS_EX_TOPMOST)
               AlwaysOnTopOff(ID)
            continue 2
         }
         
      CheckingAndMarking(ID)
   }
}

SetDeleteOnTop()
{
   WinGet, ID,, A
   WinGetPos, XWIn, YWin,,, ahk_id %ID%
   WinGetClass, Class, ahk_id %ID%
   
   for k, v in Exceptions
      if InStr(Class, v)
      {
         CoordMode, ToolTip
         ToolTip, Это окно класса %Class% исключено из мониторинга!, XWIn, YWin
         Sleep, 1500
         ToolTip
         return
      }
   
   for k, v in GUIs
      if (v.Parent = ID)
      {
         AlwaysOnTopOff(ID)
         return
      }
      
   WinSet, AlwaysOnTop, On, ahk_id %ID%
   CreateMarker(ID, XWin+ShiftX, YWin+ShiftY)
}

CreateMarker(OwnerID, X, Y)
{
   static WS_EX_TRANSPARENT := 0x20
   
; устанавливая расширенный стиль WS_EX_TRANSPARENT делаем окно-метку прозрачным для мыши
; опцией Owner%OwnerID% делаем GUI принадлежащим активному окну, тогда GUI всегда будет поверх окна-хозяина
   Gui, New, +E%WS_EX_TRANSPARENT% -Caption +LastFound +AlwaysOnTop +ToolWindow +hwndhGui +Owner%OwnerID%
   WinSet, Transparent, 200  ; прозрачность окна-метки, от 0 до 255
   Gui, Color, Red
   Gui, Show, NA w12 h12 x%X% y%Y%
   
   GUIs.Insert({Parent: OwnerID, Owned: hGui})
}

WatchingShowHideWindow(hWinEventHook, event, hwnd, idObject)
{
   if (idObject != OBJID_WINDOW)
      return
   
   if (event = EVENT_OBJECT_SHOW)
      CheckingAndMarking(hwnd)
   
   else if (event = EVENT_OBJECT_HIDE)
   {
      if !WinExist("ahk_id" hwnd)
         AlwaysOnTopOff(hwnd)
   }
}

CheckingAndMarking(hwnd)
{
   static WS_POPUP := 0x80000000, WS_CAPTION := 0xC00000, WS_BORDER := 0x800000
   
   WinWait, ahk_id %hwnd%
   WinGetClass, Class
   for k, v in Exceptions
      if InStr(Class, v)
         return
      
   WinGet, ExStyle, ExStyle
   if (ExStyle & WS_EX_TOPMOST)
   {
      WinGet, Style, Style
      if (Style & WS_POPUP && !(Style & WS_CAPTION) && !(Style & WS_BORDER))
         return
      
;     ToolTip % Class   ; раскомментировать строку для определения класса и добавления в Exceptions
      WinGetPos, XWin, YWin, WWin, HWin
      
      if (WWin = A_ScreenWidth && HWin = A_ScreenHeight)   ; проверка на полноэкранные окна
         return
      
      CreateMarker(hwnd, XWin+ShiftX, YWin+ShiftY)
   }
}

WatchingChangeLocation(hWinEventHook, event, hwnd, idObject)
{
   if (idObject != OBJID_WINDOW)
      return
  
   static i := 0, OwnedWindowID, MonitoredWindowID
   for k, v in GUIs
      if (v.Parent = hwnd)
      {
         OwnedWindowID := v.Owned
         MonitoredWindowID := v.Parent
         SetTimer, WatchingMove, 10
         break
      }
   return
   
WatchingMove:
   WinGetPos, X, Y,,, ahk_id %MonitoredWindowID%
   WinMove, ahk_id %OwnedWindowID%,, X+ShiftX, Y+ShiftY
   
   if (i++ = 30)
   {
      i := 0
      SetTimer, WatchingMove, Off
   }
   return
}

AlwaysOnTopOff(hwnd)
{
   for k, v in GUIs
      if (v.Parent = hwnd)
      {
         WinSet, AlwaysOnTop, Off, ahk_id %hwnd%
         Gui, % v.Owned ":Destroy"
         GUIs.Remove(k)
         break
      }
}

SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags)
{
   return DllCall("SetWinEventHook" , UInt, eventMin, UInt, eventMax
                                    , Ptr, hmodWinEventProc, Ptr, lpfnWinEventProc
                                    , UInt, idProcess, UInt, idThread
                                    , UInt, dwFlags, Ptr)
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

22

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

teadrinker пишет:

В функции CheckingAndMarking(). Проверяется соответствие размеров окна размерам экрана. Другого способа пока в голову не приходит.

Не понимаю почему оно тогда не работает, вроде проверка простая.

teadrinker пишет:

Вот такой вариант, можешь протестировать. Может, и с полноэкранными окнами прокатит. Заодно ещё баг исправил.

С Media Player Classis - HomeCinema работает так: в настройках самого плеера стоит "назначать alwaysontop если идёт видео". Если видео проигрывается и его развернуть на весь экран - красная метка остаётся, а если видео сначала поставить на паузу (тогда метка слетает), потом его развернуть на весь экран и начать проигрывать - метки нет.
Возможно, что это просто в MPC-HC сделано так, что в полноэкранном режиме метка alwaysontop не назначается окну даже если включена настройка "назначать alwaysontop если идёт видео", т.к. незачем.

23

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

У меня с Media Player Classis - HomeCinema работает корректно, в полноэкранном режиме метку не ставит. В общем, если с какими-то окнами проблемы, нужно просто исключать их по классу. Кстати, в исключения ещё "SysDragImage" нужно добавить — окна, возникающие при перетаскивании объектов.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

24

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

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

25

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Заметил баг при взаимодействии с другим скриптом:

XButton2 & LButton::KDE_WinMove("LButton")
KDE_WinMove(sButton)
{
    SetWinDelay, 0
    CoordMode, Mouse
    MouseGetPos, x1, y1, id
    WinExist("ahk_id" . id)
    WinGetClass, winClass
    WinGet, maximized, MinMax
    If (!GetKeyState(sButton, "P")) || (winClass == "WorkerW") || (maximized)
        Exit
    WinGetPos, winX1, winY1
    While GetKeyState(sButton, "P")
    {
        MouseGetPos, x2, y2
        x2 -= x1, y2 -= y1, winX2 := winX1 + x2, winY2 := winY1 + y2
        WinMove,,, winX2, winY2
        Sleep 15
    }
    Gosub, CleanVars
}

Я использую этот код для того, чтобы перемещать окна за любую их часть (неактивные (без их активации) и активные). И при таком перетаскивании - индикатор остаётся на старом месте. Не могли бы вы это подправить?

26

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

То-есть, фактически вопрос в том, что если двигать окно с помощью WinMove, метка остаётся на месте? Это не так. Проверить можно очень просто. Добавляем в скрипт такую горячую клавишу:

F10:: 
    WinGetPos, X, Y,,, ahk_class Notepad
    WinMove, ahk_class Notepad,, X + 50, Y + 50
    return

Полный код:

#NoEnv
SetBatchLines, -1
SetWinDelay, 0

Hotkey := "!1"   ; указать горячую клавишу для установки/снятия стиля WS_EX_TOPMOST

; сдвиг окна-метки относительно верхнего левого угла окна-хозяина
global ShiftX := 5, ShiftY := 5
, GUIs := [], ScriptPid, Exceptions, WS_EX_TOPMOST := 0x8
, EVENT_OBJECT_SHOW := 0x8002, EVENT_OBJECT_HIDE := 0x8003, OBJID_WINDOW := 0
, EVENT_OBJECT_LOCATIONCHANGE := 0x800B, WINEVENT_SKIPOWNPROCESS := 0x2

; сюда добавляем названия классов окон (или их часть), которые нужно исключить из мониторинга
Exceptions := ["Button", "tooltip", "shadow", "TaskListThumbnailWnd", "TaskListOverlayWnd", "Progman"
                  , "ComboLBox", "Shell_TrayWnd", "TTrayAlert", "#327", "NotifyIconOverflowWindow"]

; отслеживаем появление и скрытие окон
HWINEVENTHOOK1 := SetWinEventHook(EVENT_OBJECT_SHOW, EVENT_OBJECT_HIDE, 0
                     , RegisterCallback("WatchingShowHideWindow", "F"), 0, 0, WINEVENT_SKIPOWNPROCESS)
; отслеживаем изменение позиции окон                             
HWINEVENTHOOK2 := SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, 0
                     , RegisterCallback("WatchingChangeLocation", "F"), 0, 0, WINEVENT_SKIPOWNPROCESS)
OnExit, Exit

Process, Exist
ScriptPid := ErrorLevel

Hotkey, %Hotkey%, SetDeleteOnTop, On

; таймер будет отслеживать AlwaysOnTop статус всех окон, кроме добавленных в Exceptions
SetTimer, MonitoringWindows, 1000
return

Esc::ExitApp

F10:: 
    WinGetPos, X, Y,,, ahk_class Notepad
    WinMove, ahk_class Notepad,, X + 50, Y + 50
    return

MonitoringWindows:
   MonitoringWindows()
   return
   
SetDeleteOnTop:
   SetDeleteOnTop()
   return

Exit:
   DllCall("UnhookWinEvent", Ptr, HWINEVENTHOOK1)
   DllCall("UnhookWinEvent", Ptr, HWINEVENTHOOK2)
   GUIs := ""
   ExitApp

MonitoringWindows()
{
   for k, v in GUIs
      if !WinExist("ahk_id" v.Parent)
         AlwaysOnTopOff(v.Parent)
      
   WinGet, List, List
   Loop % List
   {
      ID := List%A_Index%
      
      WinWait, ahk_id %ID%
      WinGet, PID, PID
      if (PID = ScriptPid)
         continue
      
      for k, v in GUIs
         if (v.Parent = ID)
         {
            WinGet, ExStyle, ExStyle
            if !(ExStyle & WS_EX_TOPMOST)
               AlwaysOnTopOff(ID)
            continue 2
         }
         
      CheckingAndMarking(ID)
   }
}

SetDeleteOnTop()
{
   WinGet, ID,, A
   WinGetPos, XWIn, YWin,,, ahk_id %ID%
   WinGetClass, Class, ahk_id %ID%
   
   for k, v in Exceptions
      if InStr(Class, v)
      {
         CoordMode, ToolTip
         ToolTip, Это окно класса %Class% исключено из мониторинга!, XWIn, YWin
         Sleep, 1500
         ToolTip
         return
      }
   
   for k, v in GUIs
      if (v.Parent = ID)
      {
         AlwaysOnTopOff(ID)
         return
      }
      
   WinSet, AlwaysOnTop, On, ahk_id %ID%
   CreateMarker(ID, XWin+ShiftX, YWin+ShiftY)
}

CreateMarker(OwnerID, X, Y)
{
   static WS_EX_TRANSPARENT := 0x20
   
; устанавливая расширенный стиль WS_EX_TRANSPARENT делаем окно-метку прозрачным для мыши
; опцией Owner%OwnerID% делаем GUI принадлежащим активному окну, тогда GUI всегда будет поверх окна-хозяина
   Gui, New, +E%WS_EX_TRANSPARENT% -Caption +LastFound +AlwaysOnTop +ToolWindow +hwndhGui +Owner%OwnerID%
   WinSet, Transparent, 200  ; прозрачность окна-метки, от 0 до 255
   Gui, Color, Red
   Gui, Show, NA w12 h12 x%X% y%Y%
   
   GUIs.Insert({Parent: OwnerID, Owned: hGui})
}

WatchingShowHideWindow(hWinEventHook, event, hwnd, idObject)
{
   if (idObject != OBJID_WINDOW)
      return
   
   if (event = EVENT_OBJECT_SHOW)
      CheckingAndMarking(hwnd)
   
   else if (event = EVENT_OBJECT_HIDE)
   {
      if !WinExist("ahk_id" hwnd)
         AlwaysOnTopOff(hwnd)
   }
}

CheckingAndMarking(hwnd)
{
   static WS_POPUP := 0x80000000, WS_CAPTION := 0xC00000, WS_BORDER := 0x800000
   
   WinWait, ahk_id %hwnd%
   WinGetClass, Class
   for k, v in Exceptions
      if InStr(Class, v)
         return
      
   WinGet, ExStyle, ExStyle
   if (ExStyle & WS_EX_TOPMOST)
   {
      WinGet, Style, Style
      if (Style & WS_POPUP && !(Style & WS_CAPTION) && !(Style & WS_BORDER))
         return
      
;     ToolTip % Class   ; раскомментировать строку для определения класса и добавления в Exceptions
      WinGetPos, XWin, YWin, WWin, HWin
      
      if (WWin = A_ScreenWidth && HWin = A_ScreenHeight)   ; проверка на полноэкранные окна
         return
      
      CreateMarker(hwnd, XWin+ShiftX, YWin+ShiftY)
   }
}

WatchingChangeLocation(hWinEventHook, event, hwnd, idObject)
{
   if (idObject != OBJID_WINDOW)
      return
  
   static i := 0, OwnedWindowID, MonitoredWindowID
   for k, v in GUIs
      if (v.Parent = hwnd)
      {
         OwnedWindowID := v.Owned
         MonitoredWindowID := v.Parent
         SetTimer, WatchingMove, 10
         break
      }
   return
   
WatchingMove:
   WinGetPos, X, Y,,, ahk_id %MonitoredWindowID%
   WinMove, ahk_id %OwnedWindowID%,, X+ShiftX, Y+ShiftY
   
   if (i++ = 30)
   {
      i := 0
      SetTimer, WatchingMove, Off
   }
   return
}

AlwaysOnTopOff(hwnd)
{
   for k, v in GUIs
      if (v.Parent = hwnd)
      {
         WinSet, AlwaysOnTop, Off, ahk_id %hwnd%
         Gui, % v.Owned ":Destroy"
         GUIs.Remove(k)
         break
      }
}

SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags)
{
   return DllCall("SetWinEventHook" , UInt, eventMin, UInt, eventMax
                                    , Ptr, hmodWinEventProc, Ptr, lpfnWinEventProc
                                    , UInt, idProcess, UInt, idThread
                                    , UInt, dwFlags, Ptr)
}

Запускаем скрипт, открываем блокнот, присваиваем его окну метку (Alt + 1), нажимаем F10. Метка передвигается вместе с окном, независимо от того, активно оно, или нет. Т. е. ошибка где-то в твоей интерпретации моего скрипта.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

27

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Хм, тут всё немного сложней, чем просто необработка WinMove:
Дело может быть связанос однопоточностью AHK: когда выполняется мой хоткей - до других строк кода, поток может попросту не доходить, а значит нужны gosub (но куда?).

+ я немного запутался в версиях твоего скрипта (+ я же их ещё под себя переделывал)

28

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Ну так перемещение окна же делается другим скриптом, значит в другом потоке (однопоточность — это внутри одного скрипта), соответственно это повлиять никак не должно.

Drugoy пишет:

+ я немного запутался в версиях твоего скрипта (+ я же их ещё под себя переделывал)

Тут уж ничем не могу помочь

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

29

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

teadrinker пишет:

Ну так перемещение окна же делается другим скриптом

нет, я их слил воедино.

30 (изменено: creature.ws, 2013-03-04 03:38:10)

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Слили, жмёте горячую клавишу, запускается псевдопоток горячей клавиши.
Запущенный псевдопоток не может быть прерван иным псевдопотоком в течении 15мс от запуска или на протяжении выполнения в псевдопотоке 1000 строк кода.

By default, every newly launched thread is uninterruptible for a Duration of 15 milliseconds or a LineCount of 1000 script lines,

Псевдопотоки выполняются «параллельно», но не одновременно, если нажатие горячей клавиши произойдёт во время выполнения команды в ином псевдопотоке — начало выполнения подпрограммы горячей клавиши будет отложено до того момента как активный псевдопоток проработает более 15мс, либо выполнит 1000 строк кода, либо закончит своё выполнение, либо произведёт вызов sleep.
Для понимания:

Thread, Interrupt, 2000

SetTimer beep, 500
return

beep:
    i++
    ToolTip % i
    SoundBeep
    return

Space::
    SoundBeep 750
    sleep 2000
    SoundBeep 750
    return

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

+ открыть спойлер

GoSub не помогут, две «насыщенные действиями, не использующие sleep» подпрограммы могут эффективно работать одновременно,  но не «параллельно», как это предлагает autohotkey.

31 (изменено: Drugoy, 2013-06-27 19:03:47)

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

teadrinker,
а для чего в твоём скрипте вот эти строки?

Process, Exist ; это же проверка "а запущен ли процесс этого скрипта?", она же всегда будет выдавать ErrorLevel := false, ведь в момент, когда она выдала бы true - скрипт то уже не выполняется.
ScriptPid := ErrorLevel
...
if (PID = ScriptPid)
         continue

?

32

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

teadrinker пишет:

Ну так перемещение окна же делается другим скриптом, значит в другом потоке (однопоточность — это внутри одного скрипта), соответственно это повлиять никак не должно.

Drugoy пишет:

+ я немного запутался в версиях твоего скрипта (+ я же их ещё под себя переделывал)

Тут уж ничем не могу помочь

+ Кстати.

Я люблю своё оформление кода и переделывал твоё оформление под своё.
Люблю правило, что функции (как и переменные) следует именовать используя camelCase.

Так вот я перестарался с исправлением заглавных букв и у меня вместо

setWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags)
{
    Return DllCall("SetWinEventHook" , UInt, eventMin, UInt, eventMax, Ptr, hmodWinEventProc, Ptr, lpfnWinEventProc, UInt, idProcess, UInt, idThread, UInt, dwFlags, Ptr)
}

получилось

setWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags)
{
    Return DllCall("setWinEventHook" , UInt, eventMin, UInt, eventMax, Ptr, hmodWinEventProc, Ptr, lpfnWinEventProc, UInt, idProcess, UInt, idThread, UInt, dwFlags, Ptr)
}

И именно из-за этого переставали маркеры следить за перемещением окон при их передвижении. (:

33

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Drugoy пишет:

teadrinker,
а для чего в твоём скрипте вот эти строки?

Process, Exist ; это же проверка "а запущен ли процесс этого скрипта?", она же всегда будет выдавать ErrorLevel := false, ведь в момент, когда она выдала бы true - скрипт то уже не выполняется.
ScriptPid := ErrorLevel
...
if (PID = ScriptPid)
         continue

?

Нет, это проверка, не является ли рассматриваемое окно принадлежащим скрипту, т. е. собственно одной из меток. В этом случае переходим к следующему.

Drugoy пишет:

Так вот я перестарался с исправлением заглавных букв и у меня вместо ... получилось

А в чём разница, не заметил?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

34

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

teadrinker пишет:

Нет, это проверка, не является ли рассматриваемое окно принадлежащим скрипту, т. е. собственно одной из меток. В этом случае переходим к следующему.

Да, я не правильно понимал этот кусок документации:
http://l.autohotkey.net/docs/commands/Process.htm

CMD (param) > Exist (possible value): Sets ErrorLevel to the Process ID (PID) if a matching process exists, or 0 otherwise. If the PID-or-Name parameter is blank, the script's own PID is retrieved.

оказывается, в ErrorLevel записывается PID скрипта, а не просто информация о том, что он запущен (т.е. просто 1), что будь оно так - было бы бесполезно в принципе.

Если честно - то и без этих проверок всё работает: я так понимаю, что эта проверка нужна была ради избежания коллизий на случай, если вдруг сам маркер (это же GUI окно, просто особенное) станет активным окном то пользователь может попытаться добавить AOT флаг ему, а не окну к которому он прикреплён.
А если вдруг я захочу приделать скрипту какой-то свой нормальные GUI для чего-то (например, для настроек) - то почему это я не должен иметь возможность сделать и его AOT?

Drugoy пишет:

А в чём разница, не заметил?

Там где DllCall() - первый параметр у тебя идёт с большой буквы, если сменить на маленькую - маркер перестаёт следить за перемещениями окон.

Скрипт получился очень хорошим, вот только уже за дни его использования заметил 2 маленьких помарки, которые хотелось бы исправить, да не очень понимаю как:
1. Если до старта скрипта уже есть какие-то 2 окна с AOT флагом и одно из них активно и находится поверх другого, то при старте скрипта активное окно почему-то скрывается за неактивным. При этом активное окно по-прежнему остаётся активным.
Я уже вычислил, что похоже это из-за работы функции checkingAndMarking(hwnd).
2. У меня по-прежнему в полноэкранных приложениях (например, mpc-hc), если им выставить AOT флаг - показывается маркер. При этом внутри функции checkingAndMarking(hwnd) есть же проверка на полноэкранность:

If (ExStyle & WS_EX_TOPMOST)
{
    ...
    If (WWin == A_ScreenWidth && HWin == A_ScreenHeight)    ; Fullscreen check: such windows should not get a marker.
            Return
    ...
}

Но (ExStyle & WS_EX_TOPMOST) возвращает true почему-то только при снятии АОТ метки. Если же перед этим условием поставить "!" (т.е. логически переключить его) - скрипт пятит.

35

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Drugoy пишет:

Там где DllCall() - первый параметр у тебя идёт с большой буквы, если сменить на маленькую - маркер перестаёт следить за перемещениями окон.

Нет, AHK — регистронезависимый язык, это повлиять не может, проблема в чём-то другом.

Drugoy пишет:

Скрипт получился очень хорошим, вот только уже за дни его использования заметил 2 маленьких помарки, которые хотелось бы исправить, да не очень понимаю как

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

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

36

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

teadrinker пишет:

Нет, AHK — регистронезависимый язык, это повлиять не может, проблема в чём-то другом.

Я у себя проверяю - если заменить там на маленькую букву (не в слове DllCall, а в его первом параметре) - сразу перестают маркеры следить за окнами. Меняю обратно - всё ок, следят. Проверь у себя, если не веришь.

teadrinker пишет:
Drugoy пишет:

Скрипт получился очень хорошим, вот только уже за дни его использования заметил 2 маленьких помарки, которые хотелось бы исправить, да не очень понимаю как

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

Да на самом деле, с течением времен всплыли ещё множество проблем:
1. список исключений срабатывает по частичному совпадению: от этого окна некоторых программ (вроде окна tabsrmm контейнера в miranda im/ng) нельзя пришпилить, т.к. у них ahk_class #32700. Если убрать из исключений #327 - то во маркер будет рисоваться у каждого контекстного меню в системе.
2. скрипт можно повесить: достаточно в miranda'е открыть окно history++, включить там поиск (ctrl+f) и навести курсор на кнопку "вперёд" или "назад": на месте попапа, всплывающего для них появляется маркер, сам попап исчезает, а маркер остаётся навсегда и после этого скрипт перестаёт работать, а чтобы его рестартнуть надо либо убивать его процесс (а они все autohotkey.exe блин, поэтому приходится его искать среди всех запущенных), либо через контекстное меню, но при открытии контекстного меню - всё его содержимое отображается чёрным треугольником, можно только по памяти найти позицию менюитема "рестарт скрипта".
3. Очень редко, скрипт застревает на каком-то вечном лупе, это настолько редко, что я даже не пытался дебажить.

37

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Drugoy пишет:

если заменить там на маленькую букву (не в слове DllCall, а в его первом параметре) - сразу перестают маркеры следить за окнами.

Да, действительно, это так. Это не просто "первый параметр", это название функции в dll, к которой обращается DllCall(). Оказывается, они регистрозависимые, раньше не знал об этом. Никогда просто не пытался написать их названия иначе, чем в MSDN.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

38

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Drugoy пишет:

2. У меня по-прежнему в полноэкранных приложениях (например, mpc-hc), если им выставить AOT флаг - показывается маркер. При этом внутри функции checkingAndMarking(hwnd) есть же проверка на полноэкранность

Всё дело оказалось в том, что до сегодняшнего дня я не осознавал, что код

a := b :=

не чистит переменные, а чистить их надо так:

a := b := ""

Исправил все подобные чистки переменных в скрипте и этот баг пропал (:

39

Re: AHK: пририсовывать alwaysOnTop окнам кружок в углу окна (GDI+)

Гм, видимо, это был какой-то глюк, раз баг на время исчез. Но сейчас он снова тут.
Начал дебажить и узнал, что в функции checkingAndMarking() попадают окна самих маркеров (ведь они тоже же AOT), а не тех окон, к которым они прилеплены.