В одной из прошлых статей мы уже научились обрабатывать и облагораживать
окно выбора каталога, а теперь сделаем что-нибудь необычное и противоестественное с окном сообщения MessageBox. Первое, что приходит на ум - это описать аналог MessageBox в ресурсах или создавать его динамически по мере надобности, но это получается как-то слишком просто. Попробуем перехватить стандартное окно MessageBox в момент создания. Для этого поставим хук на текущий тред нашего процесса, хук должен иметь тип WH_CBT:
...
; Получить хэндл текущего потока
invoke GetCurrentThreadId
; Установить перехватчик событий
invoke SetWindowsHookEx,WH_CBT,MBHookProc,NULL,eax
; Сохранить хэндл хука
mov [hMBHook],eax
...
Теперь мы можем отследить и обработать момент, когда система захочет активировать любое окно, принадлежащее треду. Значит если сразу после установки этого хука вызвать функцию MessageBox, то перехватчик также получит управление и мы сможем получить хэндл окна создаваемого MessageBox. В сегменте данных определим нужные переменные, структуры и строки. Их назначение будет раскрыто дальше.
section '.date' data readable writeable
; Заголовок и текст окна
szTitle db 'Hello!',0
szMess db 'This is a Customized MessageBox with long text!',13,10
db 'FASM is the BEST Assembler!',0
; Новый текст на кнопках
szButt1 db 'Of Course ;)',0
szButt2 db 'No-no-no!!!',0
; Название класса окна
cname db 'EDIT',0
hMBHook dd ? ; Хэндл хука
pOldProc dd ? ; Адрес старого обработчика окна
hInstance dd ? ; Хэндл модуля
hHeap dd ? ; Хэндл кучи
hMemory dd ? ; Указатель на память
coord RECT ; Размеры текста
Зная хэндл, с окном можно сделать практически все что угодно. Здесь есть важный момент: после обработки окна надо обязательно сразу же снять хук, чтобы не было никаких накладок.
Функция перехватчика будет иметь следующий вид:
;-----------------------------------------------------------------------
; Функция обработки перехватчика
;-----------------------------------------------------------------------
proc MBHookProc nCode:DWORD,wParam:DWORD,lParam:DWORD
; Сохранить все регистры
pusha
cmp [nCode],0
jb @f
cmp [nCode],HCBT_ACTIVATE
jne @f
; Установить иконку окна
invoke GetModuleHandle,0
invoke LoadIcon,eax,1
invoke SendMessage,[wParam],WM_SETICON,ICON_BIG,eax
; Поменять текст на кнопке Yes
invoke GetDlgItem,[wParam],IDYES
invoke SetWindowText,eax,szButt1
; Поменять текст на кнопке No
invoke GetDlgItem,[wParam],IDNO
invoke SetWindowText,eax,szButt2
; Изменить стиль окна
invoke GetWindowLong,[wParam],GWL_EXSTYLE
add eax,WS_EX_LAYERED
invoke SetWindowLong,[wParam],GWL_EXSTYLE,eax
; Установить атрибут прозрачности
invoke SetLayeredWindowAttributes,[wParam],0,200,LWA_ALPHA
; Получить хэндл текстового поля
invoke GetDlgItem,[wParam],0FFFFh
; Сохранить хэндл текстового поля
mov ebx,eax
; Получить длину текста в окне
invoke GetWindowTextLength,ebx
or eax,eax
jz no_text
; Получить координаты текстового блока
invoke GetClientRect,ebx,coord
; Пересчитать координаты на дочернее окно
invoke MapWindowPoints,ebx,[wParam],coord,2
mov eax,[coord.left]
sub [coord.right],eax
add [coord.right],5
sub [coord.left],7
mov eax,[coord.top]
sub [coord.bottom],eax
add [coord.top],2
; Получить кучу текущего процесса
invoke GetProcessHeap
mov [hHeap],eax
mov esi,eax
; Выделить память для текста
invoke HeapAlloc,[hHeap],HEAP_ZERO_MEMORY,eax
mov [hMemory],eax
; Прочитать текст в буфер
invoke GetWindowText,ebx,[hMemory],esi
; Спрятать основной текст
invoke ShowWindow,ebx,SW_HIDE
; Получить шрифт текста в окне
invoke SendMessage,ebx,WM_GETFONT,NULL,NULL
mov esi,eax
; Создать поле EDIT вместо STATIC
invoke CreateWindowEx,WS_EX_WINDOWEDGE,cname,[hMemory],\
WS_CHILD+WS_VISIBLE+ES_MULTILINE+ES_READONLY,[coord.left],\
[coord.top],[coord.right],[coord.bottom],[wParam],0FFFFh,\
[hInstance],NULL
; Установить шрифт как было у исходного текста
invoke SendMessage,eax,WM_SETFONT,esi,NULL
; Очистить память в куче
invoke HeapFree,[hHeap],NULL,[hMemory]
no_text:
; Субклассировать обработчик окна
invoke SetWindowLong,[wParam],GWL_WNDPROC,NewWindowProc
; Сохранить адрес предыдущего обработчика
mov [pOldProc],eax
; Снять перехватчик
invoke UnhookWindowsHookEx,[hMBHook]
; Восстановить регистры
popa
xor eax,eax
ret
@@:
; Восстановить регистры
popa
; Передать управление следующему обработчику
invoke CallNextHookEx,[hMBHook],[nCode],[wParam],[lParam]
ret
endp
Субклассированный обработчик окна для перетаскивания его за любое место почти такой же, как и в предыдущей статье. Разница только в том, что адрес предыдущего обработчика должен храниться в отдельной переменной. Это связано с особенностями заполнения структур окна MessageBox. После выполнения получается полупрозрачный MessageBox с иконкой в заголовке, измененным текстом на стандартных кнопках "Да" и "Нет", текст сообщения помещен в EDIT и его можно выделить мышкой и скопировать, а само окно можно перемещать за любое место. Естественно, что в прикладных программах одновременно столько наворотов не потребуется, это сделано только для демонстрации.
В приложении пример программы с исходным текстом, реализующей описанный в статье метод перехвата и обработки MessageBox.