Шаг 94.
Небольшое отступление: приложение, выводящее заданный текст под произвольным углом

    На этом шаге мы рассмотрим работу программы, выводящей заданный текст под произвольным углом.

    Здесь мы представляем вам небольшое приложение, реализованное студентом 4 курса факультета математики и информационных технологий Курганского государственного университета Парфеновым Максимом (декабрь 2005 г.).

    Передо ним стояла задача создания приложения, позволяющего задать произвольный текст и вывести его под произвольным углом.

    Перечислим возможности приложения.

  1. Ввод произвольного текста в строку редактирования.
  2. Нажатием левой клавишей мыши выводить заданный в строке редактирования текст.
  3. Возможен выбор расположения текста:
    • горизонтальный;
    • вертикальный;
    • под углом.
  4. Выбор расположения текста осуществляется с помощью всплывающего меню.
  5. При выборе пункта меню "Под углом" появляется диалоговое окно, содержащее строку редактирования и кнопки: "OK" и "Выход". Угол вводится в строку редактирования.
  6. Возможна очистка окна с помощью пункта меню "Очистка".

    Приведем текст приложения:

.386P
;Плоская модель.
.MODEL FLAT, STDCALL
include konst.asm
;Директивы компоновщику для подключения библиотек.
includelib c:\masm32\lib\user32.lib 
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\gdi32.lib

;------------------------------------------------
;Сегмент данных. 
_DATA SEGMENT DWORD PUBLIC USE32 'DATA'
     NEWHWND    DD 0                
     MSG        MSGSTRUCT <?>
     WC         WNDCLASS  <?>
     PNT        PAINTSTR <?>
     REC        RECT <?>
     lg         LOGFONT <?>
     CPEDT      DB ' ',0
     CLSEDIT    DB 'EDIT',0     
     HWNDEDT    DWORD 0
     TITLENAME  DB 'Задание №3',0     
     HINST      DD 0 ;Дескриптор приложения.
     DLHINST    DD 0 ;Дескриптор диалогового окна.
     CLASSNAME  DB 'CLASS32',0     
     CAP        DB 'Сообщение',0     
     CAP2       DB 'Вы выбрали угол : ',0          
     KAS        DB 'Вы не ввели угол : ',0          
     MEN        DB 'MENUP',0     
     NFONT      DB 'Arial Cyr',0
     PA         DB 'DIAL1',0
     HMENU      DD ?
     X1         DD 0
     X2         DD 0
     A          DD 0
     LEN        DD 0
     PUSTO      DB  4 DUP(0) 
     BUF        DB  4 DUP(0) 
     BUF2       DB  4 DUP(0)
     SPOSOB     DD 0
     S1         DB 'Вы выбрали горизонтальное расположение текста',0
     S2         DB 'Вы выбрали вертикальное расположение текста',0
     S3	        DB 'Вы выбрали расположение текста под углом',0	
     HDC        DWORD ?
     MEMDC      DWORD ?
     XM         DWORD ? 
     YM         DWORD ?
     X          DWORD 0
     Y          DWORD 0
     TEXT       DB 50 DUP (0)
_DATA ENDS 

;Сегмент кода.
_TEXT SEGMENT DWORD PUBLIC USE32 'CODE'
START: 
;Получить дескриптор приложения.
     PUSH  0
     CALL  GetModuleHandleA@4
     MOV   [HINST], EAX
REG_CLASS:
;Заполнить структуру окна.
;Стиль.
     MOV [WC.CLSSTYLE],STYLE 
;Процедура обработки сообщений.
     MOV   [WC.CLWNDPROC], OFFSET WNDPROC
     MOV   [WC.CLSCBCLSEX], 0
     MOV   [WC.CLSCBWNDEX], 0
     MOV   EAX, [HINST]
     MOV   [WC.CLSHINST], EAX
;------------ пиктограмма окна
    PUSH   1
    PUSH   [HINST]
    CALL   LoadIconA@8
    MOV    [WC.CLSHICON], EAX
