Глава 24. Начала программирования для WINDOWS.

Отворил я окно...

А.К. Толстой

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

Материал, излагаемый в данной главе, неявляется руководством по программиро­ванию в среде WINDOWS. Это слишком обширный вопрос. Здесь лишь обозначаются некоторые ключевые моменты, которые помогут Вам в программировании под WINDOWS на ассемблере. По этой же причине не приводится справочная информа­ция по WINDOWS. Ее слишком много.

На первый взгляд кажется, что программировать на ассемблере под WINDOWS нет необходимости. В принципе, имея в руках такой мощный инструмент, как Си, ас­семблер можно использовать для каких-либо вставок, чтобы оптимизировать програм­му. Хочу, однако, заметить, что программирование на ассемблере в среде WINDOWS весьма приближается к программированию на Си в связи с тем, что в тех и других программах приходится использовать вызов стандартных функций операционной си­стемы (функции API -APLICATIONPROGRAMM INTERFACE). Все общение с ком­пьютером происходит через эти функции. Вы скоро увидите, что написание неболь­ших программ для WINDOWS на ассемблере так же просто, как и на Си. В этой Елаве Вам особенно понадобятся знания, полученные из Елавы 15.

Читатель, наверное, заметил тенденцию последних лет - программные пакеты занимают все больше и больше места. Это весьма странное явление. Ведь програм­ма, работающая под WINDOWS, наоборот должна становиться компактнее по срав­нению с аналогичной программой для MS DOS. Действительно, WINDOWS предо­ставляет программисту огромное количество функций. Там, где в MS DOS приходи­лось писать свою процедуру, здесь достаточно вызвать функцию с подходящими параметрами. Так что же происходит? Как всегда, это одна из великих мистифика­ций. Человечество не может без этого обойтись. И у пользователя возникает пред­ставление, что чем больше пакет и чем больше в нем файлов, тем это более солид­ный продукт. Увы, не всегда так. Большие, громоздкие пакеты часто сами не справ­ляются с собой как раз по причине огромного количества файлов. Причина появле­ния программных монстров связана прежде всего с тем, что производство программ становится все более и более индустриальным, я бы сказал, конвейерным делом. А разве можно сделать пакет совершенным и компактным в этой ситуации. И вот уже индустрия программных продуктов начинает диктовать нам условия: "Вы должны иметь такое и такое оборудование, а иначе наше программное обеспечение не будет у вас работать". Каково? Но мыто с Вами не конвейерщики, слава Богу, мы учимся делать штучный товар. Итак, вперед!

I. Некоторые общие положения.

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

1. Программирование в среде WINDOWS основывается на использовании функ­ций API (APPLICATION PROGRAM INTERFACE). Количество таких функций составляет несколько сотен. Ваша программа будет состоять из большого коли­чества вызовов этих функций.

2. СписокфункцийАР1иихописаниепрощевсегобратьиздокументациипоязыкуСи. Нижебудетподробнорассказано.какиспользоватьэтоописаниевязыкеассемблера.

3. Главным элементом (объектом) программы в среде WINDOWS является 'Окно'. Для каждого окна определяется процедура прерывания. Эта процедура 'вылав­ливает' всю информацию, предназначенную этому окну.

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

5. Для обычных прикладных программ нет практически никакой разницы в про­граммировании для WINDOWS 95-98и WINDOWS 3.1. Основное различие меж­ду этими системами для программистов заключается в следующих моментах:

- WINDOWS 95 поддерживает 32-битную адресацию, в связи с этим следует иметь в виду, что некоторые входные параметры API-функций изменили свою разрядность,

- WINDOWS 95 поддерживает многозадачность с автовыгрузкой, такую мно­гозадачность иногда называют истинной,

- WINDOWS 95 поддерживает многозадачность не только на уровне процес­сов (программ), но и на уровне потоков (частей программ), отдельную про­цедуру программы можно объявить потоком (Thread), и она станет работать как независимая программа,

- в WINDOWS 95 каждая задача имеет свою входную очередь сообщений, тогда как в предыдущих версиях, был один входной поток,

- WINDOWS 95 предоставляет в распоряжение программистам специальные окнадля текстовой информации, они называются консоли, работа с которы­ми несколько напоминает работу с MS DOS,

- WINDOWS 95 рассматривает адресное пространство как линейное, WINDOWS     работала в сегментированном адресном пространстве,

- WINDOWS 95 предоставляет новые стандартные элементы, объекты управ­ления и большое количество новых функций.

Даже когда мы перейдем с Вами к 32-битному программированию (глава 25), сама

структура программы не претерпит практически никаких изменений. П. Вызов стандартных API-функций.

Программируя в MS DOS, мы могли использовать различные прерывания либо обращаться непосредственно к оборудованию через порты ввода-вывода. Это созда­вале- значительные проблемы, т.к. оборудование на разных компьютерах могло отли­чаться друг от друга. При программировании же в WINDOWS для Вас будут суще­ствовать только API-функции. Все сношение с внешним миром, то есть с компьюте­ром, должно производиться через эти функции.

Как правило, в поставке любого языка программирования, который предполагается использовать для программирования под WINDOWS, имеется специальная библиотека, которая позволяет вызывать эти функции. Имеется такая библиотека и в макроассембле­ре: LIBW.LIB. В Главе 14 дается структура загружаемых модулей (ЕХЕ-программ), кото­рые работают в среде WINDOWS. Может сразу возникнуть вопрос, каким образом этот модуль может быть создан. Все очень просто, никаких специальных ключей не требуется. Если в Вашей программе вызывается хотя бы одна функция из библиотеки LIBWh эта библиотека будет указана программе LINK, то компоновщик создаст модуль для WINDOWS. Ниже приводится пример простой программы (см. Рис. 24.1), которая ничего не делает. Пример приводится для демонстрации самого процесса трансляции. Процеду­ра 1КГГТА5Кявляется библиотечной, но вданной программе она вызывается формально. В дальнейшем DOSSEG, DGOUP, ГМТТАЗКнайдут свое разъяснение. Все последующие программы будут иметь точно такую же структуру. Нашей дальнейшей задачей будет на­полнение этой структурытемилииным содержанием. Замечутакже, чтодлятрансляции программ в этой главе используется макроассемблер фирмы MICROSOFT версии 6.1 и компоновщик версии 5.0. Последовательность команд трансляции следующая:

ML  -с  PROG. ASM; 65 LINK PROG. OB J

На вопрос о библиотеке ответьте LIBW. Можно написать также строку:

LINK  PROG,PROG,, LIBW;

Общий вид командной строки для LINK.EXE версии 5.0:

LINK  OBJFILE,EXEFILE,MAPFILE,LIBFILE,DEFFILE;

А тот и сама программа.

.DOSSEG

DGROUP  GROUP DATA, STA

ASSUME  CS:CODE,   DS:DGROUP EXTRN   INITTASK:FAR EXTRN   DOS3CALL:FAR ;сегмент стека STA  SEGMENT   STACK    1 STACK'

DW 2000 DUP(?) STA ENDS

« Чтобы макроассемблер правильно работал под Windows, Вам придется в файл system.ini в раздел [386Enh] поместить строку типа device=\nyTb\DOSXNT.386.

17 - 4072

'CODE'

; сегмент данных

DATA SEGMENT WORD   ' DATA'

DATA ENDS

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

.CODE SEGMENT WORD JBEGIN:

/ничего не значащий

PUSH AX

CALL INITTASK ;выход из программы

