Глава 25. 32-битное программирование в WINDOWS.

(Программируем в WINDOWS 95-98.)

- Тем, кто хорошо знаком с пя­тым измерением, ничего не сто­ит раздвинуть помещение до желательных пределов. Сказку вам более, уважаемая госпожа, до черт знает каких пределов!

М.Л. Булгаков. Мастер и Маргарита.

До сих пор мы писали программы фактически для Windows 3.1. И хотя эти про­граммы прекрасно будут работать и в Windows 95, все же они «не родные» для опера­ционной системы. Сейчас наша задача - научиться писать программы для Windows 95, т.е. программы, работающие в 32-битном режиме.

В ОС Windows 95 реализована так называемая плоская, или линейная, модель па­мяти. Вся память рассматривается как один сегмент. Адрес любой ячейки является

смещением в этом сегменте. Кроме того, в Windows 95 используется страничная адре­сация (см. главу 20), позволяющая эффективно контролировать разделение задач. По этой причине можно забыть о моделях памяти, объеме данных и стека. Программисты на Си должны оценить это по достоинству.

Ксожалению, на момент написания этойглавы, уменя отсутствовали библиотеки для работы в Windows 95 для ассемблера фирмы Microsoft66, поэтому я буду пользо­ваться библиотеками для Турбо-ассемблера (5.0) и Си++ (вер.5.0). Описание новых функций API можно найти в руководствах для Си++.

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

параметров обратный (справа налево).

I. Консольный режим.

Для поддержки задач, работающих в текстовом режиме, в Windows 95 по­явился новый режим, называемый консольным. Консоль - это текстовое такое же, как для программ MS DOS. Однако программы, использующие кон­сольный режим, являются полноценными 32-битными программами, которые, как и другие программы, могут пользоваться функциями API. Консольный режим очень прост и позволяет быстро писать простые прикладные программы для ра­боты в среде Windows 95. Для работы в консольном режиме было добавлено не­сколько API-функций. Известная, наверное, уже всем, программа Far написана как раз в консольном режиме.

66   Необходима версия М ASM 6.14.

Обращаю внимание на то, что в данном разделе в вызовах функций API (но не обычные функции Си) следует различать прописные и заглавные буквы. Объясняется это просто: функции API будут вызываться во время выполнения программы из уже готовых динамических библиотек и должны иметь точно такое имя, как в этих биб­лиотеках. По этойже причине притрансляциимыиспользуемключ/ml.

Ниже (Рис. 24.6) представлена элементарная программа, открывающая окно кон­соли и ожидающая нажатие клавиши. Программа использует 32-битные библиотеки Си++(5.0): c0x32.obj,cw32. lib, import32. lib. Для трансляции можно по-прежнему ис­пользовать MASM 6.0, но для компоновки требуется программа tlink32.ехе. Эту программу можно найти в пакете Си++. Еслинаша программа называется consoll .asm, то для ее трансляции в consol 1 .exe требуются команды:

masm /Ml consoll

tlink32   c0x32+consoll, consoll, ,cw32 imort32

Библиотеки cOx32.obj, import32.1ibH cw32.1ib должны находиться в текущем ката­логе. Подобная программа, использующая Си библиотеки для MS DOS, была нами рассмотрена в главе

.386Р

/плоская модель памяти .MODEL FLAT

EXTRN AllocConsole:NEAR EXTRN       FreeConsole:NEAR EXTRN      _GETCH:NEAR

PUBLIC _MAIN

_TEXT       SEGMENT   DWORD   PUBLIC  USE32 'CODE' /процедура,    вызываемая   из   заголовка   программы cOx32.obj _MAIN       PROC NEAR

/ освободить память  и инициализировать консоль

CALL FreeConsole

CALL AllocConsole /ждем нажатие клавиши

CALL _GETCH память

CALL FreeConsole

/возвратить управление   в   заголовок программы

RET _MAIN ENDP _ТЕХТ ENDS END

Рис. 24.6. Элементарная консольная программа.

Кроме используемых в программе функций, имеются еще следующие функции API для работы с консолью (в Си нотации):

Текст заголовка для окна консоли задается следующей функцией:

int SetConsoleTitle(str), str - указатель на строку, которая станет заголовком.

Для получения стандартного дескриптора консоли используется функция: int - принимает значение:

STD_INPUT_HANDLE - -10 - для ввода

для вывода

STD_ERROR_HANDLE - -12 -для сообщения об ошибках. Вывод текста на консоль:

