1 (изменено: Androgen, 2007-03-19 12:05:11)

Тема: AHK. Отключение кнопки закрытия окна [X]

Открытые окна бывают разной важности-нужности. И иногда очень хочется сделать так, чтобы какое-нибудь мега-окно не было случайно закрыто нажатием на кнопку "закрыть" (обычно, такую красную, в правом-верхнем углу , типа "нажимай что хочешь, но только не трогай большую красную кнопку"). Сделать это нетрудно. Достаточно отключить этот вредный крестик. А поскольку кнопки окна являются этаким графическим отображением системного меню, то, стало быть, и нужно отключить соответствующий пункт этого меню.
Примечание: системное меню - это меню, которое появляется при клике левой мышью на значке в левом-верхнем углу, или при правом клике по заголовку она, или при нажатии Alt+Space.
И тогда окно уже нельзя будет закрыть нажатием на эту красную кнопку (хотя можно, нажав Alt+F4, или выбрать "Файл" -> "Выход").
Вот простейший пример (Блокнот-бессмертный):

WinGet, hWnd, ID, ahk_class Notepad ; получаем хэндл блокнота
hSysMenu := DllCall( "GetSystemMenu", "Uint", hWnd, "int", False ) ; получаем хэндл системного меню
DllCall( "RemoveMenu", "Uint", hSysMenu, "int", 6, "Uint", 0x400 ) ; удаляем 6-й пункт меню

Примечание: номера пунктов отсчитываются с 0, разделитель - тоже пункт меню, потому и удаляем 6-й пункт (называющийся "Закрыть" в русскоязычной винде).
Плохая новость: существуют программы, которые добавляют в системное меню (только себе или всем окнам) какие-нибудь свои пункты.
Хорошая новость: пункт "закрыть" всё равно самый нижний в меню (мне, во всяком случае, по другому не встречалось).
Учтём вышесказанное (Блокнот-бессмертный - универсал):

WinGet, hWnd, ID, ahk_class Notepad ; получаем хэндл блокнота
hSysMenu := DllCall( "GetSystemMenu", "Uint", hWnd, "int", False ) ; получаем хэндл системного меню
nIndex := DllCall( "GetMenuItemCount", "Uint", hSysMenu ) ; получаем количество пунктов в меню
DllCall( "RemoveMenu", "Uint", hSysMenu, "int", nIndex-1, "Uint", 0x400 ) ; удаляем самый нижний пункт меню 0x400=MF_BYPOSITION
DllCall( "RemoveMenu", "Uint", hSysMenu, "int", nIndex-2, "Uint", 0x400 ) ; удаляем разделитель (для красоты)
DllCall( "DrawMenuBar", "Uint", hSysMenu ) ; перерисовываем меню (работает и без этого, но MSDN говорит - надо)

Теперь пару вариантов использования.
Вариант первый. "Судьба тебя настигнет". Запускаем Блокнот, запускаем скрипт. Пока скрипт запущен - Блокнот не реагирует на кнопку [x]. Закрыли скрипт - Блокнот готов к убиению.

#Persistent ; скрипт будет запущен постоянно
OnExit, ReadyToDie
WinGet, hWnd, ID, ahk_class Notepad ; получаем хэндл блокнота
hSysMenu := DllCall( "GetSystemMenu", "Uint", hWnd, "int", False ) ; получаем хэндл системного меню
nIndex := DllCall( "GetMenuItemCount", "Uint", hSysMenu ) ; получаем количество пунктов в меню
DllCall( "RemoveMenu", "Uint", hSysMenu, "int", nIndex-1, "Uint", 0x400 ) ; удаляем самый нижний пункт меню 0x400=MF_BYPOSITION
DllCall( "RemoveMenu", "Uint", hSysMenu, "int", nIndex-2, "Uint", 0x400 ) ; удаляем разделитель (для красоты)
DllCall( "DrawMenuBar", "Uint", hSysMenu ) ; перерисовываем меню
Return

ReadyToDie: ; сюда перейдём при выходе из скрипта
    DllCall( "GetSystemMenu", "Uint", hWnd, "int", True ) ; восстанавливаем системное меню
    DllCall( "DrawMenuBar", "Uint", hSysMenu ) ; перерисовываем меню
ExitApp

Вариант второй. "Судьба тебя подкараулит". Теперь наоборот сначала запускаем скрипт, а затем блокнот. Эффект тот же.

#Persistent ; скрипт будет запущен постоянно
WinToLive = ahk_class Notepad ; кого ждем
OnExit, ReadyToDie
SetTimer, Auto_Window, 200 ; переходить к указанной подпрограмме через каждые 0.2 секунды
Auto_Window:
    WinGet, hWnd, ID, %WinToLive% ; получаем хэндл указанного окна
    If ( hWndPrevious = hWnd ) ; не удалять пункты из уже обработанного меню
        Return
    hSysMenu := DllCall( "GetSystemMenu", "Uint", hWnd, "int", False ) ; получаем хэндл системного меню
    nIndex := DllCall( "GetMenuItemCount", "Uint", hSysMenu ) ; получаем количество пунктов в меню
    DllCall( "RemoveMenu", "Uint", hSysMenu, "int", nIndex-1, "Uint", 0x400 ) ; удаляем самый нижний пункт меню 0x400=MF_BYPOSITION
    DllCall( "RemoveMenu", "Uint", hSysMenu, "int", nIndex-2, "Uint", 0x400 ) ; удаляем разделитель (для красоты)
    DllCall( "DrawMenuBar", "Uint", hSysMenu ) ; перерисовываем меню
    hWndPrevious := hWnd ; запоминаем предыдущий хэндл указанного окна