MOV AH,4CH

CALL DOS3CALL CODE ENDS

END _BEGIN

;вызов подпрограмм   INT 21H

Рис. 24.1. Пример простой программы.

Еще раз подчеркну, что пока мы занимаемся 16-битными программами под Windows. Эти программы будут работать как под Windows 95-98, так и под Windows 3.1,окотороймногиеуже, наверное, забыли.

Чтобы использовать функции API в своей программе, предлагается несколько по­ложений, которые помогут Вам это делать.

1. Функции API хорошо описаны в документации кязыкам Си (проще использо-

вать обычный HELPER). В справочниках по функциям API обычно также ис­пользуется Си-нотация.

2. Следует в первую очередь обратить внимание на тип, количество и порядок посылаемых в функцию параметров. Вам "вручную" придется посылать эти па­раметры. В этой главе Вы встретите прямой (слева направо) и обратный (справа налево) порядок посылки параметров.

3. Функция, как правило, что-то возвращает. Следует четко представлять, где воз­вращается значение функции (в каком регистре или регистрах).

4. Из главы 15 мы знаем, что в том случае, когда вызываемая функция получает параметры, после возврата из нее стек должен быть освобожден. Вызов функ­ций API происходит согласно правилам языка Паскаль, поэтому освобождать стек после возврата из функций API не нужно.

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

BOOL GETMESSAGE (LPMSG,   HWND,   UMSGFILTERMIN, UMSGFILTERMAX)

Данная функция имееттип BOOL. Однако мы понимаем, что логическая перемен­ная - это фикция. Речьидето целой переменной. Просто принимается соглашение, что значение 0 - ложь, а не ноль (обычно 1) - истина. А мы знаем, что обычная целая величина (или слово со знаком) возвращается в регистре АХ. С возвращаемой величи­ной мы разобрались. Смотрим на типы параметров:

LPM.SG - дальний указатель на структуру MSG (сообщение системной очереди),

HWND   -   тип WORD,

UMSGFILTERMIN   -   тип WORD,

UMSGFILTERMAX  -   тип WORD.

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

PUSH DS ; поместить

LEA АХ, MES ;в стек

PUSH AX /дальний указатель

PUSH О

PUSH О

PUSH О

CALL GETMESSAGE CMP АХ, 0   ;что возвращает? . JZ _ЕХ1Т

Воти все. Следует отметить, что хотя мы говорим здесь о функции СИ, но посылка параметров осуществляется так же, как на Паскале, т.е. слева направо. Вот еще пример:

LONG  DISPATCHMESSAGE(LPMSG);

CONST MSG FAR* LPMSG;

Функция формирует сообщение для окна из очереди сообщений для данного при­ложения (см. ниже). Возвращаемое значение имееттип LONG. Напомню, что возвра­щается такое значение в паре регистров DX: АХ (Елава 15). Впрочем, это значение нам не понадобится. Параметр, который посылается в функцию, есть указатель на некото­рую структуру MSG, о которой будет сказано ниже. Вот вызов указанной функции:

PUSH DS

LEA АХ,MSG

PUSH AX

CALL DISPATCHMESSAGE

Подведем теперь некоторый итог.

1, Параметры передаются через стек. Порядок передачи параметров - слева направо.

2. При передаче параметров длиной больше, чем слово, вначале передается старшее слово, а затем младшее.

Примеров с вызовом функций, я думаю, уже достаточно, рассмотрим теперь неко­торые необходимые структуры. Самая важная в WINDOWS структура - это структура сообщения системной очереди. Ниже она имеет имя MESSA.

MESSA

STRUCT

 

HWND

DW

?

MESSAGE

DW

?

WPARAM

DW

?

LPARAM

DD

?

TIME

DD

 

X

DW

?

Y

DW

?

MESSA

ENDS

 

Напомню (Приложение 3), что мы, таким образом, создали шаблон структуры. Для того чтобы зарезервировать место для такой структуры следует указать директиву:

MSG MESSA О.

На Си подобная структура имеет следующий вид.

TYPEDEF STRUCT TAGMSG   { /*   MSG */

HWND HWND; UINT MESSAGE; WPARAM WPARAM;

LPARAM LPARAM; DWORD TIME;

POINT PT;

} MSG;

- состоящая из координат   Y. Вдумайтесь теперь,     чего вводятся

новые типы HWND, UINT, WOARAM, если все это WORD. Если честно, то я не пони­маю для чего, и это является дополнительным стимулом в моей любви к ассемблеру.

Еще одна важная структура определяет класс окна.

WNDCLASS

 

 

STYLE

DW

?

LPFNWNDPROC

DD

?

CBCLSEXTRA

DW

?

CBWNDEXTRA

DW

?

НINSTANCE

DW

?

HI CON

DW

?

HCURSOR

DW

т

STRUCT

на процедуру обработки

HBRBACKGROUND LPSZMENUNAME LPSZCLASSNAME WNDCLASS

?

DD ? DD ? ENDS

на строку на строку

В данной структуре хранятся параметры класса окна. НаСи эта структура выгля-

HCURSOR HCURSOR;

HBRUSH HBRBACKGROUND;

LPCSTR LPSZMENUNAME;

LPCSTR LPSZCLASSNAME;

} WNDCLASS;

III. Структура программы.

В нашей главе мы будем рассматривать в основном программы, состоящие из од­ного сегмента кода и одного сегмента данных. Принято называть такую модель памя­ти SMALL - маленькой. При программировании на ассемблере ее вполне достаточно. НаРис. 24.2 изображена структура такой программы.

дит так:

