Автор: Роман Панышев (irrona)
Примеры данной статьи объясняют каким образом можно выполнить следующие операции с окнами:
Обычно первое, создаваемое приложением окно, является его главным окном. Для создания главного окна приложения используйте функцию CreateWindowEx, в которой укажите класс окна, его имя, стили, размеры, позицию на экране, идентификатор меню, идентификатор экземпляра окна и дополнительные данные. Главное окно принадлежит классу окна, определяемому создающим его приложением. Поэтому, до вызова функции CreateWindowEx, необходимо зарегистрировать класс окна и указать процедуру окна для класса.
При создании главного окна, многие приложения обычно используют стиль WS_OVERLAPPEDWINDOW. Этот стиль указывает на то, что создаваемое окно будет иметь заголовок, меню, кнопки разворачивания/сворачивания, размеры окна можно будет изменять. Функция CreateWindowEx возвращает идентификатор (описатель, хэндл, handle - используйте по своему предпочтению) окна.
Следующий пример создает главное окно с классом, определяемым создающим его приложением. Имя окна "Main Window" будет написано в заголовке окна. Объединение стилей WS_VSCROLL и WS_HSCROLL со стилем WS_OVERLAPPEDWINDOW, позволит создать окно с полосами прокрутки. Четыре константы CW_USEDEFAULT позволят задать значения по-умолчанию для размеров и позиции окна. Вместо указания идентификатора меню, используем значение NULL, т.е. окно будет иметь меню, определенное системой для окон данного класса.
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
.code
start:
WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
; Регистрация класса главного окна
mov wc.cbSize, SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
push hInstance
pop wc.hInstance
mov wc.hbrBackground, COLOR_WINDOW + 1
mov wc.lpszMenuName, NULL
mov wc.lpszClassName, OFFSET ClassMainName
invoke LoadIcon, NULL, IDI_APPLICATION
mov wc.hIcon, eax
mov wc.hIconSm, eax
invoke LoadCursor, NULL, IDC_ARROW
mov wc.hCursor, eax
invoke RegisterClassEx, addr wc
; Создание главного окна приложения
; В случае успеха переменная hwndMain не равна 0
invoke CreateWindowEx, NULL, addr ClassMainName, addr WinMainName,\
WS_OVERLAPPEDWINDOW or WS_HSCROLL or WS_VSCROLL, CW_USEDEFAULT, CW_USEDEFAULT,\
CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInst, NULL
mov hwnd, eax
; Показать окно, используя флаг, заданный программой, которая запустила приложение,
; и послать окну распоряжение на перерисовку (сообщение WM_PAINT).
invoke ShowWindow, hwnd, CmdShow
invoke UpdateWindow, hwnd
; Входим в цикл обработки сообщений
.while TRUE
invoke GetMessage, addr msg, NULL, 0, 0
.break .if(!eax)
invoke TranslateMessage, addr msg
invoke DispatchMessage, addr msg
.endw
mov eax, msg.wParam
ret
WinMain endp
Заметьте, что после создания главного окна, вызывается функция ShowWindow. Это необходимо потому, что система не отображает автоматически главное окно после его создания. Использование флага SW_SHOWDEFAULT при вызове функции ShowWindow, позволяет запускающей приложение программе установить начальное состояние главного окна (например, окно можно отобразить свернутым, используя флаг SW_SHOWMINIMIZED). Функция UpdateWindow посылает созданному окну первое в его "жизни" сообщение WM_PAINT.
Используя дочерние окна, можно разделить клиентскую область основного окна на функциональные части. Дочерние окна создаются так же, как и основные, с помощью функции CreateWindowEx. Для создания окна определенного класса, данный класс необходимо сначала зарегистрировать. При создании дочернего окна обязательно используйте стиль WS_CHILD и укажите идентификатор родительского окна, которому оно принадлежит (девятый параметр функции CreateWindowEx).
Следующий пример, делит клиентскую область главного окна на три функциональные части, принадлежащие трем дочерним окнам одинакового размера. Каждое создаваемое дочернее окно имеет высоту, равную высоте главного окна, и ширину, равную 1/3 ширины главного окна. Дочерние окна создаются в событии WM_CREATE главного окна. Поскольку каждое дочернее окно имеет стиль WS_BORDER, они имеют тонкие границы. Кроме этого, поскольку при создании дочерних окон не указан стиль WS_VISIBLE, они создаются невидимыми. Заметьте также, что каждому создаваемому дочернему окну, принадлежит свой идентификатор окна.
Главное окно изменяет размеры и позиции дочерних окон в событии WM_SIZE, которое оно получает, когда его собственные размеры изменяются. При получении сообщения WM_SIZE, главное окно извлекает размеры своей клиентской области, используя функцию GetWindowRect, и, затем отсылает полученные значения в функцию EnumChildWindows. Функция EnumChildWindows, используя пользовательскую callback-функцию EnumChildProc, присваивает каждому дочернему окну идентификатор, устанавливает его размеры и позицию при помощи функции MoveWindow, после чего вызывает функию ShowWindow для того, чтобы сделать его видимым.
.const
ID_FIRSTCHILD equ 100
ID_SECONDCHILD equ 101
ID_THIRDCHILD equ 102
.code
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL wc:WNDCLASSEX
LOCAL rcClient:RECT
LOCAL i:DWORD
.if uMsg == WM_DESTROY
invoke PostQuitMessage, NULL
.elseif uMsg == WM_CREATE
; Создание трех невидимых дочерних окон
mov wc.cbSize, SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, offset ChildProc
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
push hInstance
pop wc.hInstance
mov wc.hbrBackground, COLOR_WINDOW + 1
mov wc.lpszMenuName, NULL
mov wc.lpszClassName, OFFSET ClassChildName
invoke LoadIcon, NULL, IDI_APPLICATION
mov wc.hIcon, eax
mov wc.hIconSm, eax
invoke LoadCursor, NULL, IDC_ARROW
mov wc.hCursor, eax
invoke RegisterClassEx, addr wc
mov i, 0
.while i != 3
xor ecx, ecx
mov ecx, i
add ecx, ID_FIRSTCHILD
invoke CreateWindowEx, 0, addr ClassChildName, NULL,\
WS_CHILD or WS_BORDER, 0, 0, 0, 0, hWnd,\
ecx, hInstance, NULL
inc i
.endw
.elseif uMsg == WM_SIZE
; Получить размеры клиентской области главного окна
; и посчитать (перечислить) дочерние окна. Передать
; размеры главного окна в функцию перечисления.
invoke GetClientRect, hWnd, addr rcClient
invoke EnumChildWindows, hWnd, addr EnumChildProc, addr rcClient
.else
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
.endif
xor eax, eax
ret
WndProc endp
EnumChildProc proc uses edi hwndChild:HWND, rcParent:DWORD
LOCAL i:DWORD
LOCAL idChild:DWORD
LOCAL dwX:DWORD
LOCAL dwW:DWORD
LOCAL dwB:DWORD
;Получить идентификатор дочернего окна.
;Использовать его для изменения позиции дочернего окна.
invoke GetWindowLong, hwndChild, GWL_ID
mov idChild, eax
; Вычислить координаты дочернего окна
assume edi:ptr RECT
mov edi, rcParent
mov eax, [edi].bottom
mov dwB, eax
mov eax, [edi].right
assume edi:nothing
xor edx, edx
mov ecx, 3
idiv ecx
mov dwW, eax
.if idChild == ID_FIRSTCHILD
mov dwX, 0
.elseif idChild == ID_SECONDCHILD
mov dwX, eax
.else
shl eax, 1
mov dwX, eax
.endif
; Изменение размера и позиции дочернего окна.
invoke MoveWindow, hwndChild, dwX, 0, dwW, dwB, TRUE
;Установка видимости дочернего окна.
invoke ShowWindow, hwndChild, SW_SHOW
; Чтобы эта функция работала в цикле необходимо вернуть значение TRUE,
; т.е. значение в eax должно быть отлично от 0
xor eax, eax
inc eax
ret
EnumChildProc endp
Для уничтожения окна можно воспользоваться функцией DestroyWindow. Обычно, перед уничтожением главного окна, приложение отправляет сообщение WM_CLOSE, при обработке которого, Вы можете показать пользователю сообщение о намечающемся закрытии окна, давая ему возможность принять или отменить данное действие. Окно, у которого имеется системное меню, автоматически получает сообщение WM_CLOSE при выборе пункта меню "Закрыть". Если пользователь подтверждает закрытие окна, приложение вызывает функцию DestroyWindow. После удаления окна с экрана, система посылает ему сообщение WM_DESTROY, во время обработки которого окно сохраняет необходимые данные и освобождает занимаемые им ресурсы системы. Главное окно завершает обработку сообщения WM_DESTROY вызовом функции PostQuitMessage, для завершения работы приложения.
Следующий пример показывает каким образом можно запросить подтверждение пользователя перед закрытием окна. При получении сообщения WM_CLOSE, пример отображает окно сообщения с кнопками "Да", "Нет", "Отмена". Если пользователь нажмет кнопку "Да", происходит вызов функции DestroyWindow. Иначе, окно не уничтожается. Поскольку уничтожаемое окно, является одновременно главным окном приложения, то при обработке сообщения WM_DESTROY вызывается функция PostQuitMessage, позволяющая корректно завершить работу приложения.
.if uMsg == WM_CLOSE
; Показать окно сообщения. Если пользователь нажал кнопку "Да",
; уничтожить главное окно
invoke MessageBox, hWnd, addr szConfirm, addr szAppName, MB_YESNOCANCEL
.if eax == IDYES
invoke DestroyWindow, hWnd
.endif
.if uMsg == WM_DESTROY
; Отправить сообщение WM_QUIT для завершения работы приложения.
invoke PostQuitMessage, NULL
.endif
Исходный код с примером создания главного и трёх дочерних окон находится в файле sample_4_1.asm, а скомпилированный пример в файле sample_4_1.exe.
Для создания прозрачного диалогового окна, сначала создайте обычное диалоговое окно. Затем, в событии WM_INITDIALOG, установите бит прозрачности расширенного стиля окна и вызовите API-функцию SetLayeredWindowAttributes с установленным значением прозрачности. Код должен быть примерно таким:
; Установить бит прозрачности (WS_EX_LAYERED) расширенного стиля окна
invoke GetWindowLong,hWnd, GWL_EXSTYLE
or eax, WS_EX_LAYERED
invoke SetWindowLong, hWnd, GWL_EXSTYLE, eax
; Установить значение альфа канала в значение 70%
invoke SetLayeredWindowAttributes, hWnd, 0, (255 * 70) / 100, LWA_ALPHA
Заметьте, что третий параметр функции SetLayeredWindowAttributes является числом от 0 до 255. Значение 0 создает полностью прозрачное окно, а значение 255 создает полностью непрозрачное окно. Этот параметр имитирует более гибкую структуру BLENDFUNCTION функции AlphaBlend. Чтобы вернуть окну непрозрачность, удалите бит WS_EX_LAYERED из вызова функции SetWindowLong и перерисуйте окно. Удаление бита прозрачности заставит систему освободить ресурсы задействованные для имитации прозрачности окна. Исходный код данной операции может выглядеть примерно так:
; Удалить бит WS_EX_LAYERED расширенного стиля окна
invoke GetWindowLong,hWnd, GWL_EXSTYLE
mov ecx, WS_EX_LAYERED
not ecx
and eax, ecx
invoke SetWindowLong, hWnd, GWL_EXSTYLE, eax
; Послать окну и его дочерним окнам указание на перерисовку
invoke RedrawWindow,hWnd, NULL, NULL, RDW_ERASE or RDW_INVALIDATE or RDW_FRAME or RDW_ALLCHILDREN
Исходный код с примером создания диалогового окна с поддержкой прозрачности ищите в файле sample_4_2.rar.