Пример 3. Оконное приложение

Файл dmenu.asm

Ideal

P586

Radix  16

Model  flat


struc  WndClassEx

    cbSize    dd   0

    style     dd   0

    lpfnWndProc  dd   0

    cbClsExtra  dd   0

    cbWndExtra  dd   0

    hInstance   dd   0

    hIcon     dd   0

    hCursor       dd   0

    hbrBackground dd   0

    lpszMenuName dd   0

    lpszClassName dd   0

    hIconSm       dd   0

ends  WndClassEx


struc  Point

    left     dd   0

    top      dd   0

    right     dd   0

    bottom    dd   0       

ends  Point


struc  msgStruc

    hwnd     dd   0

    message       dd   0

    wParam    dd   0

    lParam    dd   0

    time     dd   0

    pt      Point  <>

ends  msgStruc


MyMenu        = 0065

ID_OPEN           = 9C41

ID_SAVE           = 9C42

ID_EXIT           = 9C43


CS_VREDRAW      = 0001

CS_HREDRAW      = 0002

IDI_APPLICATION       = 7F00

IDC_ARROW      = 7F00

COLOR_WINDOW     = 5

WS_EX_WINDOWEDGE   = 00000100

WS_EX_CLIENTEDGE   = 00000200

WS_EX_OVERLAPPEDWINDOW   = WS_EX_WINDOWEDGE OR WS_EX_CLIENTEDGE

WS_OVERLAPPED    = 00000000

WS_CAPTION      = 00C00000

WS_SYSMENU      = 00080000

WS_THICKFRAME    = 00040000

WS_MINIMIZEBOX       = 00020000

WS_MAXIMIZEBOX       = 00010000

WS_OVERLAPPEDWINDOW =    WS_OVERLAPPED   OR \

              WS_CAPTION     OR \

              WS_SYSMENU     OR \

              WS_THICKFRAME   OR \

              WS_MINIMIZEBOX   OR \

              WS_MAXIMIZEBOX 

CW_USEDEFAULT    = 80000000

SW_SHOW           = 5

WM_COMMAND      = 0111

WM_DESTROY      = 0002

WM_CLOSE       = 0010

MB_OK        = 0


PROCTYPE   ptGetModuleHandle  stdcall    \

           lpModuleName :dword


PROCTYPE   ptLoadIcon      stdcall    \

           hInstance   :dword,    \

           lpIconName  :dword


PROCTYPE   ptLoadCursor     stdcall    \

           hInstance   :dword,    \

           lpCursorName :dword


PROCTYPE   ptLoadMenu      stdcall    \

           hInstance   :dword,    \

           lpMenuName  :dword


PROCTYPE   ptRegisterClassEx  stdcall    \

           lpwcx     :dword


PROCTYPE   ptCreateWindowEx   stdcall    \

           dwExStyle   :dword,    \

           lpClassName  :dword,    \

           lpWindowName :dword,    \

           dwStyle       :dword,    \

           x       :dword, \

           y       :dword,    \

           nWidth    :dword,    \

           nHeight       :dword,    \

           hWndParent  :dword,    \

           hMenu     :dword, \

           hInstance   :dword,    \

           lpParam       :dword


PROCTYPE   ptShowWindow     stdcall    \

           hWnd     :dword,    \

           nCmdShow   :dword


PROCTYPE   ptUpdateWindow       stdcall    \

           hWnd     :dword


PROCTYPE   ptGetMessage     stdcall    \

           pMsg     :dword,    \

           hWnd     :dword,    \

           wMsgFilterMin :dword,    \

           wMsgFilterMax :dword


PROCTYPE   ptTranslateMessage  stdcall    \

           lpMsg     :dword


PROCTYPE   ptDispatchMessage  stdcall    \

           pmsg     :dword


PROCTYPE   ptSetMenu      stdcall    \

           hWnd     :dword,    \

           hMenu     :dword


PROCTYPE   ptPostQuitMessage  stdcall    \

           nExitCode   :dword


PROCTYPE   ptDefWindowProc       stdcall    \

           hWnd     :dword,    \

           Msg      :dword,    \

           wParam    :dword,    \

           lParam    :dword


PROCTYPE   ptSendMessage    stdcall    \

           hWnd     :dword,    \

           Msg      :dword,    \

           wParam    :dword,    \

           lParam    :dword


PROCTYPE   ptMessageBox     stdcall    \

           hWnd     :dword,    \

           lpText    :dword,    \

           lpCaption   :dword,    \

           uType     :dword