;------------ курсор окна
    PUSH   2
    PUSH   [HINST]
    CALL   LoadCursorA@8
    MOV    [WC.CLSHCURSOR], EAX
;------------
    
    MOV    [WC.CLBKGROUND], 17   ;Цвет окна.
    MOV    DWORD PTR [WC.CLMENNAME], OFFSET MEN
    MOV    DWORD PTR [WC.CLNAME], OFFSET  CLASSNAME
    PUSH   OFFSET WC
    CALL   RegisterClassA@4
;Создать окно зарегистрированного класса.
    PUSH   0
    PUSH   [HINST]
    PUSH   0
    PUSH   0
    PUSH   400 ;   DY - высота окна.
    PUSH   600 ;   DX - ширина окна.
    PUSH   100 ;  Y - координата левого верхнего угла.
    PUSH   100 ;  X - координата левого верхнего угла.
    PUSH   WS_OVERLAPPEDWINDOW
    PUSH   OFFSET TITLENAME      ;Имя окна.
    PUSH   OFFSET CLASSNAME  ;Имя класса.
    PUSH   0
    CALL    CreateWindowExA@48
;Проверка на ошибку.
    CMP    EAX,0
    JZ    _ERR
    MOV   [NEWHWND], EAX  ;Дескриптор окна.
;Определить идентификатор меню.
    PUSH  EAX
    CALL  GetMenu@4
    MOV   HMENU,EAX
    PUSH  SW_SHOWNORMAL
    PUSH  [NEWHWND]
    CALL  ShowWindow@8      ;Показать созданное окно.
;------------------------------------
    PUSH  [NEWHWND]
    CALL UpdateWindow@4     ;Команда перерисовать видимую
                            ;часть окна, сообщение WM_PAINT.
;Цикл обработки сообщений 
MSG_LOOP:
    PUSH 0
    PUSH 0
    PUSH 0
    PUSH OFFSET MSG
    CALL GetMessageA@16
    CMP  EAX, 0
    JE   END_LOOP
    PUSH OFFSET MSG        
    CALL TranslateMessage@4
    PUSH OFFSET MSG
    CALL DispatchMessageA@4
    JMP  MSG_LOOP 
END_LOOP: 
;Выход из программы (закрыть процесс).
    PUSH  [MSG.MSWPARAM]
    CALL ExitProcess@4
_ERR:
    JMP  END_LOOP
;-----------------------------------------
;Процедура окна.
;Расположение параметров в стеке: 
;[ЕВР+014Н] LPARAM 
;[ЕВР+10Н] WAPARAM 
;[ЕВР+0СН] MES 
;[ЕВР+8] HWND 
WNDPROC PROC
        PUSH EBP
        MOV  EBP, ESP
        PUSH EBX
        PUSH ESI
        PUSH EDI
;Cообщение WM_DESTROY - при закрытии окна.
        CMP  DWORD PTR [EBP+0CH], WM_DESTROY
        JE   WMDESTROY
;Сообщение WM_CREATE - при создании окна.
        CMP  DWORD PTR [EBP+0CH], WM_LBUTTONDOWN
        JE   LBUTTON
        CMP  DWORD PTR [EBP+0CH], WM_CREATE
        JE   WMCREATE
;Сообщение WM_COMMAND -  при событиях 
;с элементами на окне.
        CMP  DWORD PTR   [EBP+0CH],WM_COMMAND
        JE   WMCOMMND
        CMP  DWORD PTR [EBP+0CH], WM_PAINT
        JE   WMPAINT 
;Остальные события возвращаем обратно.
        JMP  DEFWNDPROC
        