TYPEDEF STRUCT TAGWNDCLASS {

/* */

UINT STYLE;

WNDPROC LPFNWNDPROC;

INT CBCLSEXTRA;

INT CBWNDEXTRA;

HINSTANCE HINSTANCE;

HICON HICON;

Определение фднк ц и и API а Базовых структур WINDOWS |

 

аервяешоїв и коисмаижы

резервный заголовок ІбБайм.

хода

——- ОаредвА&яае классов охсш

очереди

а

фджкции

Рис. 24.2. Структура программы.

Стоит более подробно рассмотреть сегмент кода.

1. Заголовок кода. В заголовке кода присутствует вызов трех функций API. Инте­ресно, что при создании программы на Си данный заголовок создается автоматичес­ки, и программист может и не подозревать о существовании этих функций. Мы же стараемся понять все. эти

inittask - инициализирует регистры, командную строку и память. Входных па­раметров не требует. Вызывается первой. Возвращаемые значения регистров: АХ = 1 (О - ошибка), СХ - размер стека, DI - уникальный номер дляданной задачи, DX- пара­метр NCMDSHOW (см. ниже), ES - сегментный адрес (селектор) PSP, ES:BX - адрес командной строки, SI - уникальный номер для ранее запущенного того же приложе­ния. В WINDOWS 3.1 при запуске приложения несколько раз, каждый раз в память загружается только часть сегментов, часть сегментов является общим ресурсом. Этим достигалась экономия памяти. В WINDOWS 95 от этого отказались. Каждая запущен­ная задача является изолированной и независимой. В WINDOWS 95 SI всегда будет содержать 0. Кроме упомянутого, данная процедура заполняет резервный заголовок сегмента данных.

INITAPP - инициализирует очередь событий для данного приложения. Вызов: PUSH DI номер задачи

CALL INITAPP

В случае ошибкиданная функция возвращает 0, иначе не нулевое значение. waitevent- проверяет наличие событий для указанного приложения. Если со­бытие есть, то оно удаляется из очереди.

PUSH АХ - номер приложения,  если О то текущее.

CALL WAITEVENT

Итак, в начале сегмента кода имеем следующий фрагмент.

CALL INITTASK '                  /инициализировать задачу

OR АХ, АХ ;СХ - границы стека

JZ TO_OS ;

MOV HPREV,SI /номер предыдущего прил.

MOV HINST,DI /номер для новой задачи MOV WORD   PTR LPSZCMD,BX       ;ES:BX -   адрес  командной строки MOV WORD  PTR LPSZCMD+2,ES /

MOV параметр

PUSH 0 /текущая задача

CALL WAITEVENT /очистить очередь событий PUSH HINST

CALL   INITAPP /инициализировать приложения OR АХ,АХ

JZ TO OS

CALL MAIN ;запуск основной части

_TO_OS: MOV АН,4CH

CALL DOS3CALL ;   выйти из программы

Естественно, все используемые здесь функции должны быть описаны как внешние. В начало программы нужно вставить следующие строки.

EXTRN INITTASKrFAR EXTRN INITAPP:FAR EXTRN  WAITEVENT:FAR EXTRN   DOS3CALL:FAR

2. Определение классов окон. В Вашей программе обязательно будет, по крайней мере, одно окно. Любое конкретное окно принадлежит какому-либо классу. Класс сна­чала должен быть зарегистрирован. После регистрации можно создавать любое коли­чество экземпляров оконданного класса. Есть уже определенные классы - такие окна можно создавать сразу. Итак, ниже приводится фрагмент, который регистрирует класс окна. Заметим, однако, что задача может не иметь окон вовсе. Такая задача не будет представлена на нижней панели окна. Однако список задач, который можно получить, например, по СгаЕ+АЕТ+ОЕЕ, будет содержать и эту задачу.

LEA

: стиль окна в STYLE

MOV AX, STYLE

MOV WNDCLASS.STYLE,   AX   /Значения стилей см.ниже

;процедура обработки

MOV ВХ,OFFSET WNDPROC

MOV WORD  PTR WNDCLASS.LPFNWNDPROC, BX

MOV BX,SEG WNDPROC

MOV WORD PTR BX

XOR

;резервные байты  в   конце  резервируемой структуры

MOV WNDCLASS.CBCLSEXTRA, АХ

;резервные байты в   конце   структуры для  каждого окна

MOV WNDCLASS.CBWNDEXTRA, АХ

окна отсутствует

MOV WNDCLASS.HICON, АХ

/номер запускаемой задачи

MOV

MOV WNDCLASS.HINSTANCE, АХ

;определить номер   стандартного курсора

PUSH PUSH PUSH CALL MOV

;определить номер PUSH

CALL

;цвет фона

MOV

;имя меню из XOR MOV MOV

DS

CURSOR LOADCURSOR

WNDCLAS S.HCURSOR, АХ

стандартного объекта О ;WHITE_BRUSH

GETSTOCKOBJECT

WNDCLASS.HBRBACKGROUND, AX

файла ресурсов    (отсутствует  = NULL) АХ, АХ

WORD  PTR WNDCLASS.LPSZMENUNAME,АХ WORD   PTR WNDCLASS.LPSZMENUNAME+2, AX ; указатель на строку,  содержащую имя класса MOV ВХ, OFFSET С LAS NAME

MOV WORD PTR WNDCLASS.LPSZCLASSNAME,BX

MOV WORD PTR WNDCLASS.LPSZCLASSNAME+2,DS

; вызов процедуры регистрации PUSH DS     /указатель на PUSH DI      /структуры WNDCLASS CALL REGISTERCLASS

После того как класс был зарегистрирован, структура \У^С1А88 более не нуж­на. Так что эта структура может создаваться в блоке памяти, который потом можно освободить. После того как все классы зарегистрированы, обычно создают главное окно задачи, остальные окна создаются по мере необходимости. Ниже приведена про­цедура создания окна.

АРІ предлагает две функции создания окон— CREATEWINDOW и CREATE-WINDOWEX. Вторая функция отличается от первой только наличием параметра (двой­ное слово) расширенного стиля окна. Поэтому мы рассмотрим только первую функ­цию. В Си-нотации она имеет вид:

Н^О   С1*ЕАТЕИ1ЫЬХЭТ»ЕХ^ЕХЗТП|Е,    ЕР82СЕА88КАМЕ, LPSZWINDOWNAME,

DWSTYLE,   X,   У,   NWIDTН,   NНЕIGНT,   НWNDРАRЕNT,   НМЕЖ1, ШШТ, LPVCREATEPARAMS)

Не надо пугаться вида этой функции, впрочем, венгерская нотация любого сведет с ума. Вот вызов этой функции на ассемблере:

расширенного  стиля окна

MOV   BX,HIGHWORD EX_STYLE

PUSH ВХ

MOV   BX,LOWWORD EX_STYLE ; адрес строки-имени класса окна PUSH DS LEA  ВХ,RNAME PUSH BX

/адрес строки-заголовка окна PUSH DS

LEA BX,SZAPPNAME

PUSH BX ; стиль окна

MOV BX,HIGHWORD STYLE

PUSH BX

MOV     BX,LOWWORD STYLE

PUSH BX

;координата Х левого  верхнего угла

PUSH XSTART ;координата Y левого верхнего угла

PUSH YSTART

окна PUSH DXCLIENT окна

PUSH DYCLIENT

/номер окна-родителя PUSH О

/номер   (идентификатор)   меню окна

PUSH О

;номер задачи

PUSH HINST

/адрес блока параметров окна (нет)

PUSH О PUSH О

CALL CREATEWINDOWEX

Каквидите, ничего сложного, и можно перейти кследующей части кода. Но преж­де замечу, что Вы можете в своей программе использовать вызов обычных прерыва­ний, как в MS DOS. Дело в том, что WINDOWS поддерживает стандартные DOS-овские прерывания. Скажем, вместо CALL DOS3CALL можно просто ставить INT 21Н. Особенно полезными будут функции работы с файлами. Конечно, надо иметь в виду, какие функции (и прерывания) имеют смысл для WINDOWS, а какие нет.

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

LOOP1:

/извлечение сообщения из очереди PUSH DS

LEA на структуру

PUSH ВХ /сообщения

PUSH О PUSH О

PUSH О

CALL GETMESSAGE ; проверка - не получено сообщение "выход" СМР АХ, О JZ NOLOOP1

/перевод всех пришедших сообщений к стандарту ANSI

PUSH DS LEA BX,MSG PUSH BX

CALL TRANSLATEMESSAGE /указать  WINDOWS   передать   данное   сообщение соответствующему окну

PUSH DS

LEA BX,MSG PUSH BX

CALL DISPATCHMESSAGE

/замкнуть цикл (петлю)

JMP SHORT LOOP1

4. Данный раздел назван "функцииокон идругие функции". Сдругими функция­ми понятно. Рассмотри более подробно структуру функции окна. Фактически эти фун­кции являются процедурами прерывания, которые обрабатывают сообщения, пришед­шие данному окну как посланные из цикла обработки очереди, так и непосредственно операционной системой. Здесь, как и раньше, нам придется перевестиязык Си на ас­семблер. Опираясь на сведения данной главы и на главы 11,13,15, мы сделаем это без труда. Итак, заголовок функции окнана Си можно представить в виде:

VOID  FUNJtfIND (UNSIGNED INT  HWND,   UNSIGNED  INT MES,   INT WPARAM, LONG LPARAM).