intWriteConsole(handl, str, len, lenl, noused), handl - дескриптор вывода, str - указа­тель на строку, len количество символов, которое необходимо вывести, - указа­тель на двойное слово, куда помещается количество реально выведенных символов, noused - неиспользуемый параметр.

Ввод с консоли:

intReadConsole(handl, buf, len, lenl, noused), handl -дескриптор консоли, buf- ука­затель на буфер приема, len - количество вводимых символов, len 1 - указатель на двой­ное слово, которое будет содержать количество реально введенных символов, noused -

неиспользуемый параметр. Установка позиции курсора:

int SetConsoleCursorPosition(handl, coord), handl -дескриптор консоли, coord - ука­затель на структуру -xdb? ydb?

Установка цветов текста и фона: int SetConsoleTextAttribute(handI, color),

handl - дескриптор консоли, color - слово, определяющее цвет фона и текста, полу­чается путем операции OR над следующими параметрами FOREGROEJND_BLUE 0x0001 - синий текст, FOREGROUND_GREEN0x0002 - зеленыйтекст, FOREGROUND_RED0x0004-красныйтекст,

- повышенная яркость текста, BACKGRO^D_BLUE0x0010-cHHm^oH, BACKGROUND_GREEN0x0020^eHb^oH, BACKGROUND_RED0x0040 - красный фон, BACKGROWDJNTENSiTY0x0080 - повышеннаяяркость фона.

Обработка мыши и клавиатуры: intReadConsoleInput(handl, buf, num.numl),

handl - дескриптор консоли, указатель на структуру (см. ниже), num - количе­ство информационных записей о событиях, numl - количество реально возвращенных записей. Функция ReadMouselnputO читает одну или несколько записей о входных событиях в буфер. Структура буфера следующая (в Си нотации):

typedef struct   INPUT- RECORD {

WORD EventType;

union

{

KEY EVENT RECORD KeyEvent; &&  нажатие клавиши

MOUSE_EVENT_RECORD MouseEvent;  && событие мыши W1ND0W_BUFFER_SIZE_REC0RD WindowBufferSizeEvent;  &&изменениё размера MENU_EVENT_RECORD MenuEvent;      && используется системой FOCUS EVENT RECORD FocusEvent;  && используется системой } Event; } INPUT_RECORD;

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

1. В программе используются как функции API, так и обычные Си функции (от­крыть файл, прочитать в буфер, закрыть файл).

