<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title><![CDATA[Серый форум &mdash; AHK v2: Можно ли кнопкой X сворачивать окно в трей, а не kill процесс?]]></title>
		<link>https://forum.script-coding.com/viewtopic.php?id=18580</link>
		<atom:link href="https://forum.script-coding.com/extern.php?action=feed&amp;tid=18580&amp;type=rss" rel="self" type="application/rss+xml" />
		<description><![CDATA[Недавние сообщения в теме «AHK v2: Можно ли кнопкой X сворачивать окно в трей, а не kill процесс?».]]></description>
		<lastBuildDate>Fri, 10 Apr 2026 12:28:15 +0000</lastBuildDate>
		<generator>PunBB</generator>
		<item>
			<title><![CDATA[Re: AHK v2: Можно ли кнопкой X сворачивать окно в трей, а не kill процесс?]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?pid=163320#p163320</link>
			<description><![CDATA[<p><a href="https://www.autohotkey.com/docs/v2/lib/WinGetProcessPath.htm">WinGetProcessPath</a></p>]]></description>
			<author><![CDATA[null@example.com (teadrinker)]]></author>
			<pubDate>Fri, 10 Apr 2026 12:28:15 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?pid=163320#p163320</guid>
		</item>
		<item>
			<title><![CDATA[Re: AHK v2: Можно ли кнопкой X сворачивать окно в трей, а не kill процесс?]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?pid=163319#p163319</link>
			<description><![CDATA[<p>Большое спасибо за пример, очень помог разобраться.</p><p>В итоге взял у вас идею с перехватом клика по трею через OnMessage(0x404) и логику работы с WinExist + IsWindowVisible + ShowWindow. На этой основе собрал свой вариант.</p><p>Сейчас всё работает стабильно:</p><p>ЛКМ по иконке в трее =&gt; сворачивает/разворачивает окно.<br />Alt+F4 =&gt; скрывает окно вместо закрытия и корректно возвращается в фокус через SetForegroundWindow.</p><p>Вопрос с иконкой решил так:<br />Получаю hwnd окна Snagit, дальше через GetWindowThreadProcessId + QueryFullProcessImageName вытягиваю путь к SnagitEditor.exe и уже его передаю в TraySetIcon(). В итоге в трее отображается родная иконка программы, а не стандартная AHK.</p><p>В целом всё получилось. Поведение теперь как у нормального tray-приложения.</p><br /><p>Ещё раз спасибо за ваши советы и идеи из предоставленного примера — очень помогло решить эту задачу.</p><br /><div class="codebox"><pre><code>#Requires AutoHotkey v2
#SingleInstance Force

SnagitWin := &quot;ahk_class SnagIt9Editor&quot;
DetectHiddenWindows true

ListLines(false)
A_AllowMainWindow := false

hwnd := WinExist(SnagitWin)
if hwnd {
    path := GetWindowProcessPath(hwnd)
    if path != &quot;&quot;
        TraySetIcon(path)
}

GetWindowProcessPath(hwnd) {
    DllCall(&quot;GetWindowThreadProcessId&quot;, &quot;ptr&quot;, hwnd, &quot;uint*&quot;, &amp;pid := 0)
    hProcess := DllCall(&quot;OpenProcess&quot;, &quot;uint&quot;, 0x1000, &quot;int&quot;, 0, &quot;uint&quot;, pid)
    if !hProcess
        return &quot;&quot;

    buf := Buffer(260 * 2)
    size := 260
    ok := DllCall(&quot;QueryFullProcessImageName&quot;, &quot;ptr&quot;, hProcess, &quot;uint&quot;, 0, &quot;ptr&quot;, buf, &quot;uint*&quot;, &amp;size)
    DllCall(&quot;CloseHandle&quot;, &quot;ptr&quot;, hProcess)

    return ok ? StrGet(buf, size) : &quot;&quot;
}

OnMessage(0x404, TrayClick)

TrayClick(wParam, lParam, msg, hwnd) {
    if (lParam = 0x201) {
        hwnd := WinExist(SnagitWin)
        if hwnd {
            visible := DllCall(&quot;IsWindowVisible&quot;, &quot;ptr&quot;, hwnd)
            DllCall(&quot;ShowWindow&quot;, &quot;ptr&quot;, hwnd, &quot;int&quot;, visible ? 0 : 9)
            if !visible
                DllCall(&quot;SetForegroundWindow&quot;, &quot;ptr&quot;, hwnd)
        }
        return 0
    }
}

!F4:: {
    hwnd := WinExist(SnagitWin)
    if WinActive(SnagitWin) &amp;&amp; hwnd
        DllCall(&quot;ShowWindow&quot;, &quot;ptr&quot;, hwnd, &quot;int&quot;, 0)
}</code></pre></div>]]></description>
			<author><![CDATA[null@example.com (leex)]]></author>
			<pubDate>Fri, 10 Apr 2026 07:35:27 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?pid=163319#p163319</guid>
		</item>
		<item>
			<title><![CDATA[Re: AHK v2: Можно ли кнопкой X сворачивать окно в трей, а не kill процесс?]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?pid=163318#p163318</link>
			<description><![CDATA[<p>Я использую такой скрипт для сворачивания любых окон в трей, может вам подойдёт, как есть, или приспособите под свои нужды:<br /></p><div class="codebox"><pre><code>#Requires AutoHotkey v2.0
#NoTrayIcon

hider := HideToTray()

!6:: hider.Hide(WinExist(&#039;A&#039;))
!7:: hider.RestoreLast()
!8:: hider.RestoreAll()

class HideToTray
{
    ; add classes here to exclude from hiding:
    excludeClasses := [&#039;Progman&#039;, &#039;WorkerW&#039;, &#039;Shell_TrayWnd&#039;, &#039;ApplicationFrameWindow&#039;]
    
    static __New() =&gt; SingletonDecorator.ApplyTo(this)
    
    __New() {
        this.test := &#039;&#039;
        this.exclude := Map()
        for winClass in this.excludeClasses {
            this.exclude[winClass] := &#039;&#039;
        }
        this.hiddenWindows := []
        this.knownWindows := Map()
        this.knownWindows.DefineProp(&#039;DeleteIfExists&#039;, {call: (s, p) =&gt; s.Has(p) &amp;&amp; s.Delete(p)})
        this.SetHooks()
        Loop 2 {
            ObjRelease(ObjPtr(this))
        }
    }

    ; public methods
    Hide(hWnd) {
        if !this.exclude.Has(WinGetClass(hWnd)) {
            this.stopHook := true
            this.hiddenWindows.Push(HideToTray.HiddenWindow(hWnd))
            this.knownWindows[hWnd] := &#039;&#039;
            this.stopHook := false
        }
    }
    RestoreAll() =&gt; (this.stopHook := true, this.hiddenWindows := [], this.stopHook := false)
    RestoreLast() =&gt; this.hiddenWindows.Has(1) &amp;&amp; this.hiddenWindows.Pop()

    ; private methods
    SetHooks() {
        static EVENT_SYSTEM_MINIMIZESTART := 0x0016
             , EVENT_SYSTEM_MINIMIZEEND   := 0x0017
             , EVENT_OBJECT_DESTROY       := 0x8001
             , OBJID_WINDOW := 0

        this.HasOwnProp(&#039;stopHook&#039;) || (
            this.stopHook := false,
            OnMessage(0x404, this.OnNotify := ObjBindMethod(this, &#039;AHK_NOTIFYICON&#039;)),
            this.destroyHook := WinEventHook(EVENT_OBJECT_DESTROY      , EVENT_OBJECT_DESTROY    , HookProc),
            this.minHook     := WinEventHook(EVENT_SYSTEM_MINIMIZESTART, EVENT_SYSTEM_MINIMIZEEND, HookProc)
        )

        HookProc(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime) {
            if idObject = OBJID_WINDOW &amp;&amp; !this.stopHook {
                switch event {
                    case EVENT_OBJECT_DESTROY       : this.knownWindows.DeleteIfExists(hwnd), this.UnHide(hwnd)
                    case EVENT_SYSTEM_MINIMIZESTART : this.knownWindows.Has(hwnd) &amp;&amp; this.Hide(hwnd)
                    case EVENT_SYSTEM_MINIMIZEEND   : this.UnHide(hwnd)
                }
            }
        }
    }

    UnHide(hWnd) {
        try for i, wnd in this.hiddenWindows {
            continue
        } until wnd.hwnd = hwnd &amp;&amp; this.hiddenWindows.RemoveAt(i)
    }

    AHK_NOTIFYICON(wp, lp, msg, hwnd) {
        static WM_LBUTTONDOWN := 0x201, WM_RBUTTONUP := 0x205
             , iconID := 0x404, iconMsg := 0x404, maxLenMenuStr := 40
        if !(lp = WM_LBUTTONDOWN || lp = WM_RBUTTONUP) {
            return
        }
        for i, wnd in this.hiddenWindows {
            if wnd.icon.wnd.hwnd = hwnd {
                switch lp {
                    case WM_RBUTTONUP: ShowMenu(i)
                    case WM_LBUTTONDOWN: this.hiddenWindows.RemoveAt(i)
                }
                break
            }
        }
        ShowMenu(idx) {
            title := this.hiddenWindows[idx].title
            b := StrLen(title) &gt; maxLenMenuStr
            menuText := &#039;Restore «&#039; . SubStr(title, 1, maxLenMenuStr) . (b ? &#039;...&#039; : &#039;&#039;) . &#039;»&#039;
            iconMenu := Menu()
            iconMenu.Add(menuText, (*) =&gt; this.AHK_NOTIFYICON(iconID, WM_LBUTTONDOWN, iconMsg, hwnd))
            iconMenu.SetIcon(menuText, &#039;HICON:*&#039; . this.hiddenWindows[idx].icon.hIcon)
            iconMenu.Add(&#039;Restore all windows&#039;, (*) =&gt; this.RestoreAll())
            iconMenu.Show()
            iconMenu.Delete()
        }
    }

    __Delete() {
        Loop 2 {
            ObjAddRef(ObjPtr(this))
        }
        this.DeleteProp(&#039;minHook&#039;)
        this.DeleteProp(&#039;destroyHook&#039;)
        OnMessage(0x404, this.OnNotify, 0)
        this.RestoreAll()
    }

    class HiddenWindow {
        __New(hWnd) {
            this.title := WinGetTitle(hWnd)
            this.icon := TrayIcon(this.GetWindowIcon(hWnd), this.title)
            WinMinimize(hWnd), WinHide(hWnd)
            this.hwnd := hWnd
        }

        __Delete() {
            try WinExist(this.hwnd) &amp;&amp; (WinShow(), WinRestore(), WinActivate())
        }

        GetWindowIcon(hWnd) {
            static WM_GETICON := 0x007F, ICON_SMALL := 0, GCLP_HICONSM := -34
                 , GetClassLong := &#039;GetClassLong&#039; . (A_PtrSize = 4 ? &#039;&#039; : &#039;Ptr&#039;)
            ((hIcon := SendMessage(WM_GETICON, ICON_SMALL, A_ScreenDPI, , hWnd))
            || (hIcon := DllCall(GetClassLong, &#039;Ptr&#039;, hWnd, &#039;Int&#039;, GCLP_HICONSM, &#039;Ptr&#039;))
            || (hIcon := LoadPicture(&#039;Shell32&#039;, &#039;Icon3&#039;, &amp;IMAGE_ICON := 1)))
            return hIcon
        }
    }
}

class TrayIcon
{
    NIF_MESSAGE := 1, NIF_ICON := 2, NIF_TIP := 4
    NIM_ADD := 0, NIM_DELETE := 2
    iconID := 0x404, iconMsg := 0x404

    __New(hIcon, tip?) {
        flags := this.NIF_MESSAGE | this.NIF_ICON | (IsSet(tip) ? this.NIF_TIP : 0)
        this.wnd := Gui(), this.hIcon := hIcon
        this.NOTIFYICONDATA := Buffer(396, 0)
        NumPut(
            &#039;Ptr&#039; , this.NOTIFYICONDATA.size,
            &#039;Ptr&#039; , this.wnd.hwnd,
            &#039;UInt&#039;, this.iconID,
            &#039;UInt&#039;, flags,
            &#039;Ptr&#039; , this.iconMsg,
            &#039;Ptr&#039; , hIcon, this.NOTIFYICONDATA
        )
        if IsSet(tip) {
            StrPut(tip, this.NOTIFYICONDATA.ptr + 4 * A_PtrSize + 8, &#039;CP0&#039;)
        }
        DllCall(&#039;Shell32\Shell_NotifyIcon&#039;, &#039;UInt&#039;, this.NIM_ADD, &#039;Ptr&#039;, this.NOTIFYICONDATA)
    }

    __Delete() {
        DllCall(&#039;Shell32\Shell_NotifyIcon&#039;, &#039;UInt&#039;, this.NIM_DELETE, &#039;Ptr&#039;, this.NOTIFYICONDATA)
        this.wnd.Destroy()
    }
}

class WinEventHook
{
    ; Event Constants: https://is.gd/tRT5Wr
    __New(eventMin, eventMax, hookProc, options := &#039;&#039;, idProcess := 0, idThread := 0, dwFlags := 0) {
        this.callback := CallbackCreate(hookProc, options, 7)
        this.hHook := DllCall(&#039;SetWinEventHook&#039;, &#039;UInt&#039;, eventMin, &#039;UInt&#039;, eventMax, &#039;Ptr&#039;, 0, &#039;Ptr&#039;, this.callback
                                               , &#039;UInt&#039;, idProcess, &#039;UInt&#039;, idThread, &#039;UInt&#039;, dwFlags, &#039;Ptr&#039;)
    }
    __Delete() {
        DllCall(&#039;UnhookWinEvent&#039;, &#039;Ptr&#039;, this.hHook)
        CallbackFree(this.callback)
    }
}

class SingletonDecorator
{
    static ApplyTo(targetClass) {
        proto    := targetClass.Prototype
        origCall := targetClass.GetMethod(&#039;Call&#039;)
        origNew  := proto.HasMethod(&#039;__New&#039;)    ? proto.GetMethod(&#039;__New&#039;)    : &#039;&#039;
        origDel  := proto.HasMethod(&#039;__Delete&#039;) ? proto.GetMethod(&#039;__Delete&#039;) : &#039;&#039;
        
        targetClass.DefineProp(&#039;Call&#039;, {Call: (cls, p*) =&gt; cls.HasProp(&#039;singleton&#039;) ? cls.singleton : origCall(cls, p*)})
        proto.DefineProp(&#039;__New&#039;, {Call: (inst, p*) =&gt; (
            (origNew &amp;&amp; origNew(inst, p*)),
            targetClass.singleton := inst,
            ObjRelease(ObjPtr(inst))
        )})
        proto.DefineProp(&#039;__Delete&#039;, {Call: inst =&gt; (
            ObjAddRef(ObjPtr(inst)),
            (origDel &amp;&amp; origDel(inst)),
            targetClass.DeleteProp(&#039;singleton&#039;)
        )})
    } 
}</code></pre></div>]]></description>
			<author><![CDATA[null@example.com (teadrinker)]]></author>
			<pubDate>Thu, 09 Apr 2026 13:31:21 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?pid=163318#p163318</guid>
		</item>
		<item>
			<title><![CDATA[Re: AHK v2: Можно ли кнопкой X сворачивать окно в трей, а не kill процесс?]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?pid=163317#p163317</link>
			<description><![CDATA[<p>Спасибо за наводку, @teadrinker!</p><p>Вы были правы — было не то окно.</p><p>Использовал ahk_exe SnagitEditor.exe, и вот что происходило:</p><p>При запуске скрипта (даже без активного процесса самого Snagit) в панели задач появлялась иконка SnagitEditor, но окно не открывалось по клику на этом значке.<br />Процесс SnagitEditor.exe стартовал по F11 — в диспетчере было видно, но управление не работало.<br />F11 переставал работать, когда запускал реальный Snagit штатным методом.</p><br /><p>Проверил ваш код, а потом нашел нужное окно — ahk_class SnagIt9Editor.</p><p>И вот что удалось реализовать:</p><div class="codebox"><pre><code>#Requires AutoHotkey v2
#SingleInstance Force

SnagitWin := &quot;ahk_class SnagIt9Editor&quot;
global hidden := false

; Чистка стандартного меню AHK в трее
A_TrayMenu.Delete()

; F11 - toggle
F11::Toggle()

; Alt+F4 - скрыть вместо закрытия
!F4:: {
    global SnagitWin
    DetectHiddenWindows true

    if WinActive(SnagitWin) {
        if hwnd := WinExist(SnagitWin) {
            WinHide &quot;ahk_id &quot; hwnd
        }
        return
    }
}

Toggle() {
    global SnagitWin, hidden
    DetectHiddenWindows true

    hwnd := WinExist(SnagitWin)
    if !hwnd
        return

    if DllCall(&quot;IsWindowVisible&quot;, &quot;ptr&quot;, hwnd) {
        WinHide &quot;ahk_id &quot; hwnd
        hidden := true
    } else {
        WinShow &quot;ahk_id &quot; hwnd
        WinActivate &quot;ahk_id &quot; hwnd
        hidden := false
    }
}
</code></pre></div><br /><p>F11 — toggle видимости<br />Клик по крестику (Alt+F4) — и окно летит в трей. </p><br /><p>Нужен совет как реализовать обработку ЛКМ по иконке в трее — чтобы разворачивалось окно программы из трея.<br />Можно ли заменить стандартную зелёную иконку AHK в трее на свою?<br />Или есть ли другой нормальный способ сделать кнопку в трее для этого?</p><br /><p>Буду признателен за любые идеи и дельные советы.</p>]]></description>
			<author><![CDATA[null@example.com (leex)]]></author>
			<pubDate>Wed, 08 Apr 2026 22:03:19 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?pid=163317#p163317</guid>
		</item>
		<item>
			<title><![CDATA[Re: AHK v2: Можно ли кнопкой X сворачивать окно в трей, а не kill процесс?]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?pid=163316#p163316</link>
			<description><![CDATA[<div class="quotebox"><cite>leex пишет:</cite><blockquote><div class="codebox"><pre><code>Snagit := &quot;ahk_exe SnagitEditor.exe&quot;</code></pre></div></blockquote></div><p>А вы уверены, что у этого процесса только одно окно? Теоретически, их может быть несколько, и не все из них видимы. Лучше окно определять по заголовку, если всегда начинается одинаково или есть одинаковая часть, либо по классу (ahk_class).<br />Попробуйте запустить такой код и понажимать F11:<br /></p><div class="codebox"><pre><code>#Requires AutoHotkey v2

F11:: {
    DetectHiddenWindows true
    if !hWnd := WinExist(&#039;ahk_exe SnagitEditor.exe&#039;) {
        throw Error(&#039;Failed to get hwnd of SnagitEditor&#039;)
    }
    if DllCall(&#039;IsWindowVisible&#039;, &#039;Ptr&#039;, hWnd) {
        WinHide
    } else {
        WinShow
    }
}</code></pre></div><p>Что происходит?</p>]]></description>
			<author><![CDATA[null@example.com (teadrinker)]]></author>
			<pubDate>Wed, 08 Apr 2026 17:32:42 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?pid=163316#p163316</guid>
		</item>
		<item>
			<title><![CDATA[AHK v2: Можно ли кнопкой X сворачивать окно в трей, а не kill процесс?]]></title>
			<link>https://forum.script-coding.com/viewtopic.php?pid=163315#p163315</link>
			<description><![CDATA[<p>Здравствуйте.</p><p>Ищу способ реализовать поведение “сворачивание в трей при закрытии окна” для Snagit Editor 2021 (SnagitEditor.exe) на Windows 10.</p><p>Ситуация такая:</p><p>программа изначально не поддерживает трей<br />при закрытии (X / Alt+F4) окно всегда закрывается и завершает процесс<br />штатных настроек для “minimize/close to tray” нет</p><p>Также важно: нужно, чтобы при сворачивании в трей окно убиралось с панели задач, как у обычных tray-приложений.</p><p>Пробовал через AutoHotkey (v2):</p><p>WinHide / WinShow<br />перехват Alt+F4<br />попытки обработать закрытие окна</p><p>Но стабильного результата нет — поведение не работает, при закрытии окно всегда закрывается и завершает процесс.</p><p>Вопрос:</p><p>Можно ли вообще принудительно реализовать “tray behavior” для такого приложения?<br />Есть ли способ перехватить WM_CLOSE / системное закрытие окна через WinAPI или hooks, чтобы не давать приложению завершать процесс?<br />Или Snagit Editor в принципе нельзя заставить работать через трей из-за того, что он всегда завершает процесс при закрытии окна?</p><p>Интересуют рабочие методы (AHK / WinAPI / hooks), без сторонних tray-утилит.</p><div class="codebox"><pre><code>#SingleInstance Force
SetTitleMatchMode 2

Snagit := &quot;ahk_exe SnagitEditor.exe&quot;

; Alt+F4 → скрыть окно (в &quot;трей&quot;)
!F4:: {
    if WinExist(Snagit) {
        WinHide Snagit
        WinSetExStyle &quot;-0x80&quot;, Snagit
    }
}

; восстановить окно
^!r:: {
    if WinExist(Snagit) {
        WinShow Snagit
        WinSetExStyle &quot;+0x80&quot;, Snagit
        WinActivate Snagit
    }
}

; попытка перехвата закрытия
#HotIf WinActive(Snagit)
~!F4:: {
    WinHide Snagit
    WinSetExStyle &quot;-0x80&quot;, Snagit
}
#HotIf

; если окно пропало — контроль видимости
SetTimer CheckSnagit, 1000

CheckSnagit() {
    Snagit := &quot;ahk_exe SnagitEditor.exe&quot;

    if WinExist(Snagit) {
        if !DllCall(&quot;IsWindowVisible&quot;, &quot;ptr&quot;, WinExist(Snagit)) {
            WinShow Snagit
        }
    }
}</code></pre></div>]]></description>
			<author><![CDATA[null@example.com (leex)]]></author>
			<pubDate>Wed, 08 Apr 2026 01:38:44 +0000</pubDate>
			<guid>https://forum.script-coding.com/viewtopic.php?pid=163315#p163315</guid>
		</item>
	</channel>
</rss>