Return

ReadyToDie: ; сюда перейдём при выходе из скрипта
    DllCall( "GetSystemMenu", "Uint", hWnd, "int", True ) ; восстанавливаем системное меню
    DllCall( "DrawMenuBar", "Uint", hSysMenu ) ; перерисовываем меню
ExitApp

Понятно, что не учитывается ситуация, когда Блокнотов запущено больше одного. Но усложнять можно до бесконечности. Например, чтобы все Блокноты с 8.00 до 9.15 нельзя было закрыть той самой кнопкой. Или только если при этом запущен браузер. Или даже, страшно сказать, так можно поступать не только с блокнотами . Короче, не стану лишать вас увлекательного занятия придумать и сделать так, как нравится вам. Результатами, надеюсь, поделитесь.

Крокодил, крокожу и буду крокодить! (Твёрдое обещание нетрезвого кодера).

2

Re: AHK. Отключение кнопки закрытия окна [X]

Скрипт, защищающий некоторые программы от случайного закрытия. При нажатии на "крестик" программа не закрывается, а "сворачивается" скриптом (делается невидимой). При щелчке колесиком мыши на рабочем столе (или правой на иконке) скрипт выводит список свернутых таким образом окон в виде меню, из которого эти окна можно восстановить. Обрабатываются программы: Adobe Distiller, Adobe Acrobat, Adobe Photoshop, PageMaker, FAR, Mozilla (браузер). Кроме того, скрипт содержит дополнительное меню для вызова некоторых часто используемых программ и окон: MyComputer, MyDocuments, Internet Explorer, Mozilla, WinRAR, Свойства экрана, Панель управления, Корзина, Установка и удаление программ, SiSoftSandra. Список обрабатываемых программ и пунктов меню легко изменить/расширить.
Автор скрипта - Steve Key.

;#NoTrayIcon ;я думаю, иконка не нужна, если меню вызывается и так...
;если не нужно - раскомментируйте предыдущую строчку

;эта установка убирает "системные" пункты меню иконки скрипта в трее;
;если эти пункты нужны, задайте "Y" вместо "N"
mwt_StandardMenu = n

;если "Моё меню" не нужно, поставьте "N"
mwt_MyMenu = y

;ширина и высота крестика в верхнем правом углу окон, подобрать по факту
; 33 - это экспериментально подобранный размер крестика у меня на компе,
; я люблю настраивать размеры кнопок, системного шрифта и лифтов побольше,
; чтобы "не целиться" в них мышкой, обычно же 29-30 хватает...
WKrest = 33
HKrest = 33

;Моё меню
if mwt_StandardMenu = Y
    Menu, Tray, Add ;разделитель
if mwt_MyMenu = Y
{
 Menu, Tray, Add, Диски, MyComputer
 Menu, Tray, Add, Документы, MyDocuments
 Menu, Tray, Add, Internet Explorer, IE
 Menu, Tray, Add, Браузер, Mozilla
 Menu, Tray, Add, Архиватор, WinRAR
 Menu, Tray, Add, Свойства экрана, Screen
 Menu, Tray, Add, Панель управления, Panel
 Menu, Tray, Add, Корзина, Bin
 Menu, Tray, Add, Удаление программ, DelProgs
 Menu, Tray, Add, SiSoftSandra, Sandra
;заготовка для вложенного меню
;Menu, Folders, Add, Корзина, Bin
;Menu, Tray, Add, Папки, :Folders

    Menu, Tray, Add    ;разделитель меню от скрытых программ, можно два поставить...
}
;kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk конец определения моего меню

;установка иконки скрипта в трее
;я думаю, она нахрен не нужна, если меню вызывается и так...
Menu, Tray, Icon, %A_WinDir%\system32\Shell32.dll, 91

;максимальное количество окон, которое может быть скрыто
mwt_MaxWindows = 10
;горячая клавиша, скрывающая текущее окно
;;;;;;;;;;;;;;;;mwt_Hotkey = #h ;Win+H убрано, запускается по-другому - Steve Key
;горячая клавиша, восстанавливающая все скрытые окна
mwt_HotkeyRestoreAll = ^#MButton ;Ctrl+Win+Колесико мыши
;может быть запущено не более одного экземпляра сценария
#SingleInstance
;работать со скрытыми окнами
DetectHiddenWindows, On
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;назначение горячей клавиши, восстанавливающей все скрытые окна
Hotkey, %mwt_HotkeyRestoreAll%, mwt_RestoreAll
;при завершении сценария необходимо отобразить все скрытые окна, если таковые есть
OnExit, mwt_RestoreAllThenExit
;настройка меню иконки скрипта в трее
if mwt_StandardMenu = n
    Menu, Tray, NoStandard