LBUTTON:
        PUSH OFFSET TEXT
        PUSH 50               ; получаем строку из поля редактирования
        PUSH WM_GETTEXT
        PUSH HWNDEDT
        CALL SendMessageA@16

        MOV EAX,0H                 
        MOV AX,WORD PTR [EBP+14H]      
        MOV Y,EAX                  ; сохраняем координаты щелчка
        MOV EAX,0H
        MOV AX,WORD PTR [EBP+16H]     
        MOV X,EAX 
        
        
        CMP  SPOSOB, 2
        JE M2
        CMP  SPOSOB, 3    ; проверяем какой был выбран способ вывода текста
        JE M3
        CMP  SPOSOB, 4
        JE M4
        JMP FINISH
M2:
        MOV  lg.LfEscapement,0     ;горизонтально
        JMP  VSE                  
M3:
        MOV  lg.LfEscapement,900   ;вертикально
        JMP  VSE                  

M4:                       ; под углом
        PUSH OFFSET BUF2
        CALL LENSTR       
        MOV  LEN,EBX
        CMP  LEN,0    ;проверяем, введен ли угол
        JNE  DALS
        PUSH 0   
        PUSH OFFSET CAP
        PUSH OFFSET KAS
        PUSH DWORD PTR [EBP+08H]  ;Дескриптор окна.
        CALL MessageBoxA@16
        JMP  FINISH

DALS:
        PUSH OFFSET BUF2
        CALL PEREVOD      ; перевод строки в число
        XOR  EBX,EBX
        MOV  EBX,X1
        MOV  lg.LfEscapement,EBX
VSE:      
        MOV  lg.LfHeight,12      ;Высота шрифта.
        MOV  lg.LfWidth,9        ;Ширина шрифта.
        MOV  lg.LfOrientation,0  ;вертикальная.
        MOV  lg.LfWeight,400     ;Толщина линий шрифта.
        MOV  lg.LfItalic,0       ;курсив
        MOV  lg.LfUnderline,0    ;Подчеркивание.
        MOV  lg.LfStrikeOut,0    ;Перечеркивание.
        MOV  lg.LfCharSet,0      ;Набор шрифтов.
        MOV  lg.LfOutPrecision,0
        MOV  lg.LfClipPrecision,0
        MOV  lg.LfQuality,2
        MOV  lg.LfPitchAndFamily,0
        PUSH OFFSET lg 
;Задать название шрифта.
        PUSH OFFSET NFONT
        PUSH OFFSET lg.LfFaceName        
        CALL COPYSTR
        CALL CreateFontIndirectA@4

;-------------------- выбрать созданный объект
        PUSH EAX
        PUSH MEMDC
        CALL SelectObject@8                         

;-------------------- вывести 
        
        PUSH OFFSET TEXT
	CALL LENSTR                       

        PUSH EBX         ;Длина строки.
        PUSH OFFSET TEXT ;Адрес строки.
        PUSH  X          
        PUSH  Y         
        PUSH MEMDC         ;Контекст окна.
        CALL TextOutA@20   ; выводим текст.     
KONEC:  
        ;Дать команду перерисовать окно.
        PUSH 0         
        PUSH OFFSET RECT
        PUSH DWORD PTR [EBP+08H]
        CALL InvalidateRect@12 
        JMP  FINISH 

WMCOMMND:        
;Проверить, не выбран ли пункт меню MENUC - выход.
        CMP  WORD PTR [EBP+10H],1
        JE   WMDESTROY 
        CMP  WORD PTR [EBP+10H],5
        JE   CHISTO	
        CMP  WORD PTR [EBP+10H],2
        JE   GORIZ
        CMP  WORD PTR [EBP+10H],3
        JE   VERT        
        CMP  WORD PTR [EBP+10H],4
        JE   DIAG      
        JMP  FINISH 
GORIZ:
        MOV  EAX,2
        MOV  SPOSOB,EAX
        PUSH OFFSET PUSTO
        PUSH OFFSET BUF2
        CALL COPYSTR       ;очищаем BUF2
        PUSH 0                   ;MB_OK
        PUSH OFFSET CAP
        PUSH OFFSET S1
        PUSH DWORD PTR [EBP+08H] ;Дескриптор окна.
        CALL MessageBoxA@16
        JMP  FINISH
