IPB
ЛогинПароль:

> Программирование под Win32
сообщение
Сообщение #1


Пионер
**

Группа: Пользователи
Сообщений: 69
Пол: Мужской
Реальное имя: Вася Пупкин

Репутация: -  1  +


Решил написать про программирование на асме под Win32.
И так сегодня в номере smile.gif мы разберем создание окна и всё sad.gif

Мы разберем простую программу, которая выводит только окно.
Я взял пример программы Wap32.asm из пакета TASM и несколько упростил ее.
.386
.model flat, stdcall
include win32.inc


Файл win32.inc содержит некоторые нужные константы и структуры


extrn            CreateWindowExA:PROC
extrn            DefWindowProcA:PROC
extrn            DispatchMessageA:PROC
extrn            ExitProcess:PROC
extrn            GetMessageA:PROC
extrn            GetModuleHandleA:PROC
extrn            LoadCursorA:PROC
extrn            LoadIconA:PROC
extrn            PostQuitMessage:PROC
extrn            RegisterClassA:PROC
extrn            ShowWindow:PROC
extrn            TranslateMessage:PROC
extrn            UpdateWindow:PROC
.data
newhwnd          dd 0
msg              MSGSTRUCT   <?>
wc               WNDCLASS    <?>
hInst            dd 0
szTitleName      db 'Win32 Assembly Program',0
szClassName      db 'ASMCLASS32',0
.code
start:
       push    0
       call    GetModuleHandleA
       mov     [hInst], eax



Получим дескриптор программы.
Далее инициализируем структуру WndClass для регистрации окна

       mov     [wc.clsStyle], CS_HREDRAW + CS_VREDRAW + CS_GLOBALCLASS


clsStyle - определяет стиль класса

       mov     [wc.clsLpfnWndProc], offset WndProc


clsLpfnWndProc - указывает на процедуру окна

       mov     [wc.clsCbClsExtra], 0
       mov     [wc.clsCbWndExtra], 0
       mov     eax, [hInst]
       mov     [wc.clsHInstance], eax


clsHInstance - содержит дескриптор программы

       push    IDI_APPLICATION
       push    0
       call    LoadIconA
       mov     [wc.clsHIcon], eax
       push    IDC_ARROW
       push    0
       call    LoadCursorA
       mov     [wc.clsHCursor], eax
       mov     [wc.clsHbrBackground], COLOR_WINDOW + 1
       mov     dword ptr [wc.clsLpszMenuName], 0
       mov     dword ptr [wc.clsLpszClassName], offset szClassName


clsLpszClassName - определяет имя класса окна

       push    offset wc
       call    RegisterClassA



Создаем окно:

       push    0
       push    [hInst]                 ; дескриптор окна
       push    0
       push    0
       push    CW_USEDEFAULT           ; высота
       push    CW_USEDEFAULT           ; ширина
       push    CW_USEDEFAULT           ; y
       push    CW_USEDEFAULT           ; x
       push    WS_OVERLAPPEDWINDOW     ; стиль
       push    offset szTitleName      ; заголовок окна
       push    offset szClassName      ; имя класса
       push    0                       ; дополнительный стиль
       call    CreateWindowExA
       mov     [newhwnd], eax


newhwnd - дескриптор окна
Покажем окно:

       push    SW_SHOWNORMAL
       push    [newhwnd]
       call    ShowWindow


Обновим окно:

       push    [newhwnd]
       call    UpdateWindow


Создаем цикл для обработки сообщений окна

msg_loop:
       push    0
       push    0
       push    0
       push    offset msg
       call    GetMessageA
       cmp     ax, 0
       je      end_loop
       push    offset msg
       call    TranslateMessage
       push    offset msg
       call    DispatchMessageA
       jmp     msg_loop
end_loop:


выход из программы:

       push    [msg.msWPARAM]
       call    ExitProcess



Процедура окна:

