Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Новичок: проблема с программой под Win32  (Прочитано 8846 раз)
0 Пользователей и 1 Гость смотрят эту тему.
sawych
Гость
« : 24-08-2005 22:31 » 

Написал сюда, потому что мне нужна помощь, у меня куча вопросов.
Очень надеюсь на ваше понимание и помощь, и пожалуйста не отсылайте меня к мануалу как это делают на некоторых форумах, если вы сами в силах мне помочь.
Начну с самого главного:
Я сразу скажу что в программировании на Ассемблере я новичек в общем освоил я основы (вроде), правда есть некоторые вопросы из-за дурацких высокоуровневых директив. Вот начал изучение Asm под Win32 и начал я с уроков Iczelion`на. И вот на 5 уроке у меня возникла проблема.
Дело в том что я не использую директив ветвления (не хочу), а пользуюсь стандартными средствами Ассемблера. Вот переписал программу из 5 урока и запустил.
Программа выводит текст и тут же вызывает ошибку (которую мелкософт предлагает оправить). Смотрел в отладчике, ошибка вроде заключается в том что программа пытается вернутся (ret) от куда-то на адрес 00000000 (то есть ошибка из-за стэка?).
Причем если переменную для временного сохранения хэндла логического шрифта сделать не локальной то есть не в стэке, а поместить в секции .data?, то все работает как надо. Пожалуйста, кто-нибудь может объяснить чайнику почему так происходит?
Вот исходник:


Код:
.386
.model flat,stdcall


include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\gdi32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\gdi32.lib

RGB macro red,green,blue
        xor eax,eax
        mov ah,blue
        shl eax,8
        mov ah,green
        mov al,red
endm


.DATA

class_name         db      "win_class",0
window_name        db      "Title",0
font_name          db      "script",0
out_string         db      "Win32 assembly is great and easy!",0
; структура, описывающая класс окна.
wc WNDCLASSEX <4*12,CS_HREDRAW or CS_VREDRAW,offset win_proc,0,0,?,?,?,\
                         COLOR_WINDOW+1,0,offset class_name,0>
                         
.DATA?

msg_ MSG <>        ; структура, в которой возвращается
                   ; сообщение после GetMessage
ps PAINTSTRUCT <>  ; структура для граф. функций

.CODE

_start:
        xor        ebx,ebx          ; в EBX будет 0 для команд push 0
                                    ; (короче в 2 раза)
; определим идентификатор нашей программы
        push       ebx
        call       GetModuleHandle
        mov        esi,eax          ; и сохраним его в ESI
; заполним и зарегестрируем класс
        mov        dword ptr wc.hInstance,eax ; идентификатор предка
; выберем иконку
        push       IDI_APPLICATION  ; стандартная иконка приложения
        push       ebx              ; идентификатор модуля с иконкой
        call       LoadIcon
        mov        wc.hIcon,eax     ; идентификатор иконки для нашего класса
; выберем форму курсора
        push       IDC_ARROW        ; стандартная стрелка
        push       ebx              ; идентификатор модуля с курсором
        call       LoadCursor
        mov        wc.hCursor,eax   ; идентификатор курсора для нашего класса
        push       offset wc
        call       RegisterClassEx  ; зарегистрируем класс
; создадим окно
        mov        ecx,CW_USEDEFAULT    ; push ecx короче push N в пять раз
        push       ebx              ; адрес структуры CREATESTRUCT (здесь NULL)
        push       esi              ; идентификатор процесса, который будет получать
                                    ; сообщения от окна (то есть, наш)
        push       ebx              ; идентификатор меню или окна-потомка
        push       ebx              ; идентификатор окна-предка
        push       ecx              ; высота (CW_USEDEFAULT - по умолчанию)
        push       ecx              ; ширина (по умолчанию)
        push       ecx              ; y-координата (по умолчанию)
        push       ecx              ; x-координата (по умолчанию)
        push       WS_OVERLAPPEDWINDOW     ; стиль окна
        push       offset window_name      ; заголовок окна
        push       offset class_name       ; любой зарегистрированный класс
        push       ebx              ; дополнительный стиль
        call       CreateWindowEx   ; создать окно (eax - идентификатор окна)
        push       eax              ; идентификатор для UpdateWindow
        push       SW_SHOWNORMAL    ; тип показа для для ShowWindow
        push       eax              ; идентификатор для ShowWindow
; больше идентификатор окна нам не потребуется
        call       ShowWindow       ; показать окно
        call       UpdateWindow     ; и послать ему сообщение WM_PAINT

; основной цикл - проверка сообщений от окна и выход по WM_QUIT
        mov        edi,offset msg_  ; push edi короче push N в 5 раз
message_loop:
        push       ebx              ; последнее сообщение
        push       ebx              ; первое сообщение
        push       ebx              ; идентификатор окна (0 - любое наше окно)
        push       edi              ; адрес структуры MSG
        call       GetMessage       ; получить сообщение от окна с ожиданием
                                    ; - не забывайте использовать PeekMessage
                                    ; если нужно в этом цикле что-то выполнять
        test       eax,eax          ; если получено WM_QUIT
        jz         exit_msg_loop    ; выйти
        push       edi              ; иначе - преобразовать сообщения типа
        call       TranslateMessage ; WM_KEYUP в сообщения типа WM_CHAR
        push       edi
        call       DispatchMessage  ; и послать их процедуре окна (иначе его просто
                                    ; нельзя будет закрыть)
        jmp        message_loop    ; продолжить цикл
exit_msg_loop:
; выход из программы
        push       ebx
        call       ExitProcess
;////////////////////////////////////////////////////////////////////////
; процедура win_proc
; процедура не должна изменять регистры EBP,EDI,ESI и EBX
;
win_proc proc
; так как мы получаем параметры в стеке, построим стековый кадр
        push       ebp
        mov        ebp,esp

        push       ebx ; функция должна сохр. этот регистр,
                       ; а мы его используем поэтому сохраняем его
; процедура типа WindowProc вызывается со следующими параметрами
        hWnd    equ dword ptr [ebp+08h]  ; идентификатор окна
        uMsg    equ dword ptr [ebp+0Ch]  ; номер сообщения (код сообщеиия)
        wParam  equ dword ptr [ebp+10h]  ; первый параметр
        lParam  equ dword ptr [ebp+14h]  ; второй параметр
; локальные переменные
        sub        esp,8 ; 2 X dword
        hdc     equ dword ptr [ebp+18h] ; хэндл контекста устройства (для граф. функций)
        hfont   equ dword ptr [ebp+1Ch] ; переменная для сохранения (восст.) измененяемого шрифта
                                        ; вот если эту переменную объявит не тут а в .data?
                                        ; то все работает
; если мы получили сообщение WM_DESTROY (оно означает что окно уже удалили
; с экрана, нажав alt-F4 или кнопку в верхнем правом углу)
; то пошлем нам же сообщение WM_QUIT
        cmp        uMsg,WM_DESTROY
        jne        not_wm_destroy
        push       0                    ; код выхода
        call       PostQuitMessage      ; послать WM_QUIT
        jmp        end_wm_check   ; и выйти из процедуры
  not_wm_destroy:
        cmp        uMsg,WM_PAINT ; сообщение WM_PAINT ?
        jne        not_wm_paint  ; Нет - прыгаем дальше
  ; получим хэндл контекста устр-ва
        mov        ebx,offset ps ; push ebx быстрее чем push N
        push       ebx
        push       hWnd          ; хэндл окна в кот. будем рисовать
        call       BeginPaint          ; в eax хэндл контекста устр-ва
        mov        hdc, eax
  ; создадим логический шрифт и получим его хэндл
        mov        ebx,offset font_name ; push ebx быстрее чем push N
        push       ebx             ; смещение строки названия гарнитуры шрифта
        mov        ebx,DEFAULT_PITCH or FF_SCRIPT
        push       ebx             ; питч и семейство шрифта (объед. or' ом)
        push       DEFAULT_QUALITY ; качества вывода
        push       CLIP_DEFAULT_PRECIS  ; что делать с символами, котоpые
                                        ; вылезают за пpеделы отpисовочного pегиона
        push       OUT_DEFAULT_PRECIS   ; насколько должен близко должен
                                        ;пpиближаться фонт к хаpактеpистикам,
                                        ; котоpые мы указали
        push       OEM_CHARSET          ; символьный набоp фонта]
        mov        ebx,0                ; push ebx быстрее чем push N
        push       ebx                  ; признак ПЕРЕчеркнутости
        push       ebx                  ; признак ПОДчеркнутости
        push       ebx                  ; признак курсива
        push       400                  ; устанавливает толщину линии
                                        ; 400 - нормальная толщина
        push       ebx                  ; насколько символ должен быть повеpнут (0.1 deg)
        push       ebx                  ; указывает оpиентацию вывода следующего символа,
                                        ; относительно пpедыдущего
        push       16                   ; желаемая шиpина символов
        push       24                   ; желаемая высота символов
        call       CreateFont           ; eax = хэндл логического шрифта
  ;     выберем наш шрифт для контекста устр-ва
        push       eax                  ; хэндл логического шрифта
        push       hdc                  ; хэндл контекста устр-ва
        call       SelectObject
  ;     сохраним хэндл предидущего шрифта (он в eax)
        mov        hfont, eax
  ;     установим цвет шрифта
        RGB       200,200,50            ; цвет
        push       eax
        push       hdc
        call       SetTextColor         ; устанавливаем цвет текста
  ;     установим цвет фона
        RGB        0,0,255              ; цвет
        push       eax
        push       hdc
        call       SetBkColor           ; устанавливаем цвет фона
  ; нарисуем текст в окне
        push       SIZEOF out_string    ; длина строки для вывода
        push       offset out_string    ; смещение строки
        mov        ebx,0
        push       ebx                  ; Y координата
        push       ebx                  ; X координата
        push       hdc
        call       TextOut              ; нарисуем текст
  ; восстановим старый хэндл шрифта
        push       hfont                ; сохраненный хэндл старого шрифта
        push       hdc                  ; хэндл контекста устройства
        call       SelectObject
  ; освободим хэндл контекста устр-ва
        mov        ebx,offset ps ; push ebx быстрее чем push N
        push       ebx
        push       hWnd
        call       EndPaint
  not_wm_paint:
; если мы получили другое сообщение - вызовем его обработчик по умолчанию
        pop ebx ; мы его сохраняли
        leave                       ; восстановим ebp
        jmp        DefWindowProc    ; и вызовем DefWindowProc с нашими параметрами
  end_wm_check:
        pop ebx ; мы его сохраняли
        leave                       ; восстановим ebp
        ret        16               ; и вернемся сами, очистив стек от параметров
win_proc endp
;////////////////////////////////////////////////////////////////////////
        end        _start

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

Some_Proc proc
..
Some_Proc endp

а вот так:

Some_Proc proc param1:DWORD, param2:DWORD
..
Some_Proc endp

и обращаться к переданым параметрам не через стэк, а прямо по именам?
И надо ли после такого объявления указывать в ret кол-во удаляемых из стэка байт?
И зачем нужен прототип процедуры? Только для того чтобы ее вызывать через invoke
или это еще что-то дает, и почему когда используют высокоуровневые директивы
не пишут пролога и эпилога процедуры и не указывают кол-во удаляемых байт из стэка в ret?
Надеюсь на вашу помощь =)
« Последнее редактирование: 18-12-2007 21:58 от Алексей1153++ » Записан
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #1 : 25-08-2005 07:38 » 

Цитата
Код:
        push       ebp
        mov        ebp,esp

        push       ebx ; функция должна сохр. этот регистр,
                       ; а мы его используем поэтому сохраняем его
; процедура типа WindowProc вызывается со следующими параметрами
        hWnd    equ dword ptr [ebp+08h]  ; идентификатор окна
        uMsg    equ dword ptr [ebp+0Ch]  ; номер сообщения (код сообщеиия)
        wParam  equ dword ptr [ebp+10h]  ; первый параметр
        lParam  equ dword ptr [ebp+14h]  ; второй параметр
; локальные переменные
        sub        esp,8 ; 2 X dword
        hdc     equ dword ptr [ebp+18h] ; хэндл контекста устройства (для граф. функций)
        hfont   equ dword ptr [ebp+1Ch] ; переменная для сохранения (восст.) измененяемого шрифта
                                        ; вот если эту переменную объявит не тут а в .data?
                                        ; то все работает
Тут ошибка - локальные переменные следует адресовать с отрицательным смещением к EBP:
hdc EQU dword ptr [EBP-4]
hfont EQU dword ptr [EBP-8]
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
sawych
Гость
« Ответ #2 : 25-08-2005 10:39 » 

Спасибо, попробую..
Записан
sawych
Гость
« Ответ #3 : 25-08-2005 20:54 » 

Тут ошибка - локальные переменные следует адресовать с отрицательным смещением к EBP:
hdc EQU dword ptr [EBP-4]
hfont EQU dword ptr [EBP-8]

Спасибо, ошибку исправил все работает =)
Записан
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #4 : 26-08-2005 12:10 » new

Главное - понять смысл:
mov ebp,esp ; EBP = ESP
push ebx  ; EBP = ESP - 4
sub esp,8 ; EBP = ESP - 12
Т.е.: по адресу ESP + 4 выделено 8 байт и адресовать их можно через [ESP + 4 + смещение] или [EBP - 8 + смещение]..
Отсюда и [EBP-4] и [EBP-8].
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines