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. Это даже проще, чем использование библиотеки BGI от Borland для DOS. Особенно просто работать с графикой с помощью специальных классов, предлагаемых объектными библиотеками. И в то же время многие действительно мощные возможности остаются зачастую не использованными.
Начнем с примера (дурного). Допустим мы хотим вывести в клиентской части окна график функции sin(x). Вот участок кода для C++Builder, реализующий задачу:
void __fastcall TForm1::FormPaint(TObject *Sender) 
{
Canvas -> MoveTo(10, 0);
Canvas -> LineTo(10, ClientHeight);
Canvas -> MoveTo(0, ClientHeight/2);
Canvas -> LineTo(ClientWidth, ClientHeight/2);
Canvas -> MoveTo(10, ClientHeight/2);
for (int i = 0; i < ClientWidth - 10; i += 2)
Canvas -> LineTo(i + 10, ClientHeight/2*sin(i*4*3.1416/ClientWidth) + ClientHeight/2 );
}

С первого взгляда код абсолютно не читаемый, в основном из-за постоянных вычислений координат. Задумаемся над таким положением: если бы удалось совместить начало и направление осей координат графика нашей функции и устройства вывода, код бы заметно упростился. Еще большее упрощение кода произошло бы, если бы мы не были жестко привязаны к размерам окна приложения. Но ведь GDI все это позволяет сделать! Существует набор функций, устанавливающих различные режимы преобразования логических координат в физические. В следующем маленьком раздельчике мы посмотрим как это можно сделать. И еще одно замечание. Приведенный пример не такой уж и дурной, особенно когда знаешь, как можно сделать лучше…

Масштабируемый вывод графики

Для работы нам понадобятся следующие функции:
SetMapMde - установка режима вывода;
SetWindowExtEx - установка логических размеров области вывода;
SetViewportExtEx - установка физического размера (в пикселях) области вывода;
SetViewportOrgEx - установка точки начала отсчета.
Подробностей о личной жизни этих функций приводить не будем, так как вся необходимая и достаточная информация присутствует в справочной системе. Вместо этого приведем новый вариант кода:
#define W 500 
#define H 500

SetMapMode(hdc, MM_ANISOTROPIC);
SetWindowExtEx(hdc, W, H, 0);
SetViewportExtEx(hdc, ClientWidth, -ClientHeight, 0);
SetViewportOrgEx(hdc, 0, ClientHeight/2, 0);
MoveToEx(hdc, 0, -H/2, 0);
LineTo(hdc, 0, H/2);
MoveTo(hdc, 0, 0);
LineTo(hdc, W, 0);
MoveToEx(hdc, 0, 0, 0);
for (int i = 0; i < W; i += 2) LineTo(hdc, i, H/2*sin(i*4*3.1416/W));


На мой взгляд, код стал более понятным и читабельным, особенно если знать, как работают четыре функции, начинающиеся на Set. Но как мы и договаривались, на функциях мы не останавливаемся! Замечу только следующее:
  1. Перевернуть координату Y для графической системы нам позволил знак минус у третьего параметра функции SetViewportExtEx (фактически мы этим сказали графической системе, что мы будем рисовать "вверх ногами" и что ей придется не только масштабировать графику при выводе, но еще и выворачивать ее обратно с головы на ноги, зато нам так удобнее!)
  2. Не все устройства поддерживают операции масштабирования, однако все современные видеокарты и принтеры умеют делать это. Если Вы работаете с каким-то нестандартным устройствам, можно у него поинтересоваться о его скрытых талантах путем вызова GetDeviceCaps(hdc, RASTERCAPS) Я надеюсь, приведенный пример вдохновил Вас на дальнейшее изучение богатых возможностей GDI. Следующим пунктом нашей программы будет вывод графики на принтер.

Вывод графики на принтер
Вопрос из категории FAQ: "почему при выводе на принтер картинка стала совсем крошечной?". Ответ очевиден, достаточно сравнить разрешение экрана монитора и принтера. Современные принтеры имеют разрешение как минимум в десять раз выше, чем такие же современные мониторы. Посему на экране линия длиной в 100 пикселей, кажется вполне убедительной, а при выводе той же линии на принтер получается жалкий штрих. Те, кто прочитали предыдущий раздел, вероятно уже догадались, что на помощь опять придет масштабируемый вывод графики. Посмотрим, как это делается:
#define W 500 
#define H 500

HDC hPrint;
//Каким либо образом получаем дескриптор котекста принтера - например с помощью CreateDC или
//PrintDlg()