WndProc proc uses ebx edi esi, hwnd:DWORD, wmsg:DWORD,
wparam:DWORD, lparam:DWORD

Win32 требует, чтобы EBX, EDI, и ESI были сохранены

       cmp     [wmsg], WM_DESTROY
       je      wmdestroy
       push    [lparam]
       push    [wparam]
       push    [wmsg]
       push    [hwnd]
       call    DefWindowProcA
       jmp     finish
wmdestroy:
       push    0
       call    PostQuitMessage
       mov     eax, 0
finish:
       ret
WndProc          endp
ends
end start

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

А в следующий раз мы разберём что нибудь посложнее...


--------------------
Стабильность - признак мастерства
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
 
 Ответить  Открыть новую тему 
Ответов
сообщение
Сообщение #2


Пионер
**

Группа: Пользователи
Сообщений: 69
Пол: Мужской
Реальное имя: Вася Пупкин

Репутация: -  1  +


Сегодня я напишу о работе с клавиатурой.
Как привило, у каждого компьютера есть только одна клавиатура, поэтому все запущенные Windows программы должны разделять её между всеми. Windows ответственна за то, чтобы отсылать информацию о нажатых клавишах активному в данный момент окну.

Хотя на экране может быть сразу несколько окон, только одно из них имеет фокус ввода, и только оно может получать сообщения от клавиатуры. Вы можете отличить окно, которое имеет фокус ввода от окна, которое его не имеет, посмотрев на его title bar - он будет подсвечен, в отличии от других.
В действительности, есть два типа сообщений от клавиатуры, зависящих от того, чем вы считаете клавиатуру. Вы можете считать ее набором кнопок. В этом случае, если вы нажмете кнопку, Windows пошлет сообщение WM_KEYDOWN активному окну, уведомляя о нажатии клавиши. Когда вы отпустите клавишу, Windows пошлет сообщение WM_KEYUP. Вы думаете о клавише как о кнопке. Другое взгляд на клавиатуру предполагает, что это устройство ввода символов. Тогда, Windows шлет сообщения WM_KEYDOWN или WM_KEYUP окну, в котором есть фокус ввода, и эти сообщения будут транслиpованы в сообщение WM_CHAR функцией TranslateMessage. Процедура окна может обрабатывать все три сообщения или только то, в котором оно заинтересовано. Большую часть времени вы можете игнорировать WM_KEYDOWN и WM_KEYUP, так как вызов функции TranslateMessage в цикле обработки сообщений транслирует сообщения WM_KEYDOWN и WM_KEYUP в WM_CHAR. Мы будем опираться именно на это сообщение в данном уроке.
   .386
  .model flat,stdcall
  option casemap:none
  WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
  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
  .data
  ClassName db "SimpleWinClass",0
  AppName  db "Our First Window",0
  char WPARAM 20h                        ; the character the program
  receives from keyboard
  .data?
  hInstance HINSTANCE ?
  CommandLine LPSTR ?
  .code
  start:
      invoke GetModuleHandle, NULL
      mov    hInstance,eax
      invoke GetCommandLine
      mov CommandLine,eax
      invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
      invoke ExitProcess,eax
  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  hInst
      pop   wc.hInstance
      mov   wc.hbrBackground,COLOR_WINDOW+1
      mov   wc.lpszMenuName,NULL
      mov   wc.lpszClassName,OFFSET ClassName
      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
      invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
             WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
             CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
             hInst,NULL
      mov   hwnd,eax
      invoke ShowWindow, hwnd,SW_SHOWNORMAL
      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
  WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
      LOCAL hdc:HDC
      LOCAL ps:PAINTSTRUCT
      .IF uMsg==WM_DESTROY
          invoke PostQuitMessage,NULL
      .ELSEIF uMsg==WM_CHAR
          push wParam
          pop  char
          invoke InvalidateRect, hWnd,NULL,TRUE
      .ELSEIF uMsg==WM_PAINT
          invoke BeginPaint,hWnd, ADDR ps
          mov    hdc,eax
          invoke TextOut,hdc,0,0,ADDR char,1
          invoke EndPaint,hWnd, ADDR ps
      .ELSE
          invoke DefWindowProc,hWnd,uMsg,wParam,lParam
          ret
      .ENDIF
      xor    eax,eax
      ret
  WndProc endp
  end start


