Автор: Роман Панышев (irrona)


Использование окон в Windows

      Примеры данной статьи объясняют каким образом можно выполнить следующие операции с окнами:

Создание главного (основного) окна приложения

      Обычно первое, создаваемое приложением окно, является его главным окном. Для создания главного окна приложения используйте функцию 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.

Использование прозрачных (layered) окон

      Для создания прозрачного диалогового окна, сначала создайте обычное диалоговое окно. Затем, в событии 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.


Предыдущая страница   Следующая страница


© 2005 ironahot@idknet.com - при использовании статей просьба делать ссылку на автора