VERT:
        MOV  EAX,3
        MOV  SPOSOB,EAX
        PUSH OFFSET PUSTO
        PUSH OFFSET BUF2
        CALL COPYSTR       ;очищаем BUF2
        PUSH 0                   ;MB_OK
        PUSH OFFSET CAP
        PUSH OFFSET S2
        PUSH DWORD PTR [EBP+08H] ;Дескриптор окна.
        CALL MessageBoxA@16
        JMP  FINISH
DIAG:
        MOV  EAX,4
        MOV  SPOSOB,EAX
         
        PUSH  0
        CALL  GetModuleHandleA@4
        MOV   [DLHINST], EAX
;-------------------------------------
        PUSH  0
        PUSH  OFFSET DIALPROC
        PUSH  0
        PUSH  OFFSET PA
        PUSH  [DLHINST] 
        CALL  DialogBoxParamA@20 
;-------------------------------------
        CMP   EAX,-1 
        JNE   KOL 
KOL:    JMP MSG_LOOP
        PUSH  0
        CALL  ExitProcess@4
;-------------------------------------
       JMP  FINISH  
	 	 
        
;Заполнить данную прямоугольную область.
CHISTO:
        PUSH RGBW
        CALL CreateSolidBrush@4 ;Создать кисть. 
;Выбрать кисть в данном контексте.
        PUSH EAX
        PUSH MEMDC
        CALL SelectObject@8 
        PUSH 0F00021h ;PATCOPY=заполнить данным цветом.
        PUSH YM
        PUSH XM
        PUSH 0
        PUSH 0
        PUSH MEMDC
        CALL PatBlt@24 
        PUSH 0         
        PUSH OFFSET RECT
        PUSH DWORD PTR [EBP+08H]
        CALL InvalidateRect@12
        JMP  FINISH

WMPAINT:
;--------------------------
        PUSH OFFSET PNT
        PUSH DWORD PTR [EBP+08H]
        CALL BeginPaint@8
        MOV  HDC,EAX ;Сохранить контекст (дескриптор)
;Скопировать виртуальное окно на реальное.
        PUSH 0CC0020h ;SRCCOPY=Изображение как есть
        PUSH 21        ;у источника.
        PUSH 0        ;х источника.
        PUSH MEMDC    ;Контекст источника.
        PUSH YM       ;Высота куда.
        PUSH XM       ;Ширина куда.
        PUSH 21        ;у куда.
        PUSH 0        ;х куда.
        PUSH HDC      ;Контекст куда.
        CALL BitBlt@36
;---------------------- закрыть контекст окна
        PUSH OFFSET PNT 
        PUSH DWORD PTR [EBP+08H] 
        CALL EndPaint@8 
        MOV  EAX, 0 
        JMP  FINISH

WMCREATE:	
        ;Размеры экрана.
        PUSH 0 ;X
        CALL GetSystemMetrics@4 
        MOV  XM,EAX 
        PUSH 1 ;Y
        CALL GetSystemMetrics@4       
	MOV  YM,EAX
;Открыть контекст окна.
        PUSH DWORD PTR [EBP+08H] 
        CALL GetDC@4 
        MOV  HDC,EAX
;Создать совместимый с данным окном контекст. 
        PUSH EAX
        CALL CreateCompatibleDC@4 
        MOV  MEMDC,EAX
;Создать в памяти растровое изображение, совместимое с hdc. 
        PUSH YM 
        PUSH XM 
        PUSH HDC
        CALL CreateCompatibleBitmap@12
;Выбрать растровое изображение в данном контексте. 
        PUSH EAX 
        PUSH MEMDC 
        CALL SelectObject@8
;Цвет кисти
        PUSH RGBW
        CALL CreateSolidBrush@4 ;Создать кисть. 
;Выбрать кисть в данном контексте.
        PUSH EAX
        PUSH MEMDC
        CALL SelectObject@8 