2. Вызов обычных функций осуществляется согласно нотации Си, т.е. с очисткой стека после вызова (см. главу

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

4. Обращаю Ваше внимание на следующий интересный факт. Для данных мы вы­делили отдельный сегмент. Однако мы знаем, что структура памяти все равно состоит из одного глобального сегмента. В принципе можно было бы использоватьтолько один сегмент_ТЕХТ. Однако здесь есть одно "но". Попытка осуществить запись в сегмент кода вызовет во время выполнения программы ошибку. Дело в том, что защита памяти в Windows 95 осуществляется посредством страничной адресации (см. главу 20). И те данные, которые мы поместим в сегмент кода, будут открыты только для чтения. Если данные предполагается менять, то для них следует отвести сегмент данных. В этом случае они будут открыты как для чтения, так и для записи.

.386Р

;плоская модель

.MODEL FLAT

;внешние процедуры EXTPJM      GetStdHandle: near EXTRN AllocConsolernear EXTRN FreeConsole:near EXTRN WriteConsoleA:near EXTRN _CLOSE:near EXTRN _OPEN:near EXTRN      _READ:near■ EXTRN      _GETCH:near PUBLIC MAIN

;сегмент кода

_ТЕХТ       SEGMENT   DWORD   PUBLIC  USE32 'CODE' /процедура,   с которой выполняется код программы PROC NEAR

/освободить память  и инициализировать консоль

CALL FreeConsole

CALL AllocConsole

дескриптор  вывода  на консоль

PUSH -11

CALL GetStdHandle

MOV EDI,EAX

;открыть файл для чтения

PUSH

PUSH ■

CALL

ADD MOV

8002H   ;0_BINARY OR O_RDVR

OFFSET TEX

_OPEN

стек

ESI,EAX    /сохранить дескриптор файла

CMP

JNE ошибка ли

SHORT LOO

;выйти и указать   код ошибки MOV ЕАХ,1

JMP    • SHORT _END

LOO:

из файла в буфер

PUSH

PUSH

PUSH

CALL

ADD

MOV

100

OFFSET MSG ESI

_READ ESP,12 EBX,EAX

;читаем сто байт

;адрес буфера (32-байтный)

;дескриптор

/восстановить стек ;сколько прочли байт

буфер на консоль

PUSH

PUSH

PUSH

PUSH

PUSH

CALL

CMP

JE

закрыть файл

PUSH CALL POP 0

OFFSET NUM

EBX

OFFSET MSG

EDI

WriteConsoleA

EBX, 100     /если меньше,

SHORT LOO

ESI

_CLOSE

ECX

; пустой параметр

;здесь количество выведенных /количество   выводимых байт ; адрес строки (буфера)

/дескриптор консоли

байт

то файл закончился

ASSEMBLER. Учебіїьткурс

нажатие клавиши

CALL _GETCH

память CALL FreeConsole

с кодом О XOR EAX, EAX

_END:

RET

_MAIN ENDP

_TEXT ENDS

данных

_DATA       SEGMENT DWORD   PUBLIC  USE32 'DATA'

TEX DB "TEST.TXT",0

NUM DD ?

MSG       DB        l00 DUP(O) _DATA ENDS END

Вывод текстового файла в

В заключение разбора программы Рис. 24.7 отмечу, что имя текстового файла может быть и "длинным". Используемая для открытия файла функция _ореп хоть и пришла из старого Си, но работает в соответствии с требованием времени. В имени файла, разумеется, могут содержаться и русские буквы. В последнем случае не забудь­те только о возможном искажении кодировки (см. выше).

П. 32-х битное программирование.

При рассмотрении консольного режима мы использовали три разнородные ком­поненты: MASM.EXE (Microsoft), tlink32.exe - 32-битный редактор связи (Borland) и библиотеки из пакета СІ++ Borland5.0. НеимеяновыхверсийМАБМ, вданномразде-ле мы вынуждены перейти к турбо ассемблеру. Я использую TASM версии 5.0, tlink32.exe, уже нами знакомую, и библиотеку import32.1ib из пакетаTASM. При этом мы по-прежнему придерживаемся старого стиля: не используем include-файлы, не ис­пользуем сокращенное описание сегментов.

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

Ниже представлена простая программа, написанная так, как должно писать для Windows 95. Назовем программу prog8. Трансляция осуществляется выполнениемдвух строк:

TASM32 /ml

tlink32 prog8,prog8,prog8, import32

В первую очередь обращаю Ваше внимание то, что бросается в глаза. Все вне­шние процедуры записаны с использованием и прописных и заглавных букв. Объяс­нение этому очень простое: связывание процедур осуществляется динамически, т.е.

во время выполнения программы. В динамической же библиотеке они записаны имен­но так, как у нас в программе. Сказанное в равной мере относится и к предыдущей программе (Рис. 24.7).

.386Р

/плоская модель

; в стек с права на лево

.MODEL FLAT

;внешние процедуры

EXTRN Веер:NEAR

EXTRN CreateWindowExA:NEAR

EXTRN DefWindowProcA:NEAR

EXTRN DispatchMessageA:NEAR

EXTRN ExitProcess:NEAR

EXTRN GetMessageA:NEAR

EXTRN GetModuleHandleA:NEAR

EXTRN LoadCursorA:NEAR

EXTRN LoadIconA:NEAR

EXTRN PostQuitMessagerNEAR

EXTRN RegisterClassArNEAR

EXTRN ShowWindow:NEAR

EXTRN TranslateMessage:NEAR

EXTRN UpdateWindow:NEAR PUBLIC WNDPROC

;структуры MSGSTRUCT STRUC

MSHWND DD ?

MSMESSAGE DD ?

MSWPARAM DD ?

MSLPARAM DD ?

MSTIME DD ?

MSPT DD ? MSGSTRUCT ENDS

WNDCLASS STRUC

CLSSTYLE

DD

?

CLSLPFNWNDPROC CLSCBCLSEXTRA

CLSCBWNDEXTRA

CLSHINSTANCE

DD

DD

DD

DD

? ? ? ?

CLSHICON

DD

?

ASSEMBLER. Учебітйкурс

DD ?

CLSHBRBACKGROUND   DD ?

CLSLPSZMENUNAME DD ? CLSLPSZCLASSNAME DD ? WNDCLASS ENDS

данных

DATA SEGMENT DWORD PUBLIC USE32

NEWHWND

MSG

WC

HINST

TITLENAME

CLASSNAME _DATA ENDS

сегмент кода

_TEXT   SEGMENT DWORD PUBLIC USE32

START і

PUSH CALL MOV

REG_CLASS:

структуру окна ;    стиль   CS_HREDRAW  + CS_VREDRAW

'DATA'

DD С

MSGSTRUCT WNDCLASS

DD С

DB 'WIN32, SIMPLE ASSEMBLY PROGRAM',0 DB 'CLASS32',0

<?> <?>

'CODE'

С

GetModuleHandleA [HINST], EAX

+

MOV [WC.CLSSTYLE] ,4003H

; процедура обработки сообщений

MOV [WC.CLSLPFNWNDPROC] ,

MOV [WC.CLSCBCLSEXTRA] , 0

MOV [WC.CLSCBWNDEXTRA], 0

MOV EAX, [HINST]

MOV [WC.CLSHINSTANCE], EAX

OFFSET WNDPROC

PUSH PUSH CALL MOV

PUSH PUSH CALL MOV

MOV MOV MOV

32512 С

;IDI APPLICATION

LoadlconA

EAX

32512 С

LoadCursorA

EAX

[WC.CLSHBRBACKGROUND] ,   17     /цвет окна DWORD PTR   [WC.CLSLPSZMENUNAME] , 0

DWORD  PTR   [WC.CLSLPSZCLASSNAME],   OFFSET CLASSNAME

Глава 25. 32-x битное программирование в WINDOWS

PUSH CALL

окно

OFFSET WC RegisterClassA

зарегистрированного

PUSH

G

 

 

PUSH

[HINST]

 

 

PUSH

G

 

 

PUSH

G

 

 

PUSH

4GG

DY

 

PUSH

4GG

DX

 

PUSH

lGG

Y

 

PUSH

lGG

; x

 

PUSH

OOOCFOOOOH

;WS_

OVERLAPPEDWINDOW

PUSH

OFFSET TITLENAME

/

окна

PUSH

OFFSET CLASSNAME

r

класса

PUSH

0

 

 

CALL

CreateWindowExA

 

 

на

CMP JZ MOV PUSH PUSH CALL PUSH CALL

;петля обработки MSG_LOOP:

PUSH PUSH PUSH PUSH CALL CMP JE

PUSH CALL PUSH CALL

JMP

END_LOOP:

PUSH CALL

_ERR:

CALL JMP

ошибку EAX, 0 _ERR [NEWHWND], EAX 1 ;SW_SHOWN0RMAL

[NEWHWND] ShowWindow [NEWHWND] UpdateWindow сообщений

О О О

OFFSET MSG GetMessageA АХ, О END_LOOP

OFFSET MSG TranslateMessage

OFFSET MSG

DispatchMessageA

MSG LOOP

[MSG.MSWPARAM]

ExitProcess

BEE END LOOP

классаокна

параметров

[ВР+014Н] ; LP ARAM

[BP+10H] ;WAPARAM

[BP+OCH] ;MES

[BP+8] ; HWND WNDPROC PROC

PUSH MOV PUSH PUSH PUSH CMP

JE

CMP

JE

CMP      DWORD PTR

JE LBUTTON

CMP     DWORD PTR JE RBUTTON

JMP DEFWNDPROC ;нажатие правой кнопки RBUTTON:

JMP WMDESTROY ;нажатие левой кнопки LBUTTON:

стеке

EBP

EBP,ESP

EBX ESI EDI

DWORD PTR WMDESTROY

DWORD PTR [EBP+0CH],1 WMCREATE

[EBP+OCH],2

DESTROY

CREATE

кнопка

[EBP+OCH] ,204H кнопка

приводит

закрытию окна

вызывает  звуковой сигнал

CALL

MOV

JMP

WMCREATE:

MOV

JMP DEFWNDPROCI PUSH PUSH PUSH PUSH

CALL

JMP WMDESTROY:

PUSH CALL

MOV

BEE EAX, 0

FINISH

EAX, 0

FINISH

DWORD PTR DWORD PTR DWORD PTR DWORD PTR DefWindowProcA FINISH

0

PostQuitMessage

EAX, 0

[EBP+14H] [EBP+10H] [EBP+OCH] [EBP+08H]

FINISH:

POP POP POP ■ POP RET

WNDPROC

EDI ESI EBX EBP 16

ENDP

;процедура

BEE PROC MOV PUSH

_ОР:

короткого   звукового сигнала ECX, 100

ECX

PUSH 0 PUSH 0 ;библиотечная

CALL POP DEC

PUSH

LOOPD POP ECX RET

ENDP

■_TEXT ENDS

END START

процедура,

Beep ECX ECX ECX

_OP

дающая короткий звуковой сигнал

Рис. 24.8. Простая программа для \УШОЦ,$95 с 32-битной адресацией.

Обращаю внимание на одну особенность программы на Рис.24.8 - она начинается с вызова функции ОеГМоёшеНапоТеА. Наши 16-битные программы начинались, как Вы помните, с других функций. Однако стало удобнее, не правда ли?

Ш. Использование ресурсов.

Вручную создавать кнопки, списки, меню и т.д. - дело довольно утомительное. Но, к счастью, у нас имеется мощный инструмент - ресурсы, которые можно созда­вать отдельно и затем подключать к ЕХЕ-файлам. Перед нами стоят три вопроса:

1. Как создавать ресурсы?

2. Как подключать ресурсы?

3. Как использовать ресурсы в программе? Начнем отвечать по порядку.

1. Ресурсы хранятся в текстовом файле с расширением ЯС, который можно запол­нять обычным текстовым редактором. Я советовал бы первый ресурс создать именно так. В дальнейшем удобнее, однако, использовать редактор ресурсов, ко­торый имеется, например, в Borland Си. В нем создание файла ресурсов осуще­ствляется визуальными средствами. Для простоты возьмем только один ресурс меню и только с двумя пунктами. Файл ресурсов в этом случае будет иметь вид:

MENUP MENU {

popup  "&Файлы"

{

MENUITEM   "«.Пункт меню", 101 MENUITEM  "E&xit", 102

}

}

Обратите внимание на следующие моменты. Наш ресурс называется По

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

2. Перед подключением ресурса его следует откомпилировать с помощью подхо­дящего компилятора ресурсов. Я пользовался компилятором BRC.EXE. Пред­положим, файл ресурсов называется Rl .RC, тогда строка выглядит так: BRC -32 -г R1. В результате появляется откомпилированный файл Rl .RES. Для того чтобы файл ресурсов был включен в откомпилированный файл, его следует ука­зать в строке для tlik32. Пусть объектный файл называется prog 1, тогда: tlink32 progl ,progl ,progl ,imort32„rl и в файл prog 1 .ехе будет включен ресурс Rl .RES.

3. Для того чтобы меню появилось в окне, при регистрации класса окон следует указать на строку с именем меню (унасэто MENUP). При выборе пункта меню в функцию окна приходит сообщение WM COMMAND. При этом в младшем слове WPARAM помещается номер-значение выбранного пункта меню.

На Рис. 24.9 представлена программа, полученная из программы на Рис. 24.8 до­бавлением ресурса - меню. По сравнению с предыдущей программой здесь добавился еще вызов функции MessageBox. Эта функция выдает на экран сообщение.

.386Р

/плоская модель

.model FLAT

; внешние процедуры

EXTRN Веер:NEAR

EXTRN CreateWindowExA:NEAR

EXTRN DefWindowProcA-.NEAR

EXTRN DispatchMessageArNEAR

EXTRN ExitProcess:NEAR

EXTRN Ge tMe s s ageA:NEAR

EXTRN GetModuleHandleA-.NEAR

EXTRN LoadCursorA:NEAR

EXTRN

EXTRN

EXTRN EXTRN EXTRN EXTRN EXTRN PUBLIC

структуры MSGSTRUCT STRUC MSHWND MSMESSAGE MSWPARAM MSLPARAM MSTIME

MSPT

MSGSTRUCT ENDS

LoadIconA:NEAR PostQuitMessage:NEAR RegisterClassA:NEAR ShowWindow:NEAR TranslateMessage:NEAR UpdateWindow:NEAR MessageBoxA:NEAR

DD DD DD DD DD DD

■---------

WNDCLASS STRUC

CLSSTYLE DD

DD

CLSCBCLSEXTRA DD

CLSCBWNDEXTRA DD

CLSHINSTANCE DD

CLSHICON ■ DD

CLSHCURSOR DD CLSHBRBACKGROUND DD

CLSLPSZMENUNAME DD

CLSLPSZCLASSNAME DD WNDCLASS ENDS

данных

DATA SEGMENT DWORD PUBLIC USE32 NEWHWND MSG WC

HINST

<?> <?>

TITLENAME

CLASSNAME MENU TEXTM TEXTMC _DATA ENDS

кода TEXT SEGMENT

'DATA*

DD 0

MSGSTRUCT WNDCLASS DD 0

DB    'WIN32, ASSEMBLY PROGRAM WITH MENU',0

DB 'CLASS32',0

DB    'MENUP',0

DB   ' Выбран пункт меню', О

DB   1 Сообщение1, О

DWORD   PUBLIC  USE32 'CODE'

START:

PUSH CALL MOV

REG_CLASS:

структуру окна ;стиль CS_HREDRAW +  CS VREDRAW

0

GetModuleHandleA

EAX

CS_GLOBALCLASS

MOV

[WC.CLSSTYLE],4003H

обработки сообщений

MOV MOV MOV MOV MOV

PUSH PUSH CALL MOV

PUSH PUSH CALL MOV

[WC.CLSLPFNWNDPROC],   OFFSET WNDPROC

0

[WC.CLSCBWNDEXTRA] , 0

EAX, [HINST] [WC.CLSHINSTANCE], EAX

32512 0

LoadlconA [WC.CLSHICON], EAX

32512 0

LoadCursorA [WC.CLSHCURSOR], EAX

MOV [WC.CLSHBRBACKGROUND] ,   17 ,-цветокна

MOV DWORD  PTR   [WC.CLSLPSZMENUNAME],OFFSET MENU

MOV DWORD PTR   [WC.CLSLPSZCLASSNAME], OFFSET CLASSNAME

PUSH OFFSET WC

CALL RegisterClassA /создать   зарегистрированного класса

PUSH

О

 

 

PUSH

[HINST]

 

 

PUSH

0

 

 

PUSH

0

 

 

PUSH

400

; DY

 

PUSH

400

;t DX

 

PUSH

100

;■ Y

 

PUSH

100

;X

 

PUSH

OOOCFOOOOH ;WS_

OVERLAPPEDWINDOW

PUSH

OFFSET

TITLENAME

окна

PUSH

OFFSET

CLASSNAME

класса

PUSH

0

 

 

CALL

CreateWindowExA

 

+

;проверка на

CMP JZ MOV PUSH PUSH CALL PUSH CALL

;петля обработки MSG LOOP:

ошибку EAX, О _ERR [NEWHWND], EAX 1 ;SW_SHOWNORMAL

[NEWHWND] ShowWindow [NEWHWND] UpdateWindow сообщений

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

CALL

_ERR:

CALL

JMP

[MSG.MSWPARAM]

ExitProcess

BEE

END LOOP

процедура окна

расположение параметров  в стеке [ВР+014Н] ; LP ARAM

[ВР+10Н] ; WAP ARAM

[BP+OCH] ;MES [BP+8] ; HWND

WNDPROC PROC

PUSH EBP

MOV      EBP,ESP

PUSH    EBX ■

PUSH ESI

PUSH EDI

CMP       DWORD  PTR   [EBP+OCH],2

' JE WMDESTROY

;WM DESTROY

CMP DWORD PTR   [EBP+0CH],1 ;WM_CREATE

JE WMCREATE

CMP DWORD PTR

JE LBUTTON

CMP DWORD PTR

JE RBUTTON

CMP     DWORD PTR JE WMCOMMAND

JMP DEFWNDPROC ;пришло сообщение WM_COMMAND ;В частности,   такое  сообщение приходит ;пункта меню WMCOMMAND:

;здесь следует проверить, ;Код пункта меню указан в ;у нас этот код равен 101 код  будет помещен в CMP      WORD  PTR    [ЕВР+10Н] , 102 JE WMDESTROY

WORD  PTR   [EBP+10H],101        ;Пункт меню NO_IT

сообщение

О ;MB_OK -  тип сообщения

OFFSET   TEXTMC   ; заголовок окна сообщения OFFSET  ТЕХТМ /сообщение DWORD  PTR    [ЕВР+08Н]       /дескриптор окна MessageBoxA

кнопка

[EBP+OCH] , 204H  ;правая кнопка

[ЕВР+ОСН],111Н ;WM COMMAND

и при выборе

какой пункт меню выбран файле ресурсов (один пункт меню) младшем слове  параметра WPARAM ;exit&

CMP JNE ;выдать

PUSH PUSH PUSH PUSH CALL NO_IT:

JMP DEFWNDPROC

правой кнопки приводит

RBUTTON:

JMP WMDESTROY

;нажатие левой кнопки LBUTTON:

BEE EAX, 0

FINISH

закрытию окна

вызывает   звуковой сигнал

\

CALL

MOV JMP

WMCREATE:

MOV

\JMP

DEFWNDPROC

PUSH PUSH PUSH

EAX, .0 FINISH

DWORD DWORD DWORD

PTR PTR PTR

[EBP+14H] [EBP+10H] [EBP+OCH]

PUSH call JMP

WMDESTROY: PUSH call MOV

FINISH:

POP POP POP POP RET

WNDPROC

DWORD PTR [EBP+08H] DefWindowProcA

FINISH

0

PostQuitMessage

EAX,   0   ;функция возвращает О

EDI ESI EBX EBP

16

ENDP

;процедура

BEE PROC MOV PUSH

OP:

короткого   звукового сигнала ЕСХ,100

ECX

PUSH о push о ;библиотечная

CALL

POP

DEC

PUSH

LOOPD

POP ECX

RET BEE ENDP _TEXT ENDS

END START

процедура,

Beep ECX ECX ECX

_OP

дающая короткий звуковой сигнал

Рис. 24.9. Пример использования файла ресурсов.

Одним из основных ресурсов является Диалог. Он представляет из себя диалого­вое окно, которое может содержать другие органы управления (ресурсосоздаваемые с использованием визуальных средств в редакторе ресурсов). Диалог, как и обычное окно, перед тем как использоваться в программе, должен быть создан. Только для это­го используется не функция создания окна(СгеаГе\¥іпс1о\у), а функция Біа^Вох. Как и обычное окно, диалоговое окно имеет свою функцию, обрабатывающую сообще­ния, приходящие на диалог. Структура этой функции точно такая же, как функции окна. Отличие заключается в том, что:

1. Функция возвращает 0, если данное сообщение в функции не обработано и 1 (не ноль), если сообщение обработано.

2. Некоторые сообщения окна заменены другими. Появились новые сообщения. На­пример, вместо с< х )6i I ici 1ия WM_CREATE приходитсообщение WMJ>mTJIALC)G. Основные же сообщения остались теми же.

