Blog. Just Blog

Универсальное субклассирование окон на Ассемблере

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Тема субклассирования окон уже не один раз поднималась в статьях на этом сайте. Теперь пришло время навести порядок и сделать наиболее удобное и универсальное решение для этой задачи. Воспользуемся функциями SetProp и GetProp, чтобы хранить адреса обработчиков прямо в свойствах окна без заведения дополнительных переменных.
  1. ;-------------------------------------------------------------------------
  2. ; Наименования свойств. Можно переименовать, если конфликтуют с другими 
  3. ; строками в программе
  4. ;-------------------------------------------------------------------------
  5. szOld   db 'OldWndProc', 0
  6. szNew   db 'NewWndProc', 0
  7.  
  8. ;-------------------------------------------------------------------------
  9. ; Функция субклассирования окна
  10. ;-------------------------------------------------------------------------
  11. ;  hWnd - хэндл окна для субклассирования
  12. ;  SubclassProc - адрес нового обработчика окна
  13. ;-------------------------------------------------------------------------
  14. proc SubclassWindow hWnd:DWORD,SubclassProc:DWORD
  15.         pusha
  16.  
  17.         ; Получить старый обработчик окна
  18.         invoke  GetWindowLong,[hWnd],GWL_WNDPROC
  19.         ; Сохранить его в свойствах окна
  20.         invoke  SetProp,[hWnd],szOld,eax
  21.         ; Сохранить в свойствах новый обработчик окна
  22.         invoke  SetProp,[hWnd],szNew,[SubclassProc]
  23.         ; Назначить окну универсальную функцию обработки
  24.         invoke  SetWindowLong,[hWnd],GWL_WNDPROC,CommonSubclassProc
  25.  
  26.         popa
  27.         ret
  28. endp
Использование функции простейшее. Обычно на этапе инициализации окна нужным элементам назначаются собственные обработчики:
  1.         ; Субклассировать поле ввода
  2.         invoke  GetDlgItem,[hwnddlg],ID_HEX
  3.         stdcall SubclassWindow,eax,EditWindowProc
  4.  
  5.         ; Субклассировать кнопку
  6.         invoke  GetDlgItem,[hwnddlg],ID_BTN
  7.         stdcall SubclassWindow,eax,ButtonProc
Нечасто, но может возникнуть ситуация, когда надо отменить субклассирование какого-либо элемента диалогового окна. Не проблема, есть и для этого своя функция.
  1. ;-------------------------------------------------------------------------
  2. ; Функция снятия субклассирования с окна
  3. ;-------------------------------------------------------------------------
  4. ;  hWnd - хэндл окна
  5. ;-------------------------------------------------------------------------
  6. proc UnSubclassWindow hWnd:DWORD
  7.         pusha
  8.  
  9.         ; Получить сохраненный обработчик окна
  10.         invoke  GetProp,[hWnd],szOld
  11.         or      eax,eax
  12.         ; Сохраненного обработчика нет
  13.         jz      .loc_ret
  14.         ; Назначить окну старую функцию обработки
  15.         invoke  SetWindowLong,[hWnd],GWL_WNDPROC,eax
  16.         ; Удалить сохраненные обработчики
  17.         invoke  RemoveProp,[hWnd],szOld
  18.         invoke  RemoveProp,[hWnd],szNew
  19. .loc_ret:
  20.         popa
  21.         ret
  22. endp
Здесь проверяется наличие сохраненного обработчика, если он есть, то обработчик окна восстанавливается на исходный, после чего сохраненные значения очищаются.

В чем же главная фишка этого способа субклассирования? Наверняка вы обратили внимание, что в качестве обработчика окна назначается не пользовательская процедура, а некая процедура CommonSubclassProc. Это и есть универсальная функция обработки субклассированого окна, вот ее код:
  1. ;-------------------------------------------------------------------------
  2. ; Универсальная функция обработки субклассированого окна
  3. ;-------------------------------------------------------------------------
  4. proc CommonSubclassProc hWnd:DWORD,wMsg:DWORD,wParam:DWORD,lParam:DWORD
  5.         ; Получить адрес нового обработчика
  6.         invoke  GetProp,[hWnd],szNew
  7.         or      eax,eax
  8.         jz      .call_old_proc
  9.  
  10.         ; Сохранить изменяемые регистры и вызвать обработчик
  11.         push    esi
  12.         push    edi
  13.         push    ebx
  14.         stdcall eax,[hWnd],[wMsg],[wParam],[lParam]
  15.         pop     ebx
  16.         pop     edi
  17.         pop     esi
  18.         ; Если обработчик вернул CF=0, то больше ничего не делать
  19.         jnc     .loc_ret
  20.  
  21. .call_old_proc:
  22.         ; Получить адрес старого обработчика
  23.         invoke  GetProp,[hWnd],szOld
  24.         or      eax,eax
  25.         jz      .loc_ret
  26.         ; Вызывать старый обработчик окна
  27.         invoke  CallWindowProc,eax,[hWnd],[wMsg],[wParam],[lParam]
  28. .loc_ret:
  29.         ret
  30. endp
Универсальность обработки подразумевает, что только эта функция вызывается для каждого субклассированного окна. Она получает из свойств этого окна адреса его старого и нового обработчика, затем вызывает пользовательский обработчик, а затем, если это необходимо, передает управление старому обработчику. Решение о необходимости дальнейшей обработки принимает пользовательский обработчик. Он должен вернуть взведенный флаг CF, если сообщение должно быть обработано дальше по цепочке, и CF=0, если дальнейшая обработка не требуется.

В приложении пример программы с исходным текстом, которая создает диалоговое окно с двумя субклассированными элементами: полем ввода для HEX-цифр и кнопкой, реагирующей на клик правой кнопкой мыши.

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

Subclassing.Demo.zip (3,245 bytes)


Поделиться ссылкой ВКонтакте Поделиться ссылкой на Facebook Поделиться ссылкой на LiveJournal Поделиться ссылкой в Мой Круг Добавить в Мой мир Добавить на ЛиРу (Liveinternet) Добавить в закладки Memori Добавить в закладки Google
Просмотров: 445 | Комментариев: 0

Комментарии

Отзывы посетителей сайта о статье
Комментариeв нет

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2020
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.0045 сек.) / Память: 4.75 Mb
Наверх