int pix_prn_x = GetDeviceCaps(hPrint, HORZRES );
int pix_prn_y = GetDeviceCaps(hPrint, VERTRES );
SetMapMode(hPrint, MM_ANISOTROPIC);
SetWindowExtEx(hPrint, W, H, 0);
SetViewportExtEx(hPrint, pix_prn_x, -pix_prn_y, 0);
SetViewportOrgEx(hPrint, 0, pix_prn_y/2, 0);
MoveToEx(hPrint, 0, -H/2, 0);
LineTo(hPrint, 0, H/2);
MoveToEx(hPrint, 0, 0, 0);
LineTo(hPrint, W, 0);
MoveToEx(hPrint, 0, 0, 0);
for (int i = 0; i < W; i += 2) LineTo(hPrint, i, H/2*sin(i*4*3.1416/W));

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

Вывод текста на принтер
Часто при выводе текста на экран монитора пользуются режимом отображения MM_TEXT. При этом логическая единица равна физическому пикселю. Таким образом, строка текста, напечатанная шрифтом 10pt, на экране будет иметь высоту ровно 10 пикселей. Если в том же режиме MM_TEXT попытаться вывести текст на принтер, то не трудно догадаться, что высота строки будет во много раз меньше, чем 10pt (напомним, что 1pt = 1/72 дюйма). Первый выход состоит в изменении размера шрифта. Для TrueType шрифтов это можно сделать, однако при использовании растровых шрифтов подобный метод в общем случае не применим. Попробуем воспользоваться уже проверенным нами в предыдущих примерах масштабируемым выводом графики. Для того, чтобы текст был выведен на принтер с соблюдением его истинного размера в пунктах, примем за логическую единицу один пункт шрифта.
HDC hPrint; 
//Каким либо образом получаем дескриптор контекста принтера - например с помощью CreateDC или
//PrintDlg()

int pix_prn_x = GetDeviceCaps(hPrint, HORZRES );
int pix_prn_y = GetDeviceCaps(hPrint, VERTRES );
int mm_prn_x = GetDeviceCaps(hPrint, HORZSIZE );
int mm_prn_y = GetDeviceCaps(hPrint, VERTSIZE );
int W = mm_prn_x*72/25.4;
int H = mm_prn_y*72/25.4;

HFONT hFont = CreateFont(14, 0, 0, 0, FW_MEDIUM , false, false, false,
DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH, "Arial");
HFONT hOldFont = SelectObject(hPrint, hFont);

SetMapMode(hPrint, MM_ANISOTROPIC);
SetWindowExtEx(hPrint, W, H, 0);
SetViewportExtEx(hPrint, pix_prn_x, pix_prn_y, 0);
SetViewportOrgEx(hPrint, 0, 0, 0);

TextOut(hPrint, 10, i*H/50 + 10, "1234567890 Проверка Test", 24);

SelectObject(hPrint, hOldFont);
DeleteObject(hFont);

Использование регионов отсечения для получения визуальных эффектов
Данный пример взят из книги Майкла Янга "Программирование графики в Windows 95. Векторная графика на языке С++".

Если все рассмотренные выше приемы были связаны одной идеей использования масштабируемого графического вывода, то последний иллюстрирует совершенно другой механизм. Мы рассмотрим один интересный способ получения градиентной закраски сложных объектов на примере текста. Следуя нашим договоренностям, ни слова о том, что такое регионы отсечения и как ими пользоваться. Все внимание следует сосредоточить на том факте, что регион отсечения можно создать из пути (path), а путь может состоять из блока текста. Учитывая это, следующий код должен быть абсолютно понятным.
HDC hdc; 
//Каким либо образом получаем дескриптор контекста - BeginPaint, GetDC и.т.д

HFONT hFont = CreateFont(50, 20, 0, 0, FW_BOLD, false, false, false,
DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH, "Arial");
HFONT hOldFont = SelectObject(hdc, hFont);
SetBkMode(hdc, TRANSPARENT);
BeginPath(hdc);
TextOut(hdc, 100, 100, "FADE", 4);
EndPath(hdc);
SelectClipPath(hdc, RGN_COPY);
SIZE s;
GetTextExtentPoint32(hdc, "FADE", 4, &s);
HBRUSH hbr;
RECT R = {100, 0, 100 + s.cx, 0};
int clr = 0;
for (int i = 100; i < 100 + s.cy; i+= 2)
{
hbr = CreateSolidBrush( RGB(clr, clr, clr));
clr += 10;
R.top = i;
R.bottom = i + 2;
FillRect(hdc, &R, hbr);
DeleteObject(hbr);
}
SelectObject(hdc, hOldFont);
DeleteObject(hFont);

//Обязательно нужно освободить контекст EndPaint, ReleaseDC - смотря, как он был получен!


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


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

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


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

 





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