He представляет никакого труда написать структуру такой функции на языке ас­семблера:

FUNWIND PROC PUSH ВР MOV BP,SP

MOV АХ, [ВР+ОЕН]      /HWND - номер приложения MOVAX, [ВР+ОСН]        /MES  -  номер сообщения MOV АХ, [ВР+ОАН]      /WPARAM - параметр сообщения MOVAX, [ВР+8] /старшее слово параметра LPARAM

MOVAX, [ВР+6] /младшее слово параметра LPARAM

t

POP ВР

RET 10 /освобождаем стек

FUNJfIND ENDP

Как видите, все довольно просто. И мы практически готовы написать простую программудля работы в WINDOWS. Конкретные значения параметров, констант, как и раньше, будем узнавать в процессе программирования.

IV. Пример программы.

Изложенный выше материал позволяет написать простую программу (Рис. 23.4). Программа открывает стандартное окно с обычными атрибутами управления: закры­тие, минимизация, распахивание, перемещение и изменение размеров. Константы (стиль класса, стиль окна, вид курсора и т.д.) можно взять в каком-нибудь из файлов: WINDOWS.Н, WIN.INC или прямо из системы помощи по Си, что мы и сделали. На­пример, стиль окна взят в виде: OOOCFOOOOH.

Эта константа означает следующее:

OOOCFOOOOH - WSJЭVERLAPPEDWINDOWзадает тип окна:

1) - с заголовком и бордюром,

2) - с полосой для заголовка,

3) - с управляющими элементами (правый верхний угол),

4) - с толстой окаемкой вокруг окна,

5) - с кнопками минимизации и максимизации.

Используя помощь к Borland Си, Вы можете выяснить смысл и значение других констант. Обращу Ваше внимание натот факт, что каждому свойству в 4-байтной кон­станте соответствует свой бит. Мы намеренно ставим директиву .286, чтобыподчерк-нуть, что пока работаем в режиме.

.286

.DOSSEG

DGROUP GROUP DATA, STA

ASSUME CS:CODE,   DS:DGROUP ;внешние процедуры

EXTPJM   INITTASK: FAR

EXTRN   INITAPP:FAR

EXTRN   WAIT/EVENT: FAR

EXTRN   DOS 3CALL:FAR . EXTRN REGISTERCLASS:FAR

EXTRN   LOADCURSOR:FAR

EXTRN   GETSTOCKOBJECT:FAR

EXTRN GETMESSAGE:FAR

EXTRN   TRANSLATEMESSAGE:FAR

EXTRN   DISPATCHMESSAGE:FAR

EXTRN   CREATEWINDOW:FAR

EXTRN CREATEWINDOWEX:FAR

EXTRN UPDATEWINDOW:FAR

EXTRN SHOWWINDOW:FAR

EXTRN POSTQUITMESSAGE:FAR

EXTRN DEFWINDOWPROC:FAR

;шаблоны

 

 

 

 

WNDCL

STRUCT

 

STYLE

DW

0

;стиль класса

окна

LPFNWNDPROC

DD

0

/указатель на

процедуру

CBCLSEXTRA

DW

О

 

 

CBWNDEXTRA

DW

О

 

 

HINSTANCE

DW

О

 

 

HICON

DW

О

 

 

HCURSOR

DW

О

 

 

HBRBACKGROUND

DW

О

 

 

LPSZMENUNAME

DD

О

/указатель на

строку

LPSZCLASSNAME

DD

О

на

строку

WNDCL

ENDS

 

 

 

MESSA

STRUCT

 

 

HWND

DW

0

 

 

MESSAGE

DW

о

 

 

WPARAM

DW

?

 

 

LPARAM

DD

•>

 

 

TIME

DW

?

 

 

X

DW

•>

 

 

Y

DW

о

 

 

MESSA ENDS

стека

STA SEGMENT  STACK 'STACK' DW   2000 DUP(?)

STA ENDS ;сегмент данных DATA   SEGMENT   WORD 'DATA'

/вначале 16 байт - резерв, необходимый 16-битному приложению /для правильной работы в  среде Windows

DWORD О

WORD 5

WORD       5 DUP (0) HPREV      DW ? HINST       DW ?

LPSZCMD DD ? CMDSHOW DW ?

для  создания класса WNDCLASS   WNDCL <>

/структура сообщения MSG MESSA О

/имя класса окна CLAS_NAME DB   'PRIVET',О

окна

APP_NAME DB   'FIRST PROGRAM.', 0

;тип курсора

CURSOR EQU 00007FOOH

окна

STYLE  EQU OOOCFOOOOH

окна XSTART  DW 100 YSTART  DW 100 DXCLIENT   DW 300 DYCLIENT   DW 200

DATA ENDS

; сегмент кода CODE  SEGMENT WORD 'CODE' _BEGIN:

; I.   Начальный код

CALL INITTASK

OR AX,AX

JZ _ERR

MOV HPREV,SI

MOV HINST,DI

MOV WORD  PTR LPSZCMD,BX строки

MOV WORD PTR LPSZCMD+2,ES MOV CMDSHOW,DX

PUSH 0

CALL WAITEVENT

PUSH HINST

CALL INITAPP OR

JZ _ERR

CALL MAIN

_TO_OS

MOV АН,4ch CALL dos3call

_ERR:

CALL BEEP JMP SHORT _TO_OS

;основная процедура . ***************************

;инициализировать задачу ; СХ -   границы стека

;номер предыдущего прил. ;номер для новой задачи ;ЕЗ:ВХ -   адрес командной

г

;экранный параметр задача

;очистить очередь событий ;инициализировать приложения

основной части

выйти из программы

************************

MAIN PROC

/II. Регистрация ; стиль окна NULL

окна - стандартное

MOV

;процедура обработки

LEA BX,WNDPROC

MOV WORD PTR WNDCLASS.LPFNWNDPROC, BX

MOV BX,CS

MOV WORD PTR WNDCLASS.LPFNWNDPROC+2, BX

;резервные байты в  конце резервируемой структуры

MOV WNDCLASS.CBCLSEXTRA, О

/резервные байты в конце структуры для каждого ок

MOV WNDCLASS.CBWNDEXTRA, О

/иконка окна отсутствует

MOV WNDCLAS S.НICON, О

./номер  запускаемой задачи

MOV AX,HINST

MOV WNDCLASS.HINSTANCE, АХ

/Определить номер стандартного курсора

О

DS

CURSOR LOADCURSOR

WNDCLASS.HCURSOR, АХ

номер  стандартного объекта О ;WHITE_BRUSH

GETSTOCKOBJECT

PUSH PUSH PUSH CALL MOV /определить PUSH CALL /цвет фона

MOV WNDCLASS.HBRBACKGROUND, AX

/имя меню из файла ресурсов   (отсутствует = NULL) MOV WORD PTR WNDCLASS.LPSZMENUNAME, О

MOV WORD PTR WNDCLASS.LPSZMENUNAME+2, 0

/указатель на строку,   содержащую имя класса LEA BX,CLAS_NAME

MOV WORD PTR WNDCLASS.LPSZCLASSNAME, BX

MOV WORD PTR WNDCLASS.LPSZCLASSNAME+2, DS

/вызов процедуры регистрации PUSH DS       /указатель на LEA DI,WNDCLASS

PUSH DI    /структуры WNDCLASS

CALL REGISTERCLASS CMP AX,0 JNZ OKI

окно

; звуковой сигнал при ошибке CALL BEEP