Вообще говоря, при выполнении функции DialogBox автоматически создается функция обработки окна диалога. Функция же, которую мы определяем в программе, является, таким образом, вторичной и вызывается из первой функции. Необходимость такого построения очевидна. При создании диалога мы определяем его свойства и свойства других ресурсов (кнопок, окон редактирования, списков, переключателей и т.д.). Свойствадолжна поддерживать первая функция. Любое сообщение на окно диа­лога приходит сначала в первую функцию. Оттуда сразу вызывается вторая. Если вто­рая функция возвращает 0, то данное сообщение обрабатывает первая функция, если 1 ,то первая функция полагает, что обработку на себя взяла вторая.

Каждый ресурс, который мы поместим надиалоговое окно, имеет свой номер. Впреде-лахданногодиалоганомеруникален.Какивслучаесобьиггьш

щиесорганомуправления, отражаются в сообщен™ WM_COMMAND. В младшем слове пapaмeтpawPaгamбyдeтcoдepжaтьcяидeгиификaтoppecypca,вcтapшeм-кoдcoбьиия.

Ниже (Рис. 24.11) представлено простейшее использование диалога. В диалого­вом окне имеется единственная кнопка, по нажатию которой происходит выход из программы. Создание диалога осуществляется вызовом функции DialogBoxParamA (все остальные функции, создающие диалог, работают через эту функцию). На Рис. представлен файл ресурса.

