Страница 3 из 5 Пример 3. Оконное приложение Файл dmenu.asm Ideal P586 Radix16 Modelflat
strucWndClassEx cbSizedd0 styledd0 lpfnWndProcdd0 cbClsExtradd0 cbWndExtradd0 hInstancedd0 hIcondd0 hCursordd0 hbrBackgrounddd0 lpszMenuNamedd0 lpszClassNamedd0 hIconSmdd0 endsWndClassEx
strucPoint leftdd0 topdd0 rightdd0 bottomdd0 endsPoint
strucmsgStruc hwnddd0 messagedd0 wParamdd0 lParamdd0 timedd0 ptPoint<> endsmsgStruc
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
PROCTYPEptGetModuleHandlestdcall\ lpModuleName:dword
PROCTYPEptLoadIconstdcall\ hInstance:dword,\ lpIconName:dword
PROCTYPEptLoadCursorstdcall\ hInstance:dword,\ lpCursorName:dword
PROCTYPEptLoadMenustdcall\ hInstance:dword,\ lpMenuName:dword
PROCTYPEptRegisterClassExstdcall\ lpwcx:dword
PROCTYPEptCreateWindowExstdcall\ dwExStyle:dword,\ lpClassName:dword,\ lpWindowName:dword,\ dwStyle:dword,\ x:dword, \ y:dword,\ nWidth:dword,\ nHeight:dword,\ hWndParent:dword,\ hMenu:dword, \ hInstance:dword,\ lpParam:dword
PROCTYPEptShowWindowstdcall\ hWnd:dword,\ nCmdShow:dword
PROCTYPEptUpdateWindowstdcall\ hWnd:dword
PROCTYPEptGetMessagestdcall\ pMsg:dword,\ hWnd:dword,\ wMsgFilterMin:dword,\ wMsgFilterMax:dword
PROCTYPEptTranslateMessagestdcall\ lpMsg:dword
PROCTYPEptDispatchMessagestdcall\ pmsg:dword
PROCTYPEptSetMenustdcall\ hWnd:dword,\ hMenu:dword
PROCTYPEptPostQuitMessagestdcall\ nExitCode:dword
PROCTYPEptDefWindowProcstdcall\ hWnd:dword,\ Msg:dword,\ wParam:dword,\ lParam:dword
PROCTYPEptSendMessagestdcall\ hWnd:dword,\ Msg:dword,\ wParam:dword,\ lParam:dword
PROCTYPEptMessageBoxstdcall\ hWnd:dword,\ lpText:dword,\ lpCaption:dword,\ uType:dword
PROCTYPEptExitProcessstdcall\ exitCode:dword
extrnGetModuleHandleA:ptGetModuleHandle extrnLoadIconA:ptLoadIcon extrnLoadCursorA:ptLoadCursor extrnRegisterClassExA:ptRegisterClassEx extrnLoadMenuA:ptLoadMenu extrnCreateWindowExA:ptCreateWindowEx extrnShowWindow:ptShowWindow extrnUpdateWindow:ptUpdateWindow extrnGetMessageA:ptGetMessage extrnTranslateMessage:ptTranslateMessage extrnDispatchMessageA:ptDispatchMessage extrnSetMenu:ptSetMenu extrnPostQuitMessage:ptPostQuitMessage extrnDefWindowProcA:ptDefWindowProc extrnSendMessageA:ptSendMessage extrnMessageBoxA:ptMessageBox extrnExitProcess:ptExitProcess
UDataSeg hInstdd? hWnddd?
IFNDEFVER1 hMenudd? ENDIF
DataSeg msgmsgStruc<> classTitledb'Menu demo', 0 wndTitledb'Demo program', 0 msg_open_txtdb'You selected open', 0 msg_open_tltdb'Open box', 0 msg_save_txtdb'You selected save', 0 msg_save_tltdb'Save box', 0
CodeSeg Start:callGetModuleHandleA,0; не обязательно, но желательно mov[hInst],eax
subesp,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 callLoadIconA,0, IDI_APPLICATION mov[(WndClassEx esp).hIcon],eax callLoadCursorA,0, IDC_ARROW mov[(WndClassEx esp).hCursor],eax mov[(WndClassEx esp).hbrBackground],COLOR_WINDOW IFDEFVER1 mov[(WndClassEx esp).lpszMenuName],MyMenu ELSE mov[(WndClassEx esp).lpszMenuName],0 ENDIF mov[(WndClassEx esp).lpszClassName],offset classTitle mov[(WndClassEx esp).hIconSm],0 callRegisterClassExA,esp; зарегистрируем класс окна
addesp,SIZE WndClassEx; восстановим стек ; и создадим окно IFNDEFVER2 callCreateWindowExA,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 callLoadMenu,hInst, MyMenu mov[hMenu],eax callCreateWindowExA,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 callShowWindow,eax, SW_SHOW; show window callUpdateWindow,[hWnd]; redraw window
IFDEFVER3 callLoadMenuA,[hInst], MyMenu mov[hMenu],eax callSetMenu,[hWnd], eax ENDIF
msg_loop: callGetMessageA,offset msg, 0, 0, 0 orax,ax jzexit callTranslateMessage,offset msg callDispatchMessageA,offset msg jmpmsg_loop exit:callExitProcess,0
publicstdcallWndProc procWndProcstdcall arg@@hwnd:dword,@@msg:dword,@@wPar:dword,@@lPar:dword moveax,[@@msg] cmpeax,WM_COMMAND je@@command cmpeax,WM_DESTROY jne@@default callPostQuitMessage,0 xoreax,eax jmp@@ret @@default: callDefWindowProcA,[@@hwnd], [@@msg], [@@wPar], [@@lPar] @@ret:ret @@command: moveax,[@@wPar] cmpeax,ID_OPEN je@@open cmpeax,ID_SAVE je@@save callSendMessageA,[@@hwnd], WM_CLOSE, 0, 0 xoreax,eax jmp@@ret @@open:moveax, offset msg_open_txt movedx, offset msg_open_tlt jmp@@mess @@save:moveax, offset msg_save_txt movedx, offset msg_save_tlt @@mess:callMessageBoxA,0, eax, edx, MB_OK xoreax,eax jmp@@ret endpWndProc endStart Комментарии к программе Здесь мне хотелось в первую очередь продемонстрировать использование прототипов функций API Win32. Конечно их (а также описание констант и структур из API Win32) следует вынести в отдельные подключаемые файлы, поскольку, скорее всего Вы будете использовать их и в других программах. Описание прототипов функций обеспечивает строгий контроль со стороны компилятора за количеством и типом параметров, передаваемых в функции. Это существенно облегчает жизнь программисту, позволяя избежать ошибок времени исполнения, тем более, что число параметров в некоторых функциях API Win32 весьма значительно. Существо данной программы заключается в демонстрации вариантов работы с оконным меню. Программу можно откомпилировать в трёх вариантах (версиях), указывая компилятору ключи VER2 или VER3 (по умолчанию используется ключ VER1). В первом варианте программы меню определяется на уровне класса окна и все окна данного класса будут иметь аналогичное меню. Во втором варианте, меню определяется при создании окна, как параметр функции CreateWindowEx. Класс окна не имеет меню и в данном случае, каждое окно этого класса может иметь своё собственное меню. Наконец, в третьем варианте, меню загружается после создания окна. Данный вариант показывает, как можно связать меню с уже созданным окном. Директивы условной компиляции позволяют включить все варианты в текст одной и той же программы. Подобная техника удобна не только для демонстрации, но и для отладки. Например, когда Вам требуется включить в программу новый фрагмент кода, то Вы можете применить данную технику, дабы не потерять функционирующий модуль. Ну, и конечно, применение директив условной компиляции - наиболее удобное средство тестирования различных решений (алгоритмов) на одном модуле. Представляет определённый интерес использование стековых фреймов и заполнение структур в стеке посредством регистра указателя стека (esp). Именно это продемонстрировано при заполнении структуры WndClassEx. Выделение места в стеке (фрейма) делается простым перемещением esp: subesp,SIZE WndClassEx Теперь мы можем обращаться к выделенной памяти используя всё тот же регистр указатель стека. При создании 16-битных приложений такой возможностью мы не обладали. Данный приём можно использовать внутри любой процедуры или даже произвольном месте программы. Накладные расходы на подобное выделение памяти минимальны, однако, следует учитывать, что размер стека ограничен и размещать большие объёмы данных в стеке вряд ли целесообразно. Для этих целей лучше использовать "кучи" (heap) или виртуальную память (virtual memory). Остальная часть программы достаточно тривиальна и не требует каких-либо пояснений. Возможно более интересным покажется тема использования макроопределений. |