;Заполнить данную прямоугольную область.
        PUSH 0F00021h ;PATCOPY=заполнить данным цветом.
        PUSH YM
        PUSH XM
        PUSH 0
        PUSH 0
        PUSH MEMDC
        CALL PatBlt@24 


;Удалить контекст.
        PUSH HDC
        PUSH DWORD PTR [EBP+08H]
        CALL ReleaseDC@8
        MOV  EAX, 0
        ;Создать окно редактирования.
        PUSH 0
        PUSH [HINST]
        PUSH 0
        PUSH DWORD PTR [EBP+08H]
        PUSH 20   ;DY
        PUSH 150  ;DX
        PUSH 0   ;Y
        PUSH 250   ;X
        PUSH STYLEDT
        PUSH OFFSET CPEDT   ;Имя окна.
        PUSH OFFSET CLSEDIT ;Имя класса.
        PUSH 0
        CALL CreateWindowExA@48
        MOV  HWNDEDT,EAX 
        PUSH HWNDEDT
        CALL SetFocus@4
 
      JMP  FINISH
DEFWNDPROC:	
        PUSH DWORD PTR [EBP+14H]
        PUSH DWORD PTR [EBP+10H]
        PUSH DWORD PTR [EBP+0CH]
        PUSH DWORD PTR [EBP+08H]
        CALL DefWindowProcA@16
        JMP  FINISH

WMDESTROY:	
        PUSH 0
        CALL PostQuitMessage@4   ;Сообщение WM_QUIT.
        MOV  EAX, 0
FINISH:	
        POP  EDI
        POP  ESI
        POP  EBX
        POP  EBP
        RET  16
WNDPROC ENDP


PEREVOD  PROC
PUSH EBP
        MOV  EBP,ESP
        PUSH ESI
        MOV  ESI,DWORD PTR [EBP+08H]
            
        MOV  X1,0
        MOV  X2,0
              
CHIS :  XOR  EBX,EBX 
        CMP  BYTE PTR [ESI],'0' 
        JE P
        INC  EBX 
        CMP  BYTE PTR [ESI],'1'
        JE P
        INC  EBX 
        CMP  BYTE PTR [ESI],'2'
        JE P
        INC  EBX 
        CMP  BYTE PTR [ESI],'3'
        JE P
        INC  EBX 
        CMP  BYTE PTR [ESI],'4'
        JE P
        INC  EBX 
        CMP  BYTE PTR [ESI],'5'
        JE P
        INC  EBX 
        CMP  BYTE PTR [ESI],'6'
        JE P
        INC  EBX 
        CMP  BYTE PTR [ESI],'7'
        JE P
        INC  EBX 
        CMP  BYTE PTR [ESI],'8'
        JE P
        INC  EBX 
        CMP  BYTE PTR [ESI],'9'
          
P:      INC ESI
        MOV A,EBX             
        CMP  LEN,1
        JE   P3
        CMP  LEN,2
        JE   P2
        CMP  LEN,3
        JE   P1
        JMP  EN 

P1:     MOV  EAX,A
        MOV  EDX,100
        IMUL EDX ;Умножение на 100.
        MOV  X1,EAX                    
        DEC  LEN
        JMP  CHIS

P2:     MOV  EAX,A
        MOV  EDX,10
        IMUL EDX ;Умножение на 10.
        MOV  X2,EAX  
        DEC  LEN
        JMP  CHIS

P3:     MOV  EAX,A 
        ADD EAX,X1 ;Прибавим X1.      
        ADD EAX,X2 ;Прибавим X2.
        MOV X1,EAX

        MOV  EAX,X1
        MOV  EDX,10
        IMUL EDX ;Умножение на 10.
        MOV  X1,EAX
      
EN:     POP  ESI
        POP  EBP
        RET  4
PEREVOD  ENDP 


LENSTR  PROC
        PUSH EBP
        MOV  EBP,ESP
        PUSH ESI
        MOV  ESI,DWORD PTR [EBP+8]
        XOR  EBX,EBX