#define IDC_BUTT0N1   55     //идентификатор кнопки

DIAL1  DIALOG О,   О,   240,   120    //положение диалога

STYLE D S_MO DAL FRAME  |   DSJDLOOK  I   DS CONTEXTHELP   |  WS_POPUP |

WS_VISIBLE   I   WS_CAPTION   | WSJSYSMENU

CAPTION "Окно диалога"  //заголовок окна

FONT 8,   "MS Sans Serif"

{

CONTROL "OK",  IDC_BUTT0N1,   "button",  BS_PUSHBUTTON  |  BS CENTER I   WS_CHILD    I   WS_VISIBLE    |   WS JTABSTOP,    176,    13,    50, 14 }

Рис. Файл ресурса для программы на Рис.

.386P

;плоская модель

; в стек  справа налево

.MODEL flat

процедуры

extrn DispatchMessageA:NEAR extrn ExitProcess'.NEAR

EXTRN EXTRN EXTRN EXTRN EXTRN EXTRN

PUBLIC DLGPROC

;структуры MSGSTRUCT STRUC MSHWND MSMESSAGE MSWPARAM MSLPARAM MSTIME MSPT

MSGSTRUCT ENDS

данных

DATA  SEGMENT   DWORD   PUBLIC   USE32 'DATA