RET ;ошибка при регистрации

_ОК1 :

/III.   Создание окна ; адрес  строки-имени  класса окна PUSH DS

LEA  ВХ,CLAS_NAME

PUSH BX

;адрес  строки-заголовка окна

PUSH DS LEA

PUSH BX

окна

MOV ВХ,HIGHWORD STYLE

PUSH BX

MOV STYLE PUSH BX

/координата Х левого  верхнего угла

PUSH XSTART /координата Y левого верхнего угла

PUSH YSTART

окна PUSH DXCLIENT окна

PUSH DYCLIENT

;номер окна-родителя PUSH О

; номер   (идентификатор)    меню окна

PUSH О

;номер задачи

PUSH HINST

;адрес блока  параметров   окна (нет)

PUSH О PUSH О CALL

CMP АХ,О

JNZ NO_NULL

CALL BEEP

RET при создании окна

;установка для окна состояния видимости   (окно или ;пиктограмма)   согласно параметру CMDSHOW и его отображение NO_NULL:

MOV SI,АХ

PUSH SI

PUSH CMDSHOW

CALL SHOWWINDOW ;посылка команды обновления области окна   (команда WM_PAINT) /сообщение посылается непосредственно окну

PUSH SI

CALL UPDATEWINDOW

;IV. Цикл ожидания LOOP1: ■

;извлечение сообщения  из очереди

PUSH DS

LEA на структуру

PUSH ВХ ; сообщения

PUSH О

PUSH О

PUSH О

CALL GETMESSAGE /проверка - не получено сообщение "выход" СМР АХ,О

JZ NO_LOOP1

/перевод всех  пришедших  сообщений  к  стандарту ANSI

PUSH DS

LEA BX,MSG

PUSH BX

CALL TRANSLATEMESSAGE /указать WINDOWS   передать   данное   сообщение соответствующему окну

PUSH DS

LEA BX,MSG

PUSH BX

CALL DISPATCHMESSAGE

/замкнуть цикл (петлю) JMP  SHORT LOOP1 NO_LOOPl:

RET

MAIN ENDP

/процедура для заданного класса окон /WINDOWS передает в эту процедуру параметры: ;HWND - дескриптор   (номер)   окна,   тип WORD /MES -  номер сообщения,   тип WORD

;WPARAM - дополнительная информация о сообщении,   тип WORD ; LP ARAM -  дополнительная информация  о  сообщении,   тип DWORD WNDPROC PROC PUSH ВР MOV BP,SP

MOV AX,   [BP+OCH]        ;MES - номер сообщения

CMP AX, 2     ;не сообщение ли о  закрытии   <2-сообщение о

закрытии)

JNZ NEXT

;передать сообщение  о закрытии приложения,   это сообщение /будет принято в цикле ожидания,   и т.о.   приложение завершит свой путь

PUSH О

CALL POSTQUITMESSAGE JMP _QUIT

NEXT:

сообщение дальше

;своего рода правило вежливости - то,   что не обработано

/процедурой обработки, предоставляется   для обработки /WINDOWS

PUSH   [BP+OEH] /HWND

PUSH   [BP+OCH] ;MES - номер сообщения

PUSH   [BP+OAH] ;WPARAM

PUSH    [BP+8] / HIGHWORD LP ARAM

PUSH    [BP+6] /LOWWORD LPARAM

CALL DEFWINDOWPROC .***********************************************

_QUIT:

POP BP

/вызов процедуры окна  всегда дальний,   поэтому RETF

RETF  10 /освобождаем стек от параметров

WNDPROC   ENDP     • ' сигнал

ВЕЕР PROC

MOV АН, 2 MOV DL, 7

CALL  DOS3CALL   /INT 21H RET

BEEP ENDP CODE ENDS

END _BEGIN

Рис. 24.3. Простая программа, открывающая окно.

V. Простейшие органы управления.

DGROUP

Органы управления располагаются в текущем окне и относятся к предопределен­ным классам. Следовательно, органы управления регистрировать не надо - ихдостаточ-но создать. И это хорошо! Органы управления также могут (и должны) получать сооб­щения (например, щелчок мышью по кнопке). Замечательно, однако, то, что для них не нужно создавать свои процедуры обработки. Сообщение будет приходить в процедуру окна, где расположен орган управления, и должно распознаваться этой процедурой. Отличитьже один орган отдругого можно, анализируя значения параметров LPARAM и WPARAM. Видите, как замечательно! Одним "движением руки" вы создаете на экране кнопку, еще одно движение - и кнопка начинает работать. Ну что ж, приступим.

.286

.DOSSEG DGROUP GROUP DATA, STA

ASSUME CS.-CODE, ; внешние процедуры EXTRN INITTASK:FAR EXTRN INITAPP:FAR EXTRN WAITEVENT:FAR EXTRN DOS3CALL:FAR EXTRN REGISTERCLASS:FAR EXTRN  LOADCURSOR:FAR EXTRN GETSTOCKOBJECT:FAR EXTRN GETMESSAGE:FAR EXTRN  TRANSLATEMESSAGE:FAR EXTRN DISPATCHMESSAGE:FAR CREATEWINDOW:FAR CREATEWINDOWEX:FAR UPDATEWINDOW:FAR SHOWWINDOW:FAR POSTQUITMESSAGE:FAR DEFWINDOWPROC:FAR

EXTRN

EXTRN EXTRN EXTRN EXTRN

EXTRN ;шаблоны WNDCL

STYLE

LPFNWNDPROC

CBCLSEXTRA CBWNDEXTRA HINSTANCE

HICON

HCURSOR

HBRBACKGROUND

LPSZMENUNAME

LPSZCLASSNAME

STRUCT

DW

DD

DW

DW

DW DW DW DW

DD

о о О О О О О О о

класса окна ;указатель на процедуру обработки

на строку DD на строку

WNDCL

MESS A

HWND

ENDS

STRUCT

DW 7

WPARAM LPARAM

TIME X

MESSAGE

DW 7

dw 7

dd 9

Y

MESSA

dw 7

dw 7

DW 7

ENDS

стека

STA  SEGMENT   STACK 'STACK' DW 2000 DUP(?)

STA ENDS

данных

DATA SEGMENT WORD 'DATA' ;16 байт  - резерв

DWORD O WORD Б

WORD      5  DUP (0) HPREV      DW ? HINST      DW ?

LPSZCMD DD ? CMDSHOW DW ?

для   создания класса WNDCLASS WNDCL <> ; структура сообщения

MSG MESSA О

класса   основного окна CLASJTAME   DB    1 PRIVET *,0

предопределенного класса PRED_CLAS DB   'BUTTON1,0

заголовок окна APP_NAME  DB   0 ;НЄТ

на кнопке BUT_NAME   DB    'QUIT',0

тип курсора CURSOR  EQU OOOO7FOOH окна

STYLE   EQU     080000000H   OR 000400000H

кнопки

STYLE_BUT   EQU   040000000H  OR 010000000H параметры окна

XSTART DW lOO YSTART DW lOO

DXCLIENT   DW 300

DYCLIENT  DW 200 data ends ;сегмент кода code  segment word     'code' _begin:

код

CALL INITTASK or ax,ax JZ _ERR MOV hprev,si

MOV HINST,DI MOV WORD  PTR LPSZCMD,BX

строки

MOV WORD  PTR lpszcmd+2,es

MOV CMDSHOW,DX PUSH 0

CALL WAITEVENT

PUSH hinst

CALL INITAPP OR

JZ _ERR

CALL MAIN

_TO_OS:

mov ah,4CH

CALL

_ERR:

CALL BEEP

JMP SHORT _TO_OS ;основная процедура

t ****************************************************

MAIN PROC

; Регистрация класса окна

; стиль окна NULL   -   стандартное окно

MOV wndclass.style,О

;процедура обработки

LEA

mov        word ptr bx mov bx,cs

mov        word ptr bx

/инициализировать задачу ; СХ -  границы стека

;номер предыдущего прил. ;номер для  новой задачи ;Е5:ВХ - адрес командной

; экранный параметр ; текущая задача

/очистить очередь событий

; инициализировать приложения

основной части

выйти из программы

/резервные байты  в   конце  резервируемой структуры

MOV wndclass.cbclsextra, О

/резервные байты  в   конце   структуры для   каждого окна

MOV vjndclass. cbwndextra, окна отсутствует MOV WNDCLASS.HICON, О

/номер запускаемой задачи MOV AX,HINST MOV WNDCLASS.HINSTANCE,AX

номер  стандартного курсора

PUSH О

PUSH DS

PUSH CURSOR

CALL LOADCURSOR

MOV WNDCLASS.HCURSOR,

номер стандартного

AX объекта

PUSH

CALL /цвет фона

MOV /имя меню из

MOV

MOV

;указатель на

LEA

MOV MOV

O

GETSTOCKOBJECT

WNDCLASS.HBRBACKGROUND, AX

файла ресурсов    (отсутствует = NULL) WORD  PTR WNDCLASS.LPSZMENUNAME, О WORD  PTR WNDCLASS.LPSZMENUNAME+2, 0

строку,   содержащую имя класса ВХ,CLAS_NAME

WORD  PTR WNDCLASS.LPSZCLASSNAME,BX WORD  PTR WNDCLASS.LPSZCLASSNAME+2, DS /вызов процедуры регистрации

PUSH DS      /указатель на ■ LEA  DI, WNDCLASS

PUSH  DI        /Структуры WNDCLASS CALL REGISTERCLASS CMP AX,0

JNZ _OK1

сигнал

CALL BEEP RET

_OKl:

окна

строки-имени класса окна

PUSH DS

LEA  BX,CLAS_NAME

PUSH BX

строки-заголовка окна

PUSH DS

LEA  BX,APP_NAME

PUSH BX

окна

MOV BX,HIGHWORD STYLE

PUSH BX

при ошибке

при регистрации

MOV STYLE PUSH BX

/координата Х левого верхнего угла PUSH XSTART

Y левого  верхнего угла

PUSH YSTART

окна

PUSH DXCLIENT

окна

PUSH DYCLIENT

окна-родителя

PUSH О

;номер   (идентификатор)   меню окна

PUSH О

/номер задачи

PUSH HINST

блока параметров  окна (нет) PUSH О PUSH О

CALL CREATEWINDOW CMP АХ,О

JNZ NO_NULL CALL BEEP

RET при создании окна

для  окна  состояния видимости  (окно или /пиктограмма)   согласно параметру CMDSHOW и его отображение NO_NULL:

MOV

PUSH SI PUSH CMDSHOW

CALL SHOWWINDOW /посылка команды обновления  области  окна    (команда WM_PAINT) ' / сообщение  посылается   непосредственно окну

PUSH SI

CALL UPDATEWINDOW

/ Цикл ожидания L0OP1:

/извлечение сообщения  из очереди

PUSH DS

LEA BX,MSG .   /указатель на структуру PUSH ВХ / сообщения

PUSH О PUSH О PUSH О

CALL GETMESSAGE

Глава 14. Начала программирования для WINDOWS

; проверка -  не получено сообщение "выход" СМР АХ, О

JZ NO_LOOP1

/перевод всех пришедших  сообщений к  стандарту ANSI PUSH DS LEA BX,MSG

PUSH BX

CALL TRANSLATEMESSAGE /указать WINDOWS   передать   данное   сообщение соответствующему ; окну

PUSH DS

LEA BX,MSG

PUSH BX

CALL DISPATCHMESSAGE /замкнуть цикл (петлю)

JMP SHORT LOOP1

NO_L00Pl:

RET

MAIN ENDP

/процедура для заданного класса окон /WINDOWS передает в эту процедуру параметры: /HWND - дескриптор   (номер)   окна,   тип WORD ;MES - номер сообщения,   тип WORD

,-WPARAM -  дополнительная информация  о  сообщении,   тип WORD ; LP ARAM - дополнительная информация о сообщении,   тип DWORD

WNDPROC PROC

PUSH ВР ■ MOV BP,SP MOV AX, [BP+OCH]        /MES -  номер сообщения

CMP AX, 2     /не сообщение ли о закрытии   (2   -  сообщение о

закрытии)

JNZ NEXT1

/передать сообщение  о  закрытии приложения,   это сообщение /будет принято в цикле ожидания,   и т.о.   приложение завершит / свой путь PUSH О CALL POSTQUITMESSAGE JMP _QUIT

NEXT1:

CMP AX, 1 /WM_CREATE

JNZ NEXT2

контрольных элементов строки-имени класса окна

PUSH DS

LEA BX,PRED_CLASстроки-надписи на кнопке PUSH DS

LEA   BX,BUT_NAME

PUSH BX

окна

MOV  BX,HIGHWORD STYLE_BUT

PUSH BX

MOV     BX,LOWWORD STYLE_BUT

PUSH BX

/координата X левого  верхнего угла PUSH 2O

У левого верхнего угла

PUSH 2O

окна PUSH 6O окна

PUSH 2O

окна-родителя PUSH [BP+OEH] /номер   (идентификатор) кнопки

PUSH l

задачи

PUSH HINST

блока параметров окна (нет) PUSH О PUSH О

CALL CREATEWINDOW

NEXT2:

CMP

JNZ NEXT

CMP WORD  PTR   [BP+0AH],1    /идентификатор кнопки

JNZ NEXT

/кнопка нажата  и Выход

PUSH О

CALL POSTQUITMESSAGE

NEXT:

сообщение дальше WINDOWS

/своего рода  правило  вежливости  -   то,   что  не обработано /процедурой обработки   предоставляется   для обработки ; WINDOWS

PUSH [BP+OEH]

PUSH [ВР+ОСН] /MES -  номер сообщения

PUSH [ВР+ОАН] /WPARAM

PUSH [ВР+8] /HIGHWORD LP ARAM

PUSH   [ВР+б] ;LOWWORD LPARAM

CALL DEFWINDOWPROC .*********************************************

_QUIT:

POP BP

;вызов процедуры окна  всегда  дальний,   поэтому RETF

RETF   10 /освобождаем стек от параметров

WNDPROC endp ;звуковой сигнал. веер proc

mov АН, 2

mov  DL, 7

CALL DOS3CALL

ret

веер endp CODE ENDS

END _BEGIN

Puc. 24.4. Пример простейшего органа управления.

Данная программа получена из программы на Рис. 24.3, во-первых, изменением стиля окон, во-вторых, добавлением в окно элемента управления - кнопки. Чтобы уп­ростить анализ, замечу следующее:

1. элемент управления открывается как новое окно, но с предопределенным классом, имена всехтаких классов известны и определены в самой операционной системе;

2. при создании окнав ее процедурууправления посылается сообщение WM_CREATE,

по приходу которого мы и создаем нашу кнопку;

3. создавая элемент управления, мы указываем системе идентификатор окна, которо­му она принадлежит;

