Инструкция программиста mycall
Приложение MyCall - это пользовательский интерфейс Remote Access Service для Windows 95/98. Приложение разработано как учебно-экспериментальная задача, целью которой является демонстрация отличий в реализации приложений на ассемблере и на C++. См. статью Зачем он нужен, этот ассемблер?.
Исходный текст реализации на C++ состоит из файлов:
- main.cpp - основной файл
- main.h - файл заголовков
- mycall.rc - файл описания ресурсов
- icon1.ico - иконка
Полный комплект файлов, необходимых для компиляции приложения, содержится в zip-файле mycallcb.zip (13192 байта)
Исходный текст реализации на ассемблере состоит из файлов:
- main.asm - основной файл
- main.inc - файл заголовков
- @struct.inc - файл структурных макросов
- windows.inc - файл заголовков win32
- mycall.rc - файл описания ресурсов
- icon1.ico - иконка
Полный комплект файлов, необходимых для компиляции приложения, содержится в zip-файле mycallab.zip (15913 байта)
компиляция приложения
Компиляция реализации на C++:
- Создайте в MS Developer Studio новый проект приложения win32 ("Win32 Application") с именем mycall
- Распакуйте файл mycallcb.zip, поместив извлеченные из него файлы в папку проекта
- Подключите к проекту файлы main.cpp и mycall.rc
- Установите в качестве активной конфигурации для построения проекта "Win32 Release"
- Измените установки проекта для сборщика (Settings/Link):
- удалите из списка подключаемых библиотек все, кроме kernel32.lib и user32.lib
- добавьте в список rasapi32.lib
- отключите библиотеки по умолчанию (флажок "Ignore all default libraries")
- установите в качестве точки входа функцию WinMain (поле "Entry-point symbol" в категории "Output")
Компиляция реализации на ассемблере:
Это, в свою очередь, диктует особенности программных решений. Был сознательно выбран хаотический стиль программирования, не использующий практически никаких характерных для C++ объектно-ориентированных средств организации программ. (В результате проект, например, изобилует глобальными переменными, что в общем случае следовало бы считать очень плохим стилем.) Также практически не использовались и специфические приемы ассемблерного программирования.
Но зато, положив рядом два листинга - на C++ и на ассемблере - желающие имеют теперь возможность как угодно глубоко разобраться в отличиях реализаций. Может быть полезно также сравнить дизассемблированный код приложения на C++ с реализацией на ассемблере, что позволяет сделать встроенный отладчик MS Developer Studio.
Кстати, о стиле. Для таких маленьких приложений, вполне возможно, хаотический стиль программирования выглядит более предпочтительным, чем объектно-ориентированный, как с точки зрения читабельности программы, так и с точки зрения минимизации накладных расходов: времени на разработку, объема исходного текста и результирующего объема кода.
Работа приложения начинается с передачи управления функции WinMain. По своему построению это обычная для win32 функция, выполняющая следующие задачи:
Некоторым отличием от общепринятых норм построения WinMain является то, что в качестве главного окна приложения используется не обычное окно, а окно диалога, описание которого содержится в файле описания ресурсов. Впрочем, это частый прием для небольших утилитоподобных приложений вроде MyCall.
Кроме того, в функции WinMain выполняется проверка запуска второго экземпляра приложения. Для этого применяется специальный способ, основанный на обмене регистрируемым глобальным сообщением.
Все внешние данные приложения хранятся в двух файлах: mycall.txt и mycall.ini. Они оба загружаются соответствующими функциями, вызываемыми из WinMain в начале работы приложения.
Файл mycall.ini содержит данные о позиции окна приложения на рабочем столе и о состоянии выбора списков соединений, телефонов и логинов на момент завершения предыдущего сеанса работы приложения. Это удобная возможность, позволяющая пользователю сохранять привычную ему рабочую среду.
Файл mycall.ini загружается функцией load_ini однократно в начале работы приложения. На основании содержащихся в нем данных устанавливаются значения соответствующих переменных. Если файл mycall.ini не существует, как это бывает, например, при первом запуске приложения, то в переменных остаются значения по умолчанию.
Файл mycall.ini формируется и записывается функцией load_ini непосредственно перед завершением работы приложения. Функция вызывается оконной процедурой superprocedure при обработке ею сообщения WM_DESTROY.
Файл mycall.txt содержит данные, задаваемые пользователем для соединения с провайдером: имена соединений, номера телефонов, логины и пароли.
Он загружается функцией load_data однократно в начале работы приложения. В последующем содержимое файла хранится в специальном буфере, который сканируется всякий раз, когда требуется изменить содержимое соответствующих списков. Это делает функция change_con.
Приложение MyCall не изменяет содержимого файла mycall.txt и не сохраняет его. Отредактировать файл может только пользователь с помощью какого-нибудь текстового редактора. Формат файла должен строго соблюдаться пользователем, так как приложение MyCall не проверяет его при загрузке файла. Ошибки формата не приводят к фатальным последствиям, но данные для работы с провайдерам становятся некорректными.
Общую диспетчеризацию в MyCall, как того и требует архитектура приложений win32, выполняет оконная процедура superprocedure. Она обрабатывает сообщения:
После выбора соединения, телефона и логина, пользователь, нажав кнопку, переводит приложение в режим дозвона. При этом:
Функция ras_dial на основании данных, выбранных пользователем, заполняет структуру RASDIALPARAMS и вызывает функцию API RasDial.
Для мониторинга процесса дозвона используется callback-функция ras_dial_func типа RasDialFunc (см. Platform SDK). Эта функция отображает соответствующие сообщения в строке статуса и, в случае неудачи установления соединения, посылает оконной процедуре сообщение WM_USER с параметром "0" для выполнения повторных попыток дозвона.
Использовать механизм сообщений в данном случае необходимо, так как callback-функция вызывается сервисом RAS в отдельной нити, и, следовательно, не синхронизирована с главной нитью приложения.
Если соединение установлено успешно, callback-функция ras_dial_func запускает нить монитора разрыва ras_monitor_func. Такой прием пришлось применить потому, что в сервисе RAS не предусмотрено средств для контроля работающего соединения, и, следовательно, приложение не может никаким иным способом узнать о факте разрыва соединения, например, при потере связи. Нить монитора разрыва каждые 200 мс опрашивает состояние соединения, и при обнаружении его разрыва сообщает об этом основной нити приложения путем посылки сообщения WM_USER с параметром "1", а затем завершается. Получив это сообщение, приложение переходит в режим выбора соединения.
Для прекращения дозвона и принудительного разрыва установленного соединения используется функция ras_hangup. Она вызывает функцию API RasHangUp.
Важным элементом является последующий цикл, опрашивающий состояние соединения. Дело в том, что сервис RAS может иметь задержки реакции на вызов функций API до нескольких секунд. Если в течение времени этой задержки попытаться вызвать, например, функцию RasDial, последствия, как правило, бывают фатальны. Примененный здесь цикл позволяет гарантированно дождаться окончания задержки.
Несмотря на то, что приложение MyCall разрабатывалось как учебно-экспериментальное, оно вполне функционально и может удовлетворить потребности многих пользователей.