NEWHWND DD 0

MSG MSGSTRUCT

HINST DD 0

DIAL DB   'DIALI',0 /имя - идентификатор диалога

_DATA ENDS

кода

_TEXT     SEGMENT  DWORD   PUBLIC  USE32 'CODE' START:

PUSH 0

CALL GetModuleHandleA MOV [HINST], EAX

диалог

PUSH  0 ;возвращаемая функцией диалога величина

PUSH  OFFSET   DLGPROC   /функция окна диалога PUSH  0 ;дескриптор окна,   где появляется диалог

PUSH  OFFSET  DIAL   ;имя -   идентификатор диалога

PUSH [HINST]

CALL DialogBoxParamA

/петля   обработки сообщений MSG_LOOP:

PUSH О

PUSH О

PUSH О

PUSH OFFSET MSG

GetModuleHandleA:NEAR PostQuitMessage:NEAR GetMessageA:NEAR TranslateMessage:NEAR DialogBoxParamA:NEAR EndDialog:NEAR

DD

DD DD DD DD DD

CALL

CMP

JE

GetMessageA

AX, 0

PUSH

CALL

PUSH

CALL

JMP

END_LOOP OFFSET MSG TranslateMessage

OFFSET MSG

DispatchMessageA

MSG_LOOP

END LOOP:

