Автор: Роман Панышев (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.