LBL1:
        CMP  BYTE PTR [ESI],0 
        JZ   LBL2 
        INC  EBX 
        INC  ESI 
        JMP  LBL1
LBL2:
        POP  ESI
        POP  EBP
        RET  4
LENSTR  ENDP 


COPYSTR PROC
        PUSH EBP
        MOV EBP,ESP
        MOV ESI,DWORD PTR [EBP+0CH]
        MOV EDI,DWORD PTR [EBP+08H]
L1:
        MOV AL, BYTE PTR [ESI]
        MOV BYTE PTR [EDI], AL
        CMP AL,0
        JE L2
        INC ESI
        INC EDI
        JMP L1
L2:	
        POP EBP
        RET 8
COPYSTR ENDP

DIALPROC PROC
        PUSH EBP
        MOV  EBP, ESP
        PUSH EBX
        PUSH ESI
        PUSH EDI
;-------------------------------------
        
        CMP  DWORD PTR [EBP+0CH],WM_INITDIALOG
        JE   MET1
        CMP  DWORD PTR [EBP+0CH] ,WM_COMMAND 
        JE   COM
MET1:
        MOV  EAX,0
        JMP  KONETC
COM:
        CMP  WORD PTR [EBP+10H], 6 
        JE   DELAJ        
        CMP  WORD PTR [EBP+10H], 7 
        JE   EXT2
        CMP  DWORD PTR [EBP+10H], 8 
        JE   SHOW  
        JMP  KONETC
DELAJ:    
        PUSH OFFSET BUF
        PUSH 20
        PUSH WM_GETTEXT
        PUSH 6
        PUSH DWORD PTR [EBP+08H]
        CALL SendDlgItemMessageA@20 
        JMP  KONETC   
SHOW:  
        PUSH OFFSET BUF
        CALL LENSTR
        CMP  EBX,0
        JE   NEVVOD
        PUSH OFFSET BUF
        PUSH OFFSET BUF2
        CALL COPYSTR
        PUSH 0   
        PUSH OFFSET CAP2
        PUSH OFFSET BUF2
        PUSH DWORD PTR [EBP+08H]  ;Дескриптор окна.
        CALL MessageBoxA@16
        JMP  EXT2 

NEVVOD:
        PUSH 0   
        PUSH OFFSET CAP
        PUSH OFFSET KAS
        PUSH DWORD PTR [EBP+08H]  ;Дескриптор окна.
        CALL MessageBoxA@16
        JMP  KONETC 
EXT2:
        PUSH 0
        PUSH DWORD PTR [EBP+08H]
        CALL EndDialog@8                      
              
KONETC:	
        POP  EDI
        POP  ESI
        POP  EBX
        POP  EBP
        RET  16       
DIALPROC ENDP


_TEXT   ENDS	
        END START

Текст этого приложения вместе с дополнительными файлами можно взять здесь.

    Результат работы приложения изображен на рисунке 1:


Рис.1. Результат работы приложения

    Перечислим используемые процедуры.

  1. PEREVOD - осуществляет перевод строки в число (строка должна содержать только цифры; длина строки <=3).
  2. LENSTR - определяет длину строки.
  3. COPYSTR - копирует одну строку в другую.

    Приведем краткое описание приложения.

    В процедуре окна обрабатываются следующие сообщения:

    Меню:

    Диалоговое окно:

  1. При вводе угла в строку редактирования он копируется в строку BUF.
  2. При нажатии кнопки "ОК" проверяется длина строки BUF:
    • если длина BUF=0, то выводится сообщение : "Вы не ввели угол!";
    • иначе содержимое строки BUF копируется в BUF2 и выводится сообщение: "Вы ввели угол : … ". Диалоговое окно закрывается.
  3. При нажатии кнопки "Выход" диалоговое окно закрывается.

    При щелчке левой кнопкой мыши:

    Со следующего шага мы дадим краткий обзор отладчиков и дизассемблеров.




Предыдущий шаг Содержание Следующий шаг