Выпуск 11: Исследуем WinApi |
Попрошу у всех внимания! Перед тем как начать, я хочу извинится. В прошлом выпуске я сделал одну ошибку. В языке Си исключающий ИЛИ записывается не "::", а "^". Раз вопросов нету, я не буду останавливаться на прошлом выпуске. Поскольку вы уже знаете, что такое подпрограммы и как они дейвствуют, мы можем продолжить с нашим проектом WinApi. Но перед этим мы создадим такой проект на Dev-C++ (для Си программистов). Создайте новый файл (как и в предыдущих выпусках). Добавьте модуль windows.h. В Си вам не понадобится отдельный млдуль для сообщений. Теперь добавим три переменные, как и в Дельфи проекте. Обратите внимание, как выглядят типы этих переменных в Си:
WNDCLASSEX Wc; Теперь нам надо создать функцию, в которой будут обрабатываться разные сообщения, например выход из программы и другие. Эта функция называется оконной процедурой. Её имя будет WndProc. Конечно, вы можете назвать её по своему. Тип функции - LResult (LRESULT в Си).
Delphi:
C: Эта функция имеет свои параметры, которых нельзя забывать. Только их не один, или два, как мы привыкли, а четыре. Первый параметр - ссылка на окно. Этот параметр важен, потому что программа может иметь несколько окон. Второй параметр - сообщение. Т.е. это переменная, которая хранит сообщение и потом передаёт его программе, чтобы та смогла отреагировать на него. Третий и четвёртый параметры используются в зависимости от сообщения. Об этом я уже вам говорил (только в другой форме), когда объяснял что это такое сообщение и ссылка. Мы посылали окну блокнота разные сообщения и в функции, которую мы использовали, находились точно такие же параметры. Так что если что нибудь забыли, перечитайте выпуск 05. Хватит болтать, пора создавать эти параметры.
Delphi:
C: Надеюсь вам всё понятно, а если нет - спрашивайте. Если я вопросов не получаю,
это значит что вы всё хорошо поняли, и тогда мне не зачем возвращатся к предыдущим
темам. Я тут упомянул глобальные переменные. Сейчас о них расскажу. Переменные могут быть глобальными и локальными. Какая разница?
Разница в возможностях использования. Точнее - в месте использования. Начну с
примера (в комментариях - перевод на Си, хоть это и не нужно :).
Delphi: Есть ли тут ошибки? Вроде не видно... Синтаксических ошибок и на самом деле нету.
Но посмотрите на места объявлений переменных. Программа явно не будет работать. А
где ошибка то? А ошибка в главном блоке программы. Там программа пробует "достать"
переменную, которую разрешенно трогать только её создавшей процедуре
(переменная y). Т.е. она может быть использованна только в процедуре
Nonsense. Переменная, созданная в подпрограмме называется локальной
и может быть использованна только в этой подпрограмме. Переменная x
созданна в самом начале проекта, перед всеми подпрограммами. Именно по этому
(потому что её не ограничивают командные скобки) эту переменную можно
использовать везде. Такие переменные называются глобальными. С
константами точно так же. Избегайте глобальных переменных! Вы можете сами
незная этого изменить значение не той переменной и этим "погубить" программу.
Кстати, не так как в Дельфи, в Си, когда определяется константа, надо сразу указать её
тип: const int x = 15. Чтобы как можно реже использовать глобальные переменные, мы их переместим в
главный блок/функцию программы. Но это не сейчас. Нам пора познакомится с одним
сообщением. WM_DESTROY называется :) Оно посылается, когда программу
собираются уничтожить. Мягко говоря закрыть :) А мягко потому, что для закрытия
приложения на самом деле посылаются несколько сообщений. Но вам не обязательно
про них знать :) Значит, когда посылается сообщение, оно записывается в переменную.
Наша переменная называется msg. Чтобы закрыть программу, нам нужно
проверить какое сообщение находится в переменной. Логично? Ведь мы не будем
закрывать программу, когда она включается, т.е. в переменной находится сообщение
WM_CREATE. Думаю, это понятно каждому. Так что надо делать? Воспользуемся
условным оператором. Вы бы догадались? Если нет, то вам надо больше практики по
этой теме. Если вы чувствуете, что так оно и есть (только честно!), пишите мне и я дам
вам ещё задач, а также отвечу на возникшие вопросы. Если вы не выучите первые
уроки, то дальше вам лучше не заглядовать! Я всё время говорю, что код создающий
окно OpenGL и полностью приготовляющий его к работе занимает много места. А
сколько места то? Как вам звучит: 7 страниц Word'а!? Чтобы написать такой код
надо не только получать от программирования удовольствие, но и не мало знаний. Не
забудьте про терпеливость! Продолжим. Теперь функция должна выглядить так:
Delphi: Осталось нам "правильно" закрыть программу. Ведь это сообщение и просит её
закрыть :) Конечно мы можем использовать функции из выпуска 05, но есть и другой
способ, который используется более часто. Он также является более удобным и
кратким:
Delphi:
C: На самом деле нам понадобится работать с многими сообщениями. А пока, чтобы
всего не описывать, мы сделаем, чтобы они обрабатывались по умолчанию. Для этого
надо вызвать функцию DefWindowProc, у которой всё те же параметры. Эта
функция сделает всё за вас, только укажите нужные аргументы, т.е. те, которые
используете в оконной процедуре:
Delphi:
C: Теперь уже всё :) Всё, да не совсем :) Программа то рабочая (кроме версии Си, потому
что мы там ещё не создали главную функцию), но само окно ещё не созданно :) Сейчас
всё исправим. Писать придётся больше, но не сложно. Вообще - пустое окно на WinApi
вещь легкая :) Но перед тем, как создать окно, нам надо кое чем пополнить оконную функцию. Для
более правильной работы нам надо добавить в конце (в начале для Си) функции
stdcall (в Си CALLBACK). Теперь функция выглядит так:
Delphi:
C: stdcall = стандартный вызов функции. Нужен, чтобы функции WinApi
написанные на разных языках могли нормально друг с другом взаимодействовать
(WinApi написанна на языке Си, а также и OpenGL). Теперь можно перейти к созданию
самого окна. Чтобы создать работоспособное окно на WinApi вам не хватит его "тупо" создать.
Вообще эта работа состоит из нескольких частей. Надо: 1. Определить свойтва окна Начнём, конечно, с первого пункта. Свойства окна у нас находятся в переменной
Wc, которая пока является глобальной. Теперь очень коротко объясню, что это
такое - класс. Класс - группа свойств и подпрограмм. На самом деле это вещь
более обширная, но вам пока не надо морочить голову этим материалом. Больше вы
узнаете, когда мы сами будем создавать свои классы. Потомок класса является
объектом. Он имеет свойства, описанные в классе. Например, кнопка - объект. Он
имеет свойство Caption, которое описанно в классе этого объекта. А сам класс
называется TButton. Все классы начинаются с буквы T. Окно нашей
программы будет потомком класса TWndClassEx. Один класс может иметь кучу
потомков. Это доказывает возможность на форму поставить сотни (если надо :) кнопок,
или других компонентов. Значит Wc можно назвать новым объектом. Обращатся
к этому объекту будем также, как и к другим (например кнопки) - через точку. Наш новый
объект имеет десять свойств (их больше (12), но нам нужно всего 10)! :) Девять из ник
имеют дурные начала :) Т.е. они названны не очень удобно. Я часто путаюсь :) Давайте
перейдём к первому свойству. Первое свойство называется Style. Оно определяет стиль окна, т.е. каким оно
должно быть. Окно должно перерисовываться, когда изменяют его размеры. Размер
изменять можно вертикально и горизонтально. Не забудьте, это всё надо делать в
главном блоке программы! А программистов Си попрошу ещё не начинать :)
Delphi: CS_VREDRAW значит перерисовку при вертикальном изменении размера
окна. Соответственно CS_HREDRAW - перерисовка окна при его
горизонтальном изменении размера (от Vertical Redraw и Horizontal Redraw). Почему мы
использовали or? Ведь надо, чтобы перерисовка происходила при изменении и
того, и другого размера окна! Те, кто так думают, думают совсем правильно. Но
представьте себе, что будет происходить, если мы поставим and! Вы помните,
когда надо использовать одно или другое логическое выражение? Если мы поставим
and, то перерисовка будет происходить только тогда, когда будут измененны и
вертикальные, и горизонтальные размеры окна! А пользователь может захотеть
изменить только вертикальный, или только горизонтальный размер, т.е. высоту или
ширину. Теперь понятно? Точно также и в Си. Но почему я попросил ещё не начинать? А
потому, что название главной функции в косольном приложении отличается от того,
что нам надо использовать на WinApi! Во первых, она называется не просто
main(), a WinMain(), и перед названием функции надо сказать программе,
что эта функция является WinApi программой, а не консольной. Вот так это будет
выглядеть на языке Си:
C: Теперь нам надо указать пиктограмму приложения и курсор. Свойства называются
hIcon и hCursor.
Delphi:
C: Чтобы вы лучше всё усвоили, я продолжу тему в следущем выпуске :) Мы уже
написали половину всего кода :) Если появятся вопросы - пишите. На сегодня всё. |
2005-10-24 |
Spider3D
http://spider3d.narod.ru Евгений Нарышкин Spider3D@yandex.ru |