PUSH CALL [MSG.MSWPARAM]

ExitProcess

процедура диалога

расположение параметров  в стеке

как и в  обычной функции окна [ВР+014Н] ; LPARAM

[ВР+10Н] ;WAPARAM

[ВР+ОСН]

[ВР+8] ;HWND

DLGPROC PROC

PUSH EBP

MOV EBP,ESP

PUSH EBX

PUSH ESI

PUSH EDI

MOV EAX, 0

CMP DWORD  PTR   [EBP+OCH],110H ;WM_INITDIALOG

JE WMINITDIALOG

CMP DWORD  PTR   [EBP+OCH],111H ;WM_COMMAND

JE WMCOMMAND

JMP FINISH

;пришло  сообщение  WM_COMMAND

;в частности, такое сообщение приходит и при нажатии кнопки WMCOMMAND:

CMP    WORD PTR кнопка JNE FINISH

;нажатие правой кнопки приводит к  закрытию окна PUSH О

CALL        PostQuitMessage   /сообщение о выходе из про­граммы

PUSH О

PUSH     DWORD  PTR [EBP+08H]

CALL     EndDialog     /закрыть диалоговое окно MOV       EAX, 1 JMP FINISH

;здесь те команды, которые ;при инициализации диалога , WMINITDIALOG: необходимо выполнить у  нас   здесь ничего