4. одновременно мы присваиваем нашей кнопке свой идентификатор (1);

5. по приходу в процедуру окна сообщения WM_COMMAND мы проверяем WPARAM, принажатиикнопкионравенидентификаторукнопки,т.е.внашем случае 1.

А теперь маленькая хитрость, которую, возможно, Вы уже разгадали. Если Вы пишите программу с помощью какого-нибудь DOS-овского редактора, у Вас будут проблемы при выводе русских букв. Дело в кодировке. Проблема решается весьма просто: зайдите в какой-нибудь редактор текстовых файлов, работающий под WINDOWS, например, редактор Си++, и напечатайте там все нужные русские строки.

VI. Программа-таймер.

Программа, представленная ниже, выводит окно, на котором высвечивается теку­щее время: чч:мм:сс. Я думаю, что Вы уже способны оценить средства программирова­ние в средеWINDOS. Согласитесь, что под MS DOS подобную программу (аонадолж-на быть резидентной), написать несколько сложнее. Впрочем, я беру на себя смелость заявить, что и значительного облегчения в программировании Вытоже не найдете. .

DS:DGROUP

.286

.DOSSEG

DGROUP GROUP

DATA, STA

ASSUME

процедуры

EXTRN

EXTRN   SELECTOBJECT:FAR

EXTRN EXTRN

EXTRN BEGINPAINT:FAR

EXTRN EXTRN EXTRN EXTRN EXTRN EXTRN EXTRN

EXTRN REGISTERCLASS:FAR

EXTRN

EXTRN  GETSTOCKOBJECT:FAR

EXTRN EXTRN EXTRN EXTRN

EXTRN   UPDATEWINDOW:FAR

EXTRN EXTRN EXTRN /шаблоны

для

LOGFON _HEIGHT _WIDTH _ESCAPEMENT

_ORIENTATION _WEIGHT

_ITALIC _UNDERLINE _STRIKEOUT _CHARSET

OUTPRECISION

установки фонтов STRUCT

_CLIPPRECISION QUALITY

DW DW

DW DW DW DB DB DB DB DB DB DB

_PITCHANDFAMILY _FACENAME

LOGFON

для

WNDCL

STYLE

LPFNWNDPROC CBCLSEXTRA CBWNDEXTRA

HINSTANCE

HICON

HCURSOR HBRBACKGROUND

LPSZMENUNAME LPSZCLASSNAME

WNDCL

DB DB ENDS

класса окна

STRUCT

0

32

DUP(0)

DW

DD DW

DW DW DW DW DW

DD DD ENDS

класса окна указатель  на процедуру обработки

указатель указатель

на на

строку строку

определяющая  прямоугольную область

RECT

LEFT

TOP

RIGHT

BOTTOM

RECT

STRUCT

DW DW DW DW

ENDS

?

?

DUP

;структура для

PAINTSTRUCT HDC FERASE RCPAINT FRESTORE

FINCUPDATE

RGBRESERVED

PAINTSTRUCT

;структура* для MESS A

HWND

MESSAGE

WPARAM

LPARAM

TIME DW X DW Y DW MESSA ENDS

;сегмент стека STA  SEGMENT   STACK 'STACK'

DW 2000 DUP(?)

перерисовки прямоугольной STRUCT

области

DW DW

RECT DW DW

DB l6 ENDS

системных

STRUCT

DW DW DW DD

O

(?)

сообщений

7 7

7

7 7 •> о

STA ENDS

;сегмент данных

DATA  SEGMENT WORD 'DATA'

DWORD 0

WORD 5

WORD 5 DUP (0) HPREV DW ? HINST DW ? LPSZCMD DD ? CMDSHOW DW ? hwnd dw ? HDC    DW.?

HFONT DW ?

;буфер для вывода информации BUFER DB   1      :     :     ' , 0

/структура для   задания шрифта LOGFONT  LOGFON  О '

;структура для   создания класса WNDCLASS   WNDCL   О   .'

;структура сообщения

MSG MESSA О

;прямоугольник для   вывода текста

RECTA RECT О

/структура для BEGINPAINT

PAINT  PAINTSTRUCT О

;имя класса   основного окна CLAS_NAME DB   1 PRIVET', О

окна

APP_NAME DB  0 ;нет ;тип курсора

CURSOR  EQU   0 00 07FOOH окна

STYLE   EQU 000080000H STYLE_EX     EQU 000000008H ; параметры окна .

XSTART DW 100 YSTART DW 100

DXCLIENT DW 210 DYCLIENT. DW 9 0

/ _ _ ~ ***" '" "~

DATA ENDS

кода

CODE   SEGMENT WORD 'CODE' BEGIN:

/Начальный код

CALL INITTASK

OR AX,AX

JZ _ERR MOV HPREV,SI

MOV HINST,DI MOV WORD   PTR LPSZCMD,BX

строки

MOV WORD   PTR LPSZCMD+2,ES MOV CMDSHOW,DX

PUSH O

CALL WAITEVENT PUSH

CALL INITAPP

OR AX,AX

JZ _ERR

CALL MAIN

_TO_OS:

MOV

CALL DOS3CALL _ERR:

CALL BEEP

JMP SHORT _TO_OS

процедура

•і******************************** it ****************** /

MAIN PROC

;Регистрация класса окна /стиль окна NULL -  стандартное окно WNDCLASS.STYLE,О

обработки

ВХ,WNDPROC

WORD  PTR WNDCLASS . LPFNWNDPROC, ВХ

BX,CS

WORD   PTR WNDCLASS .LPFNWNDPROC+2, BX

/инициализировать задачу ;СХ -  границы стека

І

;номер предыдущего прил.

/номер для новой задачи ;ЕЗ:ВХ -  адрес командной

; экранный параметр ; текущая задача ;очистить очередь событий

/инициализировать приложения

;запуск основной части ;   выйти из программы

MOV •процедура

LEA MOV MOV MOV

/резервные MOV

/резервные

MOV /иконка окна

MOV

байты в  конце резервируемой структуры

WNDCLASS.CBCLSEXTRA, О байты  в  конце  структуры для  каждого ок ШО^АЭ Э . СВ*ШОЕХТРА, О

отсутствует WNDCLASS.HICON, О

запускаемой задачи

MOV AX,HINST

MOV WNDCLASS.HINSTANCE,АХ

О

GETSTOCKOBJECT

номер   стандартного курсора

PUSH О

PUSH DS

PUSH CURSOR

CALL LOADCURSOR

MOV WNDCLASS.HCURSOR, AX

номер   стандартного объекта

PUSH

CALL

;цвет фона

MOV

меню из

MOV MOV

на

LEA MOV MOV

WNDCLASS.HBRBACKGROUND, AX

файла ресурсов    (отсутствует  = NULL) WORD PTR WNDCLASS.LPSZMENUNAME, О WORD  PTR WNDCLASS.LPSZMENUNAME+2, 0

строку,    содержащую имя класса BX,CLAS_NAME

WORD PTR WNDCLASS.LPSZCLASSNAME, BX WORD  PTR WNDCLASS.LPSZCLASSNAME+2, DS ;вызов процедуры регистрации PUSH DS      ; указатель на LEA DI, WNDCLASS

PUSH DI        ; структуры WNDCLASS CALL REGISTERCLASS CMP AX,0 JNZ _OK1 /звуковой сигнал  при ошибке

CALL BEEP

RET при регистрации

_ОК1:

окна