PROCTYPE   ptExitProcess    stdcall    \

           exitCode   :dword


extrn     GetModuleHandleA   :ptGetModuleHandle

extrn     LoadIconA      :ptLoadIcon

extrn     LoadCursorA     :ptLoadCursor

extrn     RegisterClassExA   :ptRegisterClassEx

extrn     LoadMenuA      :ptLoadMenu

extrn     CreateWindowExA       :ptCreateWindowEx

extrn     ShowWindow      :ptShowWindow

extrn     UpdateWindow     :ptUpdateWindow

extrn     GetMessageA     :ptGetMessage

extrn     TranslateMessage   :ptTranslateMessage

extrn     DispatchMessageA   :ptDispatchMessage

extrn     SetMenu           :ptSetMenu

extrn     PostQuitMessage       :ptPostQuitMessage

extrn      DefWindowProcA       :ptDefWindowProc

extrn     SendMessageA     :ptSendMessage

extrn     MessageBoxA     :ptMessageBox

extrn     ExitProcess     :ptExitProcess


UDataSeg

hInst     dd      ?

hWnd     dd      ?


IFNDEF VER1

hMenu     dd      ?

ENDIF



DataSeg

msg      msgStruc   <>

classTitle  db   'Menu demo', 0

wndTitle   db   'Demo program', 0

msg_open_txt db   'You selected open', 0

msg_open_tlt db   'Open box', 0

msg_save_txt db   'You selected save', 0

msg_save_tlt db   'Save box', 0


CodeSeg

Start: call  GetModuleHandleA,  0    ; не обязательно, но желательно

    mov   [hInst],eax


    sub   esp,SIZE WndClassEx     ; отведём место в стеке под структуру


    mov   [(WndClassEx esp).cbSize],SIZE WndClassEx

    mov   [(WndClassEx esp).style],CS_HREDRAW or CS_VREDRAW

    mov   [(WndClassEx esp).lpfnWndProc],offset WndProc

    mov   [(WndClassEx esp).cbWndExtra],0

    mov   [(WndClassEx esp).cbClsExtra],0

    mov   [(WndClassEx esp).hInstance],eax

    call  LoadIconA,      0, IDI_APPLICATION

    mov   [(WndClassEx esp).hIcon],eax

    call  LoadCursorA,     0, IDC_ARROW

    mov   [(WndClassEx esp).hCursor],eax

    mov   [(WndClassEx esp).hbrBackground],COLOR_WINDOW

IFDEF  VER1

    mov   [(WndClassEx esp).lpszMenuName],MyMenu

ELSE

    mov   [(WndClassEx esp).lpszMenuName],0

ENDIF

    mov   [(WndClassEx esp).lpszClassName],offset classTitle

    mov   [(WndClassEx esp).hIconSm],0

    call  RegisterClassExA,  esp   ; зарегистрируем класс окна


    add   esp,SIZE WndClassEx     ; восстановим стек

                     ; и создадим окно

IFNDEF VER2

    call  CreateWindowExA,   WS_EX_OVERLAPPEDWINDOW, \ extended window style

                  offset classTitle, \ pointer to registered class name 

                  offset wndTitle,\ pointer to window name

                  WS_OVERLAPPEDWINDOW,    \ window style

                  CW_USEDEFAULT,    \ horizontal position of window

                  CW_USEDEFAULT,    \ vertical position of window

                  CW_USEDEFAULT,    \ window width

                  CW_USEDEFAULT,    \ window height

                  0,      \ handle to parent or owner window

                  0,   \ handle to menu, or child-window identifier

                  [hInst],   \ handle to application instance

                  0       ; pointer to window-creation data

ELSE

    call  LoadMenu,      hInst, MyMenu

    mov   [hMenu],eax

    call  CreateWindowExA,   WS_EX_OVERLAPPEDWINDOW,   \ extended window style

                  offset classTitle, \ pointer to registered class name 

                  offset wndTitle,   \ pointer to window name

                  WS_OVERLAPPEDWINDOW,    \ window style

                  CW_USEDEFAULT,    \ horizontal position of window

                   CW_USEDEFAULT,    \ vertical position of window

                  CW_USEDEFAULT,    \ window width

                  CW_USEDEFAULT,    \ window height

                  0,      \ handle to parent or owner window

                  eax,  \ handle to menu, or child-window identifier

                  [hInst],   \ handle to application instance

                  0       ; pointer to window-creation data

