Вот более корректный вариант. Некоторые окна при закрытии могут не уничтожаться, а просто скрываться, прежний скрипт этого не учитывает.
#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)
}
А вот вариант с 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.