/расширенный стиль   -   всегда  поверх других MOV       BX,HIGHWORD STYLE_EX

PUSH BX

MOV       BX,LOWWORD STYLE_EX

PUSH BX

строки-имени   класса окна

PUSH DS

LEA  BX,CLAS_NAME

PUSH BX

строки-заголовка окна

PUSH DS

LEA BX,APP_NAME

;стиль окна

MOV  BX,HIGHWORD STYLE PUSH ВХ

MOV    BX,LOWWORD STYLE

PUSH BX

;координата Х левого  верхнего угла

PUSH XSTART /координата Y левого  верхнего угла

PUSH YSTART

окна

PUSH DXCLIENT

; высота окна •

PUSH DYCLIENT ;номер окна-родителя PUSH О

; номер   (идентификатор)   меню окна

PUSH О ;номер задачи

PUSH HINST

;адрес блока параметров  окна (нет) PUSH 0 •

PUSH О

CALL CREATEWINDOWEX CMP AX,0

JNZ NO_NULL

CALL BEEP

RET при создании окна

для  окна  состояния  видимости    (окно или ; пиктограмма)   согласно параметру CMDSHOW и его отображение NOJtfULL:

MOV MOV

PUSH SI PUSH CMDSHOW CALL

; посылка  команды обновления  области  окна    (команда WM^PAIN'U посылается   непосредственно окну

PUSH

CALL UPDATEWINDOW

/заполнение структуры фонта    (не нулевые поля) MOV LOGFONT._HEIGHT,    6 0

MOV LOGFONT._WEIGHT,400 ;FW_NORMAL

MOV LOGFONT.   PITCHANDFAMILY,20H ; FF_SWISS

•Цикл ожидания LOOP1:

/ извлечение сообщения из очереди PUSH DS

LEA ВХ,MSG    /указатель на структуру PUSH ВХ /сообщения

PUSH О PUSH О

PUSH О

CALL  GETMES.SAGE /проверка -  не  получено  сообщение "выход" СМР АХ, О JZ NOJLOOP1

/перевод всех  пришедших  сообщений к  стандарту ANSI

PUSH DS

LEA BX,MSG PUSH BX

CALL TRANSLATEMESSAGE /указать WINDOWS  передать  данное  сообщение соответствующему окну

PUSH DS

LEA BX,MSG

PUSH BX

CALL DISPATCHMESSAGE /замкнуть цикл (петлю)

JMP  SHORT  LOO PI N0_L00P1:

RET

MAIN ENDP

/процедура для  заданного класса окон /WINDOWS  передает   в   эту  процедуру параметры: /HWND - дескриптор    (номер)    окна,   тип WORD /MES -  номер  сообщения,   тип WORD

/WPARAM - дополнительная информация о сообщении,   тип WORD / LP ARAM -  дополнительная информация  о  сообщении,   тип DWORD

WNDPROC PROC

PUSH ВР MOV     BP,SP

MOV AX,[BP+OCHJ        /MES  -  номер сообщения

CMP AX, 2     /не  сообщение ли о  закрытии   (2-сообщение о

закрытии)

JNZ NEXT1

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

PUSH О

CALL POSTQUITMESSAGE

JMP QUIT

NEXT1:

CMP

JNZ NEXT2

/установим таймер

PUSH [BP+OEH]

PUSH l PUSH lOOO

PUSH О PUSH О

CALL SETTIMER

JMP QUIT

CREATE создание окна

через   lOOO миллисекунды

NEXT2:

CMP

JNZ NEXT3 JMP QUIT

COMMAND сообщение-команда

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

NEXT3:

CMP AX,113H ;WM_TIMER JNZ NEXT4

/послать команду  перерисовки  области   всего окна /это приведет к посылке  в  очередь  сообщения WM_PAINT

PUSH [ВР+ОЕН]

PUSH О

PUSH О

PUSH 1

CALL INVALIDATERECT JMP QUIT

NEXT4

PAINT

CMP AX,OFH

JNZ NEXT5 PUSH [BP+OEH]

PUSH DS

LEA  BX,PAINT

PUSH BX

CALL BEGINPAINT

MOV HDC,AX

-установка шрифтов

PUSH DS

LEA     BX, LOGFONT

PUSH BX

CALL CREATEFONTINDIRECT перерисовка окна

MOV

PUSH HDC

PUSH AX

CALL SELECTOBJECT

18-4072часы реального времени MOV АН, 2

INT lAH

CALL _BCD

вывести строку PUSH HOC

PUSH l

PUSH 1

PUSH DS

LEA  BX,BUFER

PUSH BX

PUSH 8 символов CALL TEXTOUT

PUSH

PUSH HFONT

CALL SELECTOBJECT

PUSH AX

CALL DELETEOBJECT

r

PUSH HDC

PUSH DS

LEA  BX,PAINT

PUSH BX

CALL ENDPAINT

JMP _QUIT

NEXT5 : NEXT:

/передать  сообщение  дальше WINDOWS

/своего рода правило вежливости - то, что не обработано /процедурой обработки   предоставляется  для обработки

/WINDOWS

HWND

- номер сообщения

WPARAM

HIGHWORD LPARAM

LPARAM

PUSH [BP+OEH] PUSH [BP+OCH] PUSH [BP+OAH]

PUSH [BP+8] PUSH [BP+6]

CALL DEFWINDOWPROC

f. ********************************************* _QUIT:

POP BP

/вызов  процедуры окна  всегда дальний,   поэтому RETF

RETF 10 стек от параметров

WNDPROC ENDP

;звуковой сигнал ВЕЕР PROC

MOV АН, 2

MOV DL,7

CALL DOS3CALL RET

ВЕЕР ENDP

/преобразование числа в BCD-формате в шаблон для печати /времени по часам реального времени _BCD PROC ; часы

MOV AL,CH SHR AL,4 ADD AL,48 AND CH,00001111B CMP CH,10 JB _OK1 ■ ADD AL,1 . SUB CH,10

_0K1:

ADD

MOV BYTE   PTR BUFER,AL MOV BYTE  PTR BUFER+1,CH /минуты

MOV

SHR AL,4

ADD AL,48

AND CL,00001111B

CMP CL,10

JB _OK2 ADD AL,1 SUB CL,10

_0K2 :

ADD

MOV BYTE   PTR BUFER+3,AL MOV BYTE   PTR BUFER+4,CL /секунды

MOV

SHR AL,4 ADD AL,48

AND DH,00001111B CMP DH,10 JB _0K3 ADD AL,1 SUB DH,10

_ОКЗ:

ADD

MOV BYTE  PTR BUFER+ б, AL

MOV BYTE   PTR BUFER+7, DH

RETN

_BCD ENDP

CODE ENDS

END _BEGIN

24.5. Программа-таймер.

Программа достаточно прокомментирована. Описание же функций, которые по­явились в данной программе, Вы найдете и сами. Обращаю внимание только то, что структура программы осталась неизменной по сравнению с предьщущими. Чтобы облегчить анализ, предлагаю учесть следующее:

1. При создании (WM_CREATE) окна мы устанавливаем и таймер. Сообщение таймера (WM_TIMER) будет приниматься опять же процедурой окна.

2. Перед выводом строки в окно мы устанавливаем текущий шрифт. После выво­да - удаляем созданный шрифт. Структура для шрифта (LOGFONT) заполняет­ся нами заранее.

3. По приходу сообщения WMJTIMER мы даем команду для перерисовки окна. После чего в очередь сообщений автоматически ставится сообщение WM_PADNT.