В последнее время, я получаю много писем с
просьбой объяснить на конкретном примере,
принцип построения программ заменяющих собой
стандартное средство дозвона Windows. Это довольно
сложно сделать просто и понятно, в связи со
спецификой работы разных модификаций этой
популярной операционной системы. Для каждого
случая необходим индивидуальный подход, но
основополагающие алгоритмы функционирования
неизменны. В начале 1999 года, я опубликовал в Usenet~е,
некоторые соображения по этому поводу в месте с
простым примером написания программы
осуществляющей дозвон и соединение с
провайдером. Не берусь утверждать, что такой
подход единственно правильный. Но как мне
кажется данный пример, позволяет ответить на
массу вопросов по программированию RASAPI.
Некоторое время назад у меня появилась проблема
с учетом времени проведенного в сети и
дополнительного сервиса в плане автоматической
конфигурации модема в зависимости от времени
суток. Хочу пояснить, что у нас используется АТС
старого типа с механическими контактами и
поэтому качество соединения с провайдером
изменяется по определенному закону. С 10-12 часов
дня до 22-24 часов вероятность рас соединения в
процессе работы примерно возрастает, а в
остальное время таких проблем обычно не
возникает. Выходом из ситуации являлось
дополнительное программирование модема при
помощи АТ команд. То есть создается несколько
соединений в телефонной книге Windows и в
зависимости от времени суток дозванивание до
провайдера осуществляется по определенной
закономерности. Появилось желание все это
автоматизировать, но подходящей программы
"звонилки" обнаружить не удалось. Тогда было
принято решение написать программу позволяющую
решить все эти и некоторые другие поставленные
задачи по телефонному соединению с провайдером.
Первоначальный вариант эскиза программы был
написан мной на Microsoft С, благодаря достаточному
обилию информации по Remote Access Service (Удаленное
Обслуживание Доступа) и вполне приличной
справочной документации в стандартной поставке
Visual C++ V5.0. Однако, такие факторы как
"раздутость" кода бесконечные мытарства при
выражении оптимизированного алгоритма
функционирования и наконец большие затраты
времени на составление красивого интерфейса
пользователя привели меня к мысли о написании
программы в среде разработки Delphi 4. Вначале это
казалось довольно сложной задачей, так как в Delphi
отсутствует модуль прототипов RASAPI функций, но
небольшое их количество и логическая простота
составления подобных прототипов в объектном
Паскале Delphi позволило в короткий срок
сгруппировать необходимые данные.
Предполагаемый алгоритм работы программы
"звонилки" достаточно прост. Есть два пути
проведения соединения с удаленным сервером.
Первый путь - это когда программа не
интегрирована в операционную систему и работает
автономно. То есть она при помощи RAS API функций
получает информацию о списке текущих соединений
и после выбора необходимого, начинает процесс
телефонного соединения вплоть до конечного
результата. Системная программа "звонилка"
в этом случаи может быть активизирована
независимо от вашей программы и при наличии
встроенной функции учета времени проведенного в
сети может сучиться, что при соединении с ее
помощью учет времени будет нарушен. Правда на сей
счет, есть оговорка, что при безостановочном
цикле проверки существующих соединений всe-таки
можно проводить достаточно точный учет времени
проведенного в Интернете. Но при этом
значительно расходуются ресурсы системы. Второй
путь - несколько сложнее, но он наиболее
интересен с точки зрения алгоритма реализации.
При помощи функций RAS API, программа интегрируется
в систему и в некоторых случаях позволяет
заменять собой системную "звонилку". При
необходимости проведения соединения, система
проецирует в адресное пространство вызывающего
процесса вашу DLL, вызывая функцию повторного
вызова, прототип которой заранее известен и
стандартизирован. В момент вызова данной функции
из DLL, в ней может быть создан новый поток или
потоки в текущем пространстве вызывающего
процесса для создания интерфейса программы
"звонилки". Так же, из этой DLL может быть
создан новый объект ядра типа процесс со своими 4
Гб виртуального адресного пространства, в
котором порождается главный поток отдельной
программы "звонилки".
В конечном итоге я остановился на втором
варианте, где из моей DLL вызывается отдельная
программа "звонилка", запускаемая в
отдельном процессе и управляемая из функции
повторного вызова DLL разнообразными способами.
Осталось дело за малым - выбором способа обмена
данными между приложениями. Автоматизация OLE и
создание и внедрение COM объектов были отброшены
сразу из за больших и безосновательных затрат
системных ресурсов и памяти. Оставалось
использовать несколько устаревший способ
динамический обмена данными между приложениями
DDE. Вот тут и начались казусы. При применении
библиотеки DDEML при выполнении транзакции XTYP_REQUEST
(запрос клиентом на возвращение данных из
сервера) необходимо создать в DLL специальную
функцию повторного вызова, в которую при запросе
клиента, сервер должен возвращать данные. Хочу
сразу отметить, что здесь речь идет только об
асинхронных транзакциях, так как функция
повторного вызова DLL должна возвращать
приложению ее запросившему, результат работы
вашей программы "звонилки" и в случае
неудачи вызывать системную "звонилку". Так
вот, если производилась вышеописанная
транзакция и не было выхода из функции
повторного вызова, отправка данных сервера
клиенту не производилась (в свою функцию
повторного вызова). Причем это происходило и в
библиотеке написанной в Microsoft С. Не помогало да же
открытие нового потока и создание обработчика
транзакции в нем. Причиной тому, по моему
глубокому убеждению, являлось специфика работы
Windows 98 (в Windows NT все работало прекрасно). Решением
проблемы оставалось использование DDE без
применения библиотеки Ddeml.dll и оконные общения на
уровне системы. Первый путь из-за своей
неэффективности (из пушки по воробьям), был
отброшен сразу и обмен данными между библиотекой
и программой "звонилки" производился при
помощи оконных сообщений (Window Messages).
Теперь я хочу кратко описать алгоритм
функционирования моей программы. В случае
интеграции программы в систему, при надобности
соединения с удаленным сервером браузер,
например Internet Explorer, проецирует DLL в сое адресное
пространство (динамическая загрузка) и вызывает
функцию повторного вызова. Информацию об имени
функции и полном пути к DLL он получает в процессе
интеграции программы. В начальном этапе вызова
функции в DLL создается новый поток, в котором
регистрируется новый класс окна и создается
невидимое пользователю скрытое окно. Функция
обработчик оконных сообщений регистрирует
только два типа оконных сообщений - это WM_DESTROY и
WM_USER. Первое необходимо для деструктуризации
окна, а второе, непосредственно для получения
данных из программы сервера. В дальнейшем,
производится поиск окна программы
"звонилки" в системе и в случаи его
отсутствия порождается новый отдельный процесс,
в главном потоке которого идет выполнение Bашей
программы. Дальнейший обмен данными между
приложениями происходит на уровне оконных
сообщений. При удачном или неудачном результате
дозвона функция повторного вызова возвращает в
программу заказчик соответствующее значение и в
зависимости от этого, вызывается или не
вызывается системная программа "звонилка".
При выгрузке DLL из памяти скрытое окно получает
сообщение WM_DESTROY, тем самым, корректно завершая
поток. Хочу особо отметить, что для упрощения
кода программы в программе-сервере (написанной в
Delphi) не создается дополнительное окно, а все
оконные сообщения обрабатываются в главном окне
программы. Для этого была переопределена
процедура базового класса VCL WndProc. Данный способ
не является достаточно корректным, однако при
правильном понимании специфики работы VCL (Visual
components library) значительно упрощает
программирование.
Приведенные мной здесь исходники не являются
законченной программой, а представляют собой
испытательный полигон для проверки
разнообразных алгоритмов и их способов
реализации. Поэтому, буду очень рад любой
информации и готов поддержать любое обсуждение
этого вопроса. Только есть большая просьба, не
присылать сообщения подобного типа - "На кой
ляд тебе это все сдалось. Уже такая прога
существует...".
PS: Внимание!!! Если в среде разработки Delphi4 будет
открыта страница с Maim.pas, работа отлаживаемой
программы будет нарушена т. к. это окно будет
ошибочно найдена функцией API FindWindow. Поэтому пред
отладкой программы закройте эту страницу. Загрузить
пример (17 Кб)