JMP FINISH:

POP POP POP POP RET DLGPROC /~ ~ _TEXT ENDS END START

FINISH

EDI ESI EBX EBP 16

ENDP

Puc. 24.11. Простой пример использования диалогового окна.

Заключительные замечания.

Материал, изложенный в данной главе, базируется на системных вызовах опера­ционной системы Windows (API функциях). Современные средства программирова­ния под Windows всецело основываются на использование библиотек объектов. Боль­шое распространение нашло визуальное программирование. Мы рассматривали про­граммирование для Windows более низкого уровня, чем то, которое обычно сейчас используется большинством программистов. Знание особенностей системных вызо­вов поможет Вам программировать более грамотно и профессионально. Я рекомендо­вал начинать программировать для Windows, используя только системные вызовы.

Несколько слов следует сказать по поводу совместимости Windows 95 (98) и Windows NT 4.0. Обычно говорят о почти полной совместимости этих операционных систем на уровне API-функций. С этим довольно трудно согласиться. Существует по крайней       три уровня несовместимости.

1. Полное отсутствие в той или иной операционной системе данной функции. И хотя таких функций немного, но они имеются.

2. Несовпадение некоторых значений входных параметров. Например, для фун­кции CreateFile параметр dwShareMode может принимать значение FILE_SHARE_DELETE, которое разрешено только для Windows NT.

3. Имеется еще один уровень несовместимости, как правило, не отраженный в документации, но который может сказаться на работе программного обеспече­ния. Автор обнаружил эту несовместимость в работе некоторых сетевых функ­ций. Например, согласно документации некоторый параметр может принимать

значение как NULL, так и «пустая строка». Но одна из операционных систем, почему-то правильно работает только с одним значением параметра.