Common Controls - практика


Доброго времени суток, уважаемые подписчики.


Сегодня мы сделаем простенькую программу с одной кнопкой и обсудим преимущества использования Common Controls в окнах перед диалогами. Так как один из самых больших недостатков диалогов является их зафиксированный размер, для примера я выбрал программу, в которой не зависимо от размеров окна кнопка всегда будет оставаться в центре окна. Для выполнения этой задачи мы должны будем обрабатывать сообщение WM_SIZE, которое посылается при изменении размера окна. Также нам надо обеспечить видимость кнопки, так как если окно будет 0 пикселей в высоту, то кнопку мы не увидим. Чтоб предотвратить задание меньшего размера, а не обрабатывать его как исключение мы должны также обрабатывать сообщение WM_SIZING, которое приходит в момент изменения размера окна. В lParam этого сообщение лежит структура RECT, изменяя которую мы можем изменять будущее положение окна. Вот в принципе и всё, что нужно знать для создания такой программы, поэтому давайте приступим!

ID_BUTTONequ100h
include macros.inc
include def32.inc
include kernel32.inc
include user32.inc
include comdlg32.inc
.386
.model flat
.data
HQuitButtondd?
wcWNDCLASSEX<size wc,cs_hredraw or cs_vredraw,offset win_proc,0,0,?,?,?, color_window,0,offset class_name,0>
msg_MSG
rcRECT
class_namedb'Simple Window',0
ButtonClassdb'Button',0
WndNamedb'Button example',0
QuitButtondb'Exit',0
.code
_start:
null ebx
push ebx
call GetModuleHandle
push ebx
push eax
mov wc.hInstance,eax
push IDI_Application
push ebx
call loadicon
mov wc.hIcon,eax
push idc_arrow
push ebx
call LoadCursor
mov wc.hCursor,eax
push offset wc
call registerclassEx
mov ecx,CW_USEDEFAULT
push ebx
push ebx
push ecx
push ecx
push ecx
push ecx
push WS_OVERLAPPEDWINDOW
push offset WndName
push offset class_name
push ebx
call CreateWindowEx
push eax
push sw_shownormal
push eax
call showwindow
call updatewindow
mov edi,offset msg_
main_loop:
push ebx
push ebx
push ebx
push edi
call Getmessage
test eax,eax
jz exit_program
push edi
call TranslateMessage
push edi
call DispatchMessage
jmp short main_loop
exit_program:
push ebx
call ExitProcess
win_proc proc
push ebp
mov ebp,esp
wp_hWndequ dword ptr [ebp+08h]
wp_uMsgequ dword ptr [ebp+0Ch]
wp_wParamequ dword ptr [ebp+10h]
wp_lParamequ dword ptr [ebp+14h]
null ebx
mov eax,wp_uMsg
cmp eax,WM_DESTROY
je destroy_proc
cmp eax,WM_CREATE
je create_proc
cmp eax,WM_SIZe
je size_proc
cmp eax,WM_SIZING
je sizing_proc
null eax
leave
jmp DefWindowProc
size_proc:
push offset rc
push wp_hWnd
call GetClientRect
mov eax,rc.bottom
sub eax,rc.top
shr eax,1
sub eax,10
mov rc.top,eax
mov eax,20
mov rc.bottom,eax
mov eax,rc.right
sub eax,rc.left
shr eax,1
sub eax,25
mov rc.left,eax
mov eax,50
push 1
push rc.bottom
push eax
push rc.top
push rc.left
push HQuitButton
call MoveWindow
jmp short WndProcEnd
sizing_proc:
mov esi,wp_lParam
mov eax,[esi+0Ch]
sub eax,47
cmp eax,[esi+04h]
jnb WndProcEnd
mov eax,[esi+04h]
add eax,47
mov [esi+0ch],eax
WndProcEnd:
null eax
inc eax
leave
jmp DefWindowProc
create_proc:
push offset rc
push wp_hWnd
call GetClientRect
mov eax,rc.bottom
sub eax,rc.top
shr eax,1
sub eax,10
mov rc.top,eax
mov eax,20
mov rc.bottom,eax
mov eax,rc.right
sub eax,rc.left
shr eax,1
sub eax,25
mov rc.left,eax
mov eax,50
push ebx
push wc.hInstance
push ID_BUTTON
push wp_hWnd
push rc.bottom
push eax
push rc.top
push rc.left
push WS_VISIBLE or WS_CHILD or WS_BORDER
push offset QuitButton
push offset ButtonClass
push ebx
call CreateWindowEx
mov HQuitButton,eax
jmp WndProcEnd
destroy_proc:
push ebx
call PostQuitMessage
leave
ret 16
win_proc endp
end _start

Теперь давайте рассмотрим некоторые моменты этой программы. Я думаю, большая часть программы Вам понятна, но есть некоторые моменты. Проблемные моменты я выделил разными цветами, первый момент (красный цвет). Этот случай наилучшим образом отображает выгоду программирования на языке Ассемблер. Так как push eax гораздо быстрее соответствующей команде для переменной в памяти, мы помещаем его в стек задолго до его использования, но ведь стек же сохраняется! Второй момент отмечен зелёным и является лишь замечанием, что иногда гораздо выгоднее несколько раз прописать один и тот же код, чем помещать его в процедуру, так как create_proc вызывается один раз, а вот size_proc может быть вызвано огромное количество раз, а так как переход в процедуру и возврат оттуда тоже занимают процессорное время, то легче два раза прописать один и тот же код.

Ещё хочу отдельно рассмотреть sizing_proc. В ней, как я уже сказал ранее, передаётся адрес глобальной структуры RECT, поэтому, записав этот адрес в esi, я могу смело сказать, что [esi]=RECT.left, [esi+04h]=RECT.top, [esi+08h]=RECT.right, [esi+0Ch]=RECT.bottom, что мы и видим в моём примере. Надеюсь, всё понятно, если есть какие вопросы, пишите!

Следующие файлы Вы можете скачать к этому уроку:
0012asm - код программы
0012exe - сама программа
0012inc - файлы дополнения
0012lib - библиотеки импорта


На сегодня это всё. Если есть какие вопросы, пишите, обязательно отвечу. Пишите, Dark_Lord@RusFAQ.ru, Dark_Lord@land.ru.
Или свяжитесь со мной по ICQ, мой номер 126222874!

Сайт управляется системой uCoz