Теперь подробнее:

   char WPARAM 20h  ; символ, который программа получает от клавиатуры


Это переменная, в которой будет сохраняться символ, получаемый от клавиатуры. Так как символ шлется в WPARAM процедуры окна, мы для простоты определяем эту переменную как обладающую типом WPARAM. Начальное значение - 20h или "пробел", так как когда наше окно обновляет свою клиентскую область в первое время, символ еще не введен, поэтому мы делаем так, чтобы отображался пробел.
       .ELSEIF uMsg==WM_CHAR
          push wParam
          pop  char
          invoke InvalidateRect, hWnd,NULL,TRUE

Это было добавлено в процедуру окна для обработки сообщения WM_CHAR. Она всего лишь помещает символ в переменную char и затем вызывает InvalidateRect, что вынуждает Windows послать сообщение WM_PAINT процедуре окна. Синтаксис этой функции следующий:
   InvalidateRect proto hWnd:HWND, lpRect:DWORD, bErase:DWORD

lpRect - указатель на пpямоугольник в клиентской области, котоpый мы хотим объявить тpебующим пеpеpисовки. Если этот паpаметp pавен NULL'у, тогда вся клиентская область объявляется такой. bErase - флаг, говоpящий Windows, нужно ли уничтожать бэкгpаунд. Если он pавен TRUE, тогда она делает это пpи вызове функции BeginPaint.
Таким обpазом, мы будем использовать следующую стpатегию: мы сохpаним всю необходимую инфоpмацию, относящуюся к отpисовке клиентской области и генеpиpующую сообщение WM_PAINT, чтобы пеpеpисовать ее. Конечно, код в секции WM_PAINT должен знать заpанее, что от него ожидают. Это кажется обходным путем делать дела, но это путь Windows.
Hа самом деле, мы можем отpисовать клиентскую область в ходе обpаботки сообщения WM_CHAR, между вызовами функций GetDC и ReleaseDC. Hет никаких пpоблем с этим. Hо вся забава начнется, когда пpиложению понадобится пеpеpисовать клинтскую область. Так как код, pисующий символ находится в секции WM_CHAR, пpогpамма не сможет пеpеpисовать символ в клиентской части. Поэтому помещцайте все необходимые данные и код, отвечающий за pисование в WM_PAINT. Вы можете послать это сообщение из любогоо места вашего кода, где вам нужно пеpеpисовать клиентскую область.
 invoke TextOut,hdc,0,0,ADDR char,1

Когда InvalidateRect вызвана, она шлет сообщение WM_PAINT обpатно пpоцедуpе окна, поэтому вызывается код в секции WM_PAINT. Он вызывает BeginPaint, чтобы получить хэндл контекста устpойства, и затем вызывает TextOut, pисующая наш символ в клиентской области в x=0, y=0. Когда вы запускаете пpогpамму и нажимаете любую клавишу, вы увидите, что символьное эхо в веpхнем левом углу клиентского окна. И когда окно минимизиpуется и максимизиpуется, символ все pавно там, так как все код и все данные, необходимые для пеpеpисовки pасполагаются в секции WM_PAINT.

В следующих статьях я напишу про мышь, меню, дочерние окна или месаги.

Сообщение отредактировано: volvo -


--------------------
Стабильность - признак мастерства
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 

Сообщений в этой теме


 Ответить  Открыть новую тему 
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 





- Текстовая версия 27.04.2024 21:31
500Gb HDD, 6Gb RAM, 2 Cores, 7 EUR в месяц — такие хостинги правда бывают
Связь с администрацией: bu_gen в домене octagram.name