;добавляются пункты меню для восстановления и выхода
   Menu, Tray, Add, Восстановить все, mwt_RestoreAll
   Menu, Tray, Add, Выход, mwt_RestoreAllThenExit
    Menu, Tray, Add
;ограничение ширины меню (в символах)
    mwt_MaxLength = 25
return   ;конец секции авто-выполнения скрипта

;kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk Начало моей проги
$LButton::   ;левая кнопка мыши...
      Click Down   ;Нажимаем кнопку мыши, чтобы сделать окно текущим, и всё...
return

$LButton Up::   ;все действия делаем, как и принято в Windows, при отжатии кнопки
;...если отжата на крестике закрытия окна (в правом верхнем углу)
    CoordMode, Mouse, Screen
    MouseGetPos,xpos,ypos   ;получаем координаты мыши...
    WinGetClass, class, A   ;все по возможности, обращается к текущему окну...
    WinGetPos, winX, winY, Width,, A

    if(xpos>=winX+Width-WKrest AND xpos<=winX+Width AND ypos<=winY+HKrest)
    {
    WinGetActiveTitle, Win_To_Close_Title   ;сохранить заголовок в переменной
    WinGetClass, Win_To_Close_Class, A   ;получить класс активного окна
                   ; список классов закрываемых окон через |
     Win_To_Close = Distiller|AdobeAcrobat|Photoshop|VWClass500|ConsoleWindowClass|MozillaWindowClass
                   ;ну, эти окна понятно, что значат|PageMaker |FAR               |Mozilla (браузер)
;класс окна можно посмотреть Windows Spy, вызвав его из стандартного меню AHK (если таковое включить)
     Loop, parse, Win_To_Close, |   ;обработать список окон (разделитель - "|")
     {
        If Win_To_Close_Class = %A_LoopField%   ;если текущее окно - из списка, то...
        {
          gosub mwt_Minimize
        }
     }
    }
     Click Up   ;отпустить кнопку мыши нужно в любом случае, и она не сработает,
return          ;если окно поменяли или убрали, что нам и нужно...
;kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk Конец моей проги

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;подпрограмма, скрывающая текущее окно
mwt_Minimize:
if mwt_WindowCount >= %mwt_MaxWindows%
{
    MsgBox Не может быть скрыто одновременно больше, чем %mwt_MaxWindows% окон.
    return
}
;получаем активное окно и используем его как "последнее найденное окно",
;т.е. окно по умолчанию для всех дальнейших вызовов функций, связанных с окнами
WinWait, A,, 2
if ErrorLevel <> 0
    return
;получаем информацию об активном окне (последнем найденном?..)
WinGet, mwt_ActiveID, ID   ;хэндл (HWND) окна
WinGetTitle, mwt_ActiveTitle   ;заголовок окна
WinGetClass, mwt_ActiveClass   ;имя класса окна
WinGet, mwt_ActivePID, PID   ;идентификатор процесса, которому принадлежит окно
WinGet, mwt_List, List, ahk_pid %mwt_ActivePID%   ;список всех окон этого процесса
WinGet, mwt_ActiveProcess, ProcessName   ;имя процесса, которому принадлежит окно
;рабочий стол и панель задач скрывать нельзя
if mwt_ActiveClass in Shell_TrayWnd,Progman
    return
;поскольку скрытие окна не будет деактивировать его, переключимся
;в другое окно (как будто пользователь нажал Alt+Escape)
Send, !{esc}
;собственно скрываем нужные окна процесса (делаем их невидимыми),
;скрытию подлежат все окна текущего процесса со стилем WS_VISIBLE,
;mwt_WindowsIDs - список хэндлов скрываемых окон,
;разделённых символом | (вертикальная черта)
mwt_WindowsIDs = ""
;проводник - особый случай, скрывать можно только текущее окно процесса
If mwt_ActiveProcess = explorer.exe
{
    WinHide, ahk_id %mwt_ActiveID%
    mwt_WindowsIDs = %mwt_WindowsIDs%|%mwt_ActiveID%
}
;скрываем все видимые окна процесса
Else
{
    Loop, %mwt_List%
    {
        mwt_id := mwt_List%A_Index%
        WinGet, style, Style, ahk_id %mwt_id%
        if ( style & 0x10000000 ) ;WS_VISIBLE
        {
            WinHide, ahk_id %mwt_id%
            ;Result := DllCall("ShowWindow", "UInt", mwt_id, "Int", "0")
            mwt_WindowsIDs = %mwt_WindowsIDs%|%mwt_id%
        }
    }
}
;если заголовок окна пуст, используем имя класса,
;т.к. нам нужен непустой заголовок пункта меню
if mwt_ActiveTitle =
    mwt_ActiveTitle = ahk_class %mwt_ActiveClass%