ENDIF

    mov   [hWnd],eax

    call  ShowWindow,     eax, SW_SHOW     ; show window

    call  UpdateWindow,    [hWnd]        ; redraw window


IFDEF  VER3

    call  LoadMenuA,      [hInst], MyMenu

    mov   [hMenu],eax

    call  SetMenu,       [hWnd], eax

ENDIF


msg_loop:

    call  GetMessageA,     offset msg, 0, 0, 0

    or   ax,ax

    jz   exit

    call  TranslateMessage,  offset msg

    call  DispatchMessageA,  offset msg

    jmp   msg_loop

exit:  call  ExitProcess,     0


public stdcall    WndProc

proc  WndProc    stdcall

arg   @@hwnd:    dword, @@msg: dword, @@wPar:    dword, @@lPar:    dword

    mov   eax,[@@msg]

    cmp   eax,WM_COMMAND

    je   @@command

    cmp   eax,WM_DESTROY

    jne   @@default

    call  PostQuitMessage,   0

    xor   eax,eax

    jmp   @@ret

@@default:

    call  DefWindowProcA,   [@@hwnd], [@@msg], [@@wPar], [@@lPar]

@@ret: ret

@@command:

    mov   eax,[@@wPar]

    cmp   eax,ID_OPEN

    je   @@open

    cmp   eax,ID_SAVE

    je   @@save

    call  SendMessageA,    [@@hwnd], WM_CLOSE, 0, 0

    xor   eax,eax

    jmp   @@ret

@@open:    mov   eax, offset msg_open_txt

    mov   edx, offset msg_open_tlt

    jmp   @@mess

@@save:    mov   eax, offset msg_save_txt

    mov   edx, offset msg_save_tlt

@@mess:    call  MessageBoxA,     0, eax, edx, MB_OK

    xor   eax,eax

    jmp   @@ret

endp  WndProc

end   Start

Комментарии к программе

Здесь мне хотелось в первую очередь продемонстрировать использование прототипов функций API Win32. Конечно их (а также описание констант и структур из API Win32) следует вынести в отдельные подключаемые файлы, поскольку, скорее всего Вы будете использовать их и в других программах. Описание прототипов функций обеспечивает строгий контроль со стороны компилятора за количеством и типом параметров, передаваемых в функции. Это существенно облегчает жизнь программисту, позволяя избежать ошибок времени исполнения, тем более, что число параметров в некоторых функциях API Win32 весьма значительно.

Существо данной программы заключается в демонстрации вариантов работы с оконным меню. Программу можно откомпилировать в трёх вариантах (версиях), указывая компилятору ключи VER2 или VER3 (по умолчанию используется ключ VER1). В первом варианте программы меню определяется на уровне класса окна и все окна данного класса будут иметь аналогичное меню. Во втором варианте, меню определяется при создании окна, как параметр функции CreateWindowEx. Класс окна не имеет меню и в данном случае, каждое окно этого класса может иметь своё собственное меню. Наконец, в третьем варианте, меню загружается после создания окна. Данный вариант показывает, как можно связать меню с уже созданным окном.

Директивы условной компиляции позволяют включить все варианты в текст одной и той же программы. Подобная техника удобна не только для демонстрации, но и для отладки. Например, когда Вам требуется включить в программу новый фрагмент кода, то Вы можете применить данную технику, дабы не потерять функционирующий модуль. Ну, и конечно, применение директив условной компиляции – наиболее удобное средство тестирования различных решений (алгоритмов) на одном модуле.

Представляет определённый интерес использование стековых фреймов и заполнение структур в стеке посредством регистра указателя стека (esp). Именно это продемонстрировано при заполнении структуры WndClassEx. Выделение места в стеке (фрейма) делается простым перемещением esp:

    sub   esp,SIZE WndClassEx

Теперь мы можем обращаться к выделенной памяти используя всё тот же регистр указатель стека. При создании 16-битных приложений такой возможностью мы не обладали. Данный приём можно использовать внутри любой процедуры или даже произвольном месте программы. Накладные расходы на подобное выделение памяти минимальны, однако, следует учитывать, что размер стека ограничен и размещать большие объёмы данных в стеке вряд ли целесообразно. Для этих целей лучше использовать “кучи” (heap) или виртуальную память (virtual memory).

Остальная часть программы достаточно тривиальна и не требует каких-либо пояснений. Возможно более интересным покажется тема использования макроопределений.

Загрузка...