;обрезаем заголовок окна до максимальной длины mwt_MaxLength
StringLeft, mwt_ActiveTitle, mwt_ActiveTitle, %mwt_MaxLength%
;обеспечение уникальности заголовка пункта меню:
Loop, %mwt_MaxWindows%
{
    if mwt_WindowTitle%a_index% = %mwt_ActiveTitle%
    {
          ;Нашли такой же заголовок, так что заголовок не уникален.
        StringTrimLeft, mwt_ActiveIDShort, mwt_ActiveID, 2
        StringLen, mwt_ActiveIDShortLength, mwt_ActiveIDShort
        StringLen, mwt_ActiveTitleLength, mwt_ActiveTitle
        mwt_ActiveTitleLength += %mwt_ActiveIDShortLength%
        mwt_ActiveTitleLength += 1
        if mwt_ActiveTitleLength > %mwt_MaxLength%
        {
            TrimCount = %mwt_ActiveTitleLength%
            TrimCount -= %mwt_MaxLength%
            StringTrimRight, mwt_ActiveTitle, mwt_ActiveTitle, %TrimCount%
        }
        ;создаём уникальный заголовок:
        mwt_ActiveTitle = %mwt_ActiveTitle% %mwt_ActiveIDShort%
        break
    }
}
;на всякий случай проверим, нет ли этого окна уже в списке ранее скрытых
mwt_AlreadyExists = n
Loop, %mwt_MaxWindows%
{
    if mwt_WindowID%a_index% = %mwt_ActiveID%
    {
        mwt_AlreadyExists = y
        break
    }
}
;добавим окно в массив и в меню
if mwt_AlreadyExists = n
{
    Menu, Tray, add, %mwt_ActiveTitle%, RestoreFromTrayMenu
    mwt_WindowCount += 1
    Loop, %mwt_MaxWindows%   ;поиск свободного элемента в массиве
    {
        if mwt_WindowID%a_index% =   ;элемент свободен
        {
            mwt_WindowID%a_index% = %mwt_ActiveID%
            mwt_WindowTitle%a_index% = %mwt_ActiveTitle%
            mwt_WindowPID%a_index% = %mwt_ActivePID%
            mwt_WinIDs%a_index% = %mwt_WindowsIDs%
            break
        }
    }
}
return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;отсюда начинается исполнение, когда вызвана команда меню иконки скрипта в трее,
;соответствующая конкретному скрытому окну
RestoreFromTrayMenu:
Menu, Tray, delete, %A_ThisMenuItem%
;поиск окна по его уникальному заголовку, сохраненному как название пункта меню
Loop, %mwt_MaxWindows%
{
    if mwt_WindowTitle%a_index% = %A_ThisMenuItem%
    {
        StringTrimRight, IDToRestore, mwt_WindowID%a_index%, 0
        StringTrimRight, PIDToRestore, mwt_WindowPID%a_index%, 0
        mwt_WindowsIDs := mwt_WinIDs%a_index%
        Loop, parse, mwt_WindowsIDs, |
            WinShow, ahk_id %A_LoopField%
        WinActivate ahk_id %IDToRestore%
        mwt_WindowID%a_index% =   ;очищаем элементы массивов
        mwt_WindowTitle%a_index% =
        mwt_WindowPID%a_index% =
        mwt_WinIDs%a_index% =
        mwt_WindowCount -= 1
        break
    }
}
return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;отсюда начинается исполнение, когда вызвана команда меню скрипта
;"Выход и восстановление всех скрытых окон"
mwt_RestoreAllThenExit:
Gosub, mwt_RestoreAll
ExitApp   ;завершение работы скрипта
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;отсюда начинается исполнение, когда вызвана команда меню скрипта
;"Восстановление всех скрытых окон"
mwt_RestoreAll:
Loop, %mwt_MaxWindows%
{
    if mwt_WindowID%a_index% <>
    {
        StringTrimRight, IDToRestore, mwt_WindowID%a_index%, 0
        StringTrimRight, PIDToRestore, mwt_WindowPID%a_index%, 0
        mwt_WindowsIDs := mwt_WinIDs%a_index%
        Loop, parse, mwt_WindowsIDs, |
            WinShow, ahk_id %A_LoopField%
        WinActivate ahk_id %IDToRestore%
        StringTrimRight, MenuToRemove, mwt_WindowTitle%a_index%, 0
        Menu, Tray, delete, %MenuToRemove%
        mwt_WindowID%a_index% =
        mwt_WindowTitle%a_index% =
        mwt_WindowPID%a_index% =
        mwt_WinIDs%a_index% =
        mwt_WindowCount -= 1
    }
    if mwt_WindowCount = 0
        break
}
return
;kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
;Дальше идет моя прога: Steve Key
MButton::    ;нажатие на колесико мыши на рабочем столе показывает контекстное меню
             ;такое же, как у иконки скрипта...
MouseGetPos, x, y, win
WinGetClass, class, ahk_id %win%
IfEqual, class, Progman
{
 Menu, Tray, Show
}
Else
{
  send {MButton}
}
return

;собственно, мое меню
MyDocuments:
Run ::{450d8fba-ad25-11d0-98a8-0800361b1103}   ;Мои документы
return

WinRAR:
Run, WinRAR.exe   ;если WinRAR установлена, то вызовется
return

Mozilla:
Run, mozilla.exe   ;если Mozilla установлен, то вызовется
Return

IE:
Run, about:blank   ;вызов Интернет Эксплорера
return

MyComputer:
Run ::{20d04fe0-3aea-1069-a2d8-08002b30309d}   ;Мой компьютер
Return

Bin:
Run ::{645ff040-5081-101b-9f08-00aa002f954e}   ;Корзина
Return

Screen:
Run, desk.cpl   ;Свойства Экрана
return

DelProgs:
Run, appwiz.cpl   ;Установка и удаление программ
return

Sandra:
Run, SanCpl.cpl   ;если Сандра установлена, то вызовется
return

Panel:
Run rundll32.exe shell32.dll`,Control_RunDLL   ;Панель управления
return
;Конец действий моего меню колесиком мыши на рабочем столе!
Предложения в русском языке начинаются с большой буквы и заканчиваются точкой.
В названии ветки всегда должен быть указан язык программирования или среда исполнения скрипта, если это возможно.

3

Re: AHK. Отключение кнопки закрытия окна [X]

Новая улучшенная версия скрипта из предыдущего поста от Steve Key.

;#NoTrayIcon ;я думаю, иконка не нужна, если меню вызывается и так...
;если не нужно - раскомментируйте предыдущую строчку

;эта установка убирает "системные" пункты меню иконки скрипта в трее;
;если эти пункты нужны, задайте "Y" вместо "N"
mwt_StandardMenu = n

;если "Моё меню" не нужно, поставьте "N"
mwt_MyMenu = y

;Моё меню
if mwt_StandardMenu = Y
    Menu, Tray, Add ;разделитель
if mwt_MyMenu = Y
{
 Menu, Tray, Add, Диски, MyComputer
 Menu, Tray, Add, Документы, MyDocuments
 Menu, Tray, Add, Internet Explorer, IE
 ;;Menu, Tray, Add, Браузер, Mozilla
 ;;Menu, Tray, Add, Архиватор, WinRAR
 Menu, Tray, Add, Свойства экрана, Screen
 Menu, Tray, Add, Панель управления, Panel
 Menu, Tray, Add, Корзина, Bin
 Menu, Tray, Add, Удаление программ, DelProgs
 ;;Menu, Tray, Add, SiSoftSandra, Sandra
;проги, которые есть не у всех, закомментировал значком ;;

;заготовка для вложенного меню
;Menu, Folders, Add, Корзина, Bin
;Menu, Tray, Add, Папки, :Folders

    Menu, Tray, Add    ;разделитель меню от скрытых программ, можно два поставить...
}
;kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk конец определения моего меню

;установка иконки скрипта в трее
;я думаю, она нахрен не нужна, если меню вызывается и так...
Menu, Tray, Icon, %A_WinDir%\system32\Shell32.dll, 91

;максимальное количество окон, которое может быть скрыто
mwt_MaxWindows = 10
;горячая клавиша, скрывающая текущее окно
;;;;;;;;;;;;;;;;mwt_Hotkey = #h ;Win+H убрано, запускается по-другому - Steve Key
;горячая клавиша, восстанавливающая все скрытые окна
mwt_HotkeyRestoreAll = ^#MButton ;Ctrl+Win+Колесико мыши
;может быть запущено не более одного экземпляра сценария
#SingleInstance
;работать со скрытыми окнами
DetectHiddenWindows, On
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;назначение горячей клавиши, восстанавливающей все скрытые окна
Hotkey, %mwt_HotkeyRestoreAll%, mwt_RestoreAll
;при завершении сценария необходимо отобразить все скрытые окна, если таковые есть
OnExit, mwt_RestoreAllThenExit
;настройка меню иконки скрипта в трее
if mwt_StandardMenu = n
    Menu, Tray, NoStandard
;добавляются пункты меню для восстановления и выхода
   Menu, Tray, Add, Восстановить все, mwt_RestoreAll
   Menu, Tray, Add, Выход, mwt_RestoreAllThenExit
    Menu, Tray, Add
;ограничение ширины меню (в символах)
    mwt_MaxLength = 25
return   ;конец секции авто-выполнения скрипта

;kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk Начало моей проги
$LButton::   ;левая кнопка мыши...
      Click Down   ;Нажимаем кнопку мыши, чтобы сделать окно текущим, и всё...
return

$LButton Up::   ;все действия делаем, как и принято в Windows, при отжатии кнопки
  CoordMode, Mouse, Screen
  MouseGetPos, mX, mY, Hwnd
  SendMessage, 0x84,, (mX & 0xFFFF)|(mY & 0xFFFF)<<16,, ahk_id %Hwnd%
;...если отжата на крестике закрытия окна (в правом верхнем углу)
  If ErrorLevel=20                               ; HTCLOSE
    {
    WinGetActiveTitle, Win_To_Close_Title   ;сохранить заголовок в переменной
    WinGetClass, Win_To_Close_Class, A   ;получить класс активного окна
                   ; список классов закрываемых окон через |
     Win_To_Close = Distiller|AdobeAcrobat|Photoshop|VWClass500|ConsoleWindowClass|MozillaWindowClass
                   ;ну, эти окна понятно, что значат|PageMaker |FAR               |Mozilla (браузер)
;класс окна можно посмотреть Windows Spy, вызвав его из стандартного меню AHK (если таковое включить)
     Loop, parse, Win_To_Close, |   ;обработать список окон (разделитель - "|")
     {
        If Win_To_Close_Class = %A_LoopField%   ;если текущее окно - из списка, то...
        {
          gosub mwt_Minimize
        }
     }
    }
     Click Up   ;отпустить кнопку мыши нужно в любом случае, но она не сработает,
return          ;если окно поменяли или убрали, что нам и нужно...
;kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk Конец моей проги

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;подпрограмма, скрывающая текущее окно
mwt_Minimize:
if mwt_WindowCount >= %mwt_MaxWindows%
{
    MsgBox Не может быть скрыто одновременно больше, чем %mwt_MaxWindows% окон.
    return
}
;получаем активное окно и используем его как "последнее найденное окно",
;т.е. окно по умолчанию для всех дальнейших вызовов функций, связанных с окнами
WinWait, A,, 2
if ErrorLevel <> 0
    return
;получаем информацию об активном окне (последнем найденном?..)
WinGet, mwt_ActiveID, ID   ;хэндл (HWND) окна
WinGetTitle, mwt_ActiveTitle   ;заголовок окна
WinGetClass, mwt_ActiveClass   ;имя класса окна
WinGet, mwt_ActivePID, PID   ;идентификатор процесса, которому принадлежит окно
WinGet, mwt_List, List, ahk_pid %mwt_ActivePID%   ;список всех окон этого процесса
WinGet, mwt_ActiveProcess, ProcessName   ;имя процесса, которому принадлежит окно
;рабочий стол и панель задач скрывать нельзя
if mwt_ActiveClass in Shell_TrayWnd,Progman
    return
;поскольку скрытие окна не будет деактивировать его, переключимся
;в другое окно (как будто пользователь нажал Alt+Escape)
Send, !{esc}
;собственно скрываем нужные окна процесса (делаем их невидимыми),
;скрытию подлежат все окна текущего процесса со стилем WS_VISIBLE,
;mwt_WindowsIDs - список хэндлов скрываемых окон,
;разделённых символом | (вертикальная черта)
mwt_WindowsIDs = ""
;проводник - особый случай, скрывать можно только текущее окно процесса
If mwt_ActiveProcess = explorer.exe
{
    WinHide, ahk_id %mwt_ActiveID%
    mwt_WindowsIDs = %mwt_WindowsIDs%|%mwt_ActiveID%
}
;скрываем все видимые окна процесса
Else
{
    Loop, %mwt_List%
    {
        mwt_id := mwt_List%A_Index%
        WinGet, style, Style, ahk_id %mwt_id%
        if ( style & 0x10000000 ) ;WS_VISIBLE
        {
            WinHide, ahk_id %mwt_id%
            ;Result := DllCall("ShowWindow", "UInt", mwt_id, "Int", "0")
            mwt_WindowsIDs = %mwt_WindowsIDs%|%mwt_id%
        }
    }
}
;если заголовок окна пуст, используем имя класса,
;т.к. нам нужен непустой заголовок пункта меню
if mwt_ActiveTitle =
    mwt_ActiveTitle = ahk_class %mwt_ActiveClass%
;обрезаем заголовок окна до максимальной длины mwt_MaxLength
StringLeft, mwt_ActiveTitle, mwt_ActiveTitle, %mwt_MaxLength%
;обеспечение уникальности заголовка пункта меню:
Loop, %mwt_MaxWindows%
{
    if mwt_WindowTitle%a_index% = %mwt_ActiveTitle%
    {
          ;Нашли такой же заголовок, так что заголовок не уникален.
        StringTrimLeft, mwt_ActiveIDShort, mwt_ActiveID, 2
        StringLen, mwt_ActiveIDShortLength, mwt_ActiveIDShort
        StringLen, mwt_ActiveTitleLength, mwt_ActiveTitle
        mwt_ActiveTitleLength += %mwt_ActiveIDShortLength%
        mwt_ActiveTitleLength += 1
        if mwt_ActiveTitleLength > %mwt_MaxLength%
        {
            TrimCount = %mwt_ActiveTitleLength%
            TrimCount -= %mwt_MaxLength%
            StringTrimRight, mwt_ActiveTitle, mwt_ActiveTitle, %TrimCount%
        }
        ;создаём уникальный заголовок:
        mwt_ActiveTitle = %mwt_ActiveTitle% %mwt_ActiveIDShort%
        break
    }
}
;на всякий случай проверим, нет ли этого окна уже в списке ранее скрытых
mwt_AlreadyExists = n
Loop, %mwt_MaxWindows%
{
    if mwt_WindowID%a_index% = %mwt_ActiveID%
    {
        mwt_AlreadyExists = y
        break
    }
}
;добавим окно в массив и в меню
if mwt_AlreadyExists = n
{
    Menu, Tray, add, %mwt_ActiveTitle%, RestoreFromTrayMenu
    mwt_WindowCount += 1
    Loop, %mwt_MaxWindows%   ;поиск свободного элемента в массиве
    {
        if mwt_WindowID%a_index% =   ;элемент свободен
        {
            mwt_WindowID%a_index% = %mwt_ActiveID%
            mwt_WindowTitle%a_index% = %mwt_ActiveTitle%
            mwt_WindowPID%a_index% = %mwt_ActivePID%
            mwt_WinIDs%a_index% = %mwt_WindowsIDs%
            break
        }
    }
}
return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;отсюда начинается исполнение, когда вызвана команда меню иконки скрипта в трее,
;соответствующая конкретному скрытому окну
RestoreFromTrayMenu:
Menu, Tray, delete, %A_ThisMenuItem%
;поиск окна по его уникальному заголовку, сохраненному как название пункта меню
Loop, %mwt_MaxWindows%
{
    if mwt_WindowTitle%a_index% = %A_ThisMenuItem%
    {
        StringTrimRight, IDToRestore, mwt_WindowID%a_index%, 0
        StringTrimRight, PIDToRestore, mwt_WindowPID%a_index%, 0
        mwt_WindowsIDs := mwt_WinIDs%a_index%
        Loop, parse, mwt_WindowsIDs, |
            WinShow, ahk_id %A_LoopField%
        WinActivate ahk_id %IDToRestore%
        mwt_WindowID%a_index% =   ;очищаем элементы массивов
        mwt_WindowTitle%a_index% =
        mwt_WindowPID%a_index% =
        mwt_WinIDs%a_index% =
        mwt_WindowCount -= 1
        break
    }
}
return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;отсюда начинается исполнение, когда вызвана команда меню скрипта
;"Выход и восстановление всех скрытых окон"
mwt_RestoreAllThenExit:
Gosub, mwt_RestoreAll
ExitApp   ;завершение работы скрипта
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;отсюда начинается исполнение, когда вызвана команда меню скрипта
;"Восстановление всех скрытых окон"
mwt_RestoreAll:
Loop, %mwt_MaxWindows%
{
    if mwt_WindowID%a_index% <>
    {
        StringTrimRight, IDToRestore, mwt_WindowID%a_index%, 0
        StringTrimRight, PIDToRestore, mwt_WindowPID%a_index%, 0
        mwt_WindowsIDs := mwt_WinIDs%a_index%
        Loop, parse, mwt_WindowsIDs, |
            WinShow, ahk_id %A_LoopField%
        WinActivate ahk_id %IDToRestore%
        StringTrimRight, MenuToRemove, mwt_WindowTitle%a_index%, 0
        Menu, Tray, delete, %MenuToRemove%
        mwt_WindowID%a_index% =
        mwt_WindowTitle%a_index% =
        mwt_WindowPID%a_index% =
        mwt_WinIDs%a_index% =
        mwt_WindowCount -= 1
    }
    if mwt_WindowCount = 0
        break
}
return
;kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
;Дальше идет моя прога: Steve Key
MButton::    ;нажатие на колесико мыши на рабочем столе показывает контекстное меню
             ;такое же, как у иконки скрипта...
MouseGetPos, x, y, win
WinGetClass, class, ahk_id %win%
IfEqual, class, Progman
{
 Menu, Tray, Show
}
Else
{
  send {MButton}
}
return

;собственно, мое меню
MyDocuments:
Run ::{450d8fba-ad25-11d0-98a8-0800361b1103}   ;Мои документы
return

WinRAR:
Run, WinRAR.exe   ;если WinRAR установлена, то вызовется
return

Mozilla:
Run, mozilla.exe   ;если Mozilla установлен, то вызовется
Return

IE:
Run, about:blank   ;вызов Интернет Эксплорера
return

MyComputer:
Run ::{20d04fe0-3aea-1069-a2d8-08002b30309d}   ;Мой компьютер
Return

Bin:
Run ::{645ff040-5081-101b-9f08-00aa002f954e}   ;Корзина
Return

Screen:
Run, desk.cpl   ;Свойства Экрана
return

DelProgs:
Run, appwiz.cpl   ;Установка и удаление программ
return

Sandra:
Run, SanCpl.cpl   ;если Сандра установлена, то вызовется
return

Panel:
Run rundll32.exe shell32.dll`,Control_RunDLL   ;Панель управления
return
;Конец действий моего меню колесиком мыши на рабочем столе!

Замеченный глючок: "глушит" двойной щелчек мыши в Word'е для выделения слова.

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

4 (изменено: Steve Key, 2008-03-05 10:50:10)

Re: AHK. Отключение кнопки закрытия окна [X]

Вариант отключения "красной кнопки" (крестика закрытия окна):

Некоторые проги, которые у меня на работе закрывать не нужно, собственно, запускаются кнопками (посредством KeyMan'а), или иконками на панели быстрого запуска.
А ранее разработанные скрипты для отключения "крестика" "тормозят" и глушат двойной щелчек в Word, в частности...

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

Вот что получилось для FAR:

run C:\PROGRAMS\AHK\FAR.lnk
winwait ahk_class ConsoleWindowClass
WinToLive = ahk_class ConsoleWindowClass ; FAR

    WinGet, hWnd, ID, %WinToLive% ; получаем хэндл указанного окна
    hSysMenu := DllCall( "GetSystemMenu", "Uint", hWnd, "int", False ) ; получаем хэндл системного меню
    nIndex := DllCall( "GetMenuItemCount", "Uint", hSysMenu ) ; получаем количество пунктов в меню
    DllCall( "RemoveMenu", "Uint", hSysMenu, "int", nIndex-1, "Uint", 0x400 ) ; удаляем самый нижний пункт меню 0x400=MF_BYPOSITION
    DllCall( "RemoveMenu", "Uint", hSysMenu, "int", nIndex-2, "Uint", 0x400 ) ; удаляем остальные пункты меню FAR
    DllCall( "RemoveMenu", "Uint", hSysMenu, "int", nIndex-3, "Uint", 0x400 ) ;
    DllCall( "RemoveMenu", "Uint", hSysMenu, "int", nIndex-4, "Uint", 0x400 ) ;
    DllCall( "RemoveMenu", "Uint", hSysMenu, "int", nIndex-5, "Uint", 0x400 ) ; удаляем разделитель (для красоты)
    DllCall( "DrawMenuBar", "Uint", hSysMenu ) ; перерисовываем меню
Return

Вот для Adobe Distiller (ну вы поняли общий принцип - нужно вписАть название класса окна, которое можно глянуть в WinSpy):

Код:

run C:\PROGRAMS\AHK\Acrobat Distiller.lnk
winwait ahk_class Distiller
WinToLive = ahk_class Distiller

    WinGet, hWnd, ID, %WinToLive% ; получаем хэндл указанного окна
    hSysMenu := DllCall( "GetSystemMenu", "Uint", hWnd, "int", False ) ; получаем хэндл системного меню
    nIndex := DllCall( "GetMenuItemCount", "Uint", hSysMenu ) ; получаем количество пунктов в меню
    DllCall( "RemoveMenu", "Uint", hSysMenu, "int", nIndex-1, "Uint", 0x400 ) ; удаляем самый нижний пункт меню 0x400=MF_BYPOSITION
    DllCall( "RemoveMenu", "Uint", hSysMenu, "int", nIndex-2, "Uint", 0x400 ) ; удаляем разделитель (для красоты)
    DllCall( "DrawMenuBar", "Uint", hSysMenu ) ; перерисовываем меню
Return

5

Re: AHK. Отключение кнопки закрытия окна [X]

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

LButton::
  CoordMode, Mouse, Screen
  MouseGetPos, mX, mY, Hwnd
  SendMessage, 0x84,, (mX & 0xFFFF)|(mY & 0xFFFF)<<16,, ahk_id %Hwnd%
  If ErrorLevel=20                               ; HTCLOSE
   WinMinimize A
  else
   Click Down
Return

LButton Up:: Click Up

Срабатывает даже на кнопках, сделанных "серыми" предыдущим скриптом.

6

Re: AHK. Отключение кнопки закрытия окна [X]

Еще по отключению крестика в избранных приложениях путем запуска их через скрипт...
Если запустить скрипт повторно, то удаляется еще 2-3-4 пункта меню... Становится невозможным разворачивание-сворачивание и т. п...:(
Скрипт нужно модифицировать:

if WinExist("Acrobat Distiller")
 {
   WinActivate ahk_class Distiller
   exitapp
 }

run C:\PROGRAMS\AHK\Acrobat Distiller.lnk
winwait ahk_class Distiller
WinToLive = ahk_class Distiller

    WinGet, hWnd, ID, %WinToLive% ; получаем хэндл указанного окна
    hSysMenu := DllCall( "GetSystemMenu", "Uint", hWnd, "int", False ) ; получаем хэндл системного меню
    nIndex := DllCall( "GetMenuItemCount", "Uint", hSysMenu ) ; получаем количество пунктов в меню
    DllCall( "RemoveMenu", "Uint", hSysMenu, "int", nIndex-1, "Uint", 0x400 ) ; удаляем самый нижний пункт меню 0x400=MF_BYPOSITION
    DllCall( "RemoveMenu", "Uint", hSysMenu, "int", nIndex-2, "Uint", 0x400 ) ; удаляем разделитель (для красоты)
    DllCall( "DrawMenuBar", "Uint", hSysMenu ) ; перерисовываем меню
Return

Добавлено определение, не запущено ли уже это приложение, и если да, то